15
15
using System . IO ;
16
16
using System . Threading . Tasks ;
17
17
using System . Windows . Input ;
18
- using OpenLoco . ObjectEditor . Types ;
19
18
using System . ComponentModel ;
20
- using Core . Objects . Sound ;
21
19
using System . Collections . ObjectModel ;
22
- using OpenLoco . ObjectEditor . Data ;
23
- using NAudio . Wave ;
24
- using System . Threading ;
25
- using System . Runtime . InteropServices ;
20
+ using OpenLoco . ObjectEditor . Types ;
21
+ using AvaGui . Models ;
22
+ using Avalonia . Media ;
23
+ using Avalonia . Markup . Xaml . Converters ;
24
+ using Avalonia . Data . Converters ;
26
25
27
26
namespace AvaGui . ViewModels
28
27
{
29
- public interface IExtraContentViewModel
30
- { }
31
-
32
- public record UiSoundObject ( string SoundName )
33
- {
34
- public UiSoundObject ( string soundName , RiffWavHeader header , byte [ ] data ) : this ( soundName )
35
- {
36
- Header = header ;
37
- Data = data ;
38
- Duration = $ "{ data . Length / ( decimal ) header . ByteRate : 0.#} s";
39
- }
40
-
41
- public string Duration { get ; init ; }
42
-
43
- public RiffWavHeader Header { get ; set ; }
44
- public byte [ ] Data { get ; set ; }
45
- }
46
-
47
- public class SoundViewModel : ReactiveObject , IExtraContentViewModel
28
+ public record UIG1Element32 (
29
+ [ Category ( "Image" ) ] int ImageIndex ,
30
+ [ Category ( "Image" ) ] string ImageName ,
31
+ [ Category ( "G1Element32" ) ] uint32_t Offset ,
32
+ [ Category ( "G1Element32" ) ] int16_t Width ,
33
+ [ Category ( "G1Element32" ) ] int16_t Height ,
34
+ [ Category ( "G1Element32" ) ] int16_t XOffset ,
35
+ [ Category ( "G1Element32" ) ] int16_t YOffset ,
36
+ [ Category ( "G1Element32" ) ] G1ElementFlags Flags ,
37
+ [ Category ( "G1Element32" ) ] int16_t ZoomOffset
38
+ )
48
39
{
49
- ILocoObject parent ; // currently not needed
50
-
51
- WaveOutEvent ? CurrentWOEvent { get ; set ; }
52
-
53
- public SoundViewModel ( ILocoObject parent )
54
- {
55
- if ( parent . Object is not SoundObject soundObject )
56
- {
57
- return ;
58
- }
59
-
60
- this . parent = parent ;
61
-
62
- var hdr = soundObject . SoundObjectData . PcmHeader ;
63
- var text = parent . StringTable . Table [ "Name" ] [ LanguageId . English_UK ] ?? "<null>" ;
64
- Sound = new UiSoundObject ( text , SawyerStreamWriter . WaveFormatExToRiff ( hdr , soundObject . PcmData . Length ) , soundObject . PcmData ) ;
65
-
66
- PlaySoundCommand = ReactiveCommand . Create ( PlaySound ) ;
67
- PauseSoundCommand = ReactiveCommand . Create ( ( ) => CurrentWOEvent ? . Pause ( ) ) ;
68
- StopSoundCommand = ReactiveCommand . Create ( ( ) => CurrentWOEvent ? . Stop ( ) ) ;
69
-
70
- //ImportSoundCommand = ReactiveCommand.Create(ImportSound);
71
- //ExportSoundCommand = ReactiveCommand.Create(ExportSound);
72
- }
73
-
74
- public void PlaySound ( )
75
- {
76
- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
77
- {
78
- // unfortunately NAudio is not cross-platform! this code just crashes on Linux
79
- // so there isn't anything to do here until a cross-platform audio lib is used
80
- return ;
81
- }
82
-
83
- if ( CurrentWOEvent != null )
84
- {
85
- if ( CurrentWOEvent . PlaybackState == PlaybackState . Playing )
86
- {
87
- return ;
88
- }
89
-
90
- if ( CurrentWOEvent . PlaybackState == PlaybackState . Paused )
91
- {
92
- CurrentWOEvent . Play ( ) ;
93
- return ;
94
- }
95
- }
96
-
97
- // do it asyncly to a) give user ui control and b) allow multiple sounds to play at once
98
- _ = Task . Run ( ( ) =>
99
- {
100
- if ( CurrentWOEvent ? . PlaybackState == PlaybackState . Stopped )
101
- {
102
- Thread . Sleep ( 100 ) ; // give time to wait until previous sound is disposed
103
- }
104
-
105
- CurrentWOEvent ? . Dispose ( ) ;
106
-
107
- using ( var ms = new MemoryStream ( Sound . Data ) )
108
- using ( var rs = new RawSourceWaveStream ( ms , new WaveFormat ( ( int ) Sound . Header . SampleRate , Sound . Header . BitsPerSample , Sound . Header . NumberOfChannels ) ) )
109
- using ( CurrentWOEvent = new WaveOutEvent ( ) )
110
- {
111
- CurrentWOEvent . Init ( rs ) ;
112
- CurrentWOEvent . Play ( ) ;
113
-
114
- // need to wait for music to finish
115
- while ( CurrentWOEvent ? . PlaybackState != PlaybackState . Stopped )
116
- {
117
- if ( CurrentWOEvent == null )
118
- {
119
- break ;
120
- }
121
- Thread . Sleep ( 50 ) ;
122
- }
123
- }
124
-
125
- CurrentWOEvent = null ;
126
- } ) ;
127
- }
128
-
129
- [ Reactive ]
130
- public UiSoundObject Sound { get ; set ; }
131
-
132
- [ Reactive ]
133
- public ICommand PlaySoundCommand { get ; set ; }
134
-
135
- [ Reactive ]
136
- public ICommand PauseSoundCommand { get ; set ; }
137
-
138
- [ Reactive ]
139
- public ICommand StopSoundCommand { get ; set ; }
140
-
141
- //public async Task ImportSound()
142
- //{
143
- // var folders = await PlatformSpecific.OpenFolderPicker();
144
- // var dir = folders.FirstOrDefault();
145
- // if (dir == null)
146
- // {
147
- // return;
148
- // }
149
-
150
- // var dirPath = dir.Path.LocalPath;
151
- // if (Directory.Exists(dirPath) && Directory.EnumerateFiles(dirPath).Any())
152
- // {
153
- // var files = Directory.GetFiles(dirPath);
154
- // var sorted = files.OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f).Split('-')[0]));
155
-
156
- // var g1Elements = new List<G1Element32>();
157
- // var i = 0;
158
- // //foreach (var file in sorted)
159
- // //{
160
- // // var img = SixLabors.ImageSharp.Image.Load<Rgb24>(file);
161
- // // var data = PaletteMap.ConvertRgb24ImageToG1Data(img);
162
- // // var hasTransparency = data.Any(b => b == 0);
163
- // // var oldImage = Parent.G1Elements[i++];
164
- // // oldImage.ImageData = PaletteMap.ConvertRgb24ImageToG1Data(img); // simply overwrite existing pixel data
165
- // //}
166
- // }
167
-
168
- // //this.RaisePropertyChanged(nameof(Images));
169
- //}
170
-
171
- //public async Task ExportSound()
172
- //{
173
- // var folders = await PlatformSpecific.OpenFolderPicker();
174
- // var dir = folders.FirstOrDefault();
175
- // if (dir == null)
176
- // {
177
- // return;
178
- // }
179
-
180
- // var dirPath = dir.Path.LocalPath;
181
- // if (Directory.Exists(dirPath))
182
- // {
183
- // var counter = 0;
184
- // //foreach (var image in Images)
185
- // //{
186
- // // var imageName = counter++.ToString(); // todo: use GetImageName from winforms project
187
- // // var path = Path.Combine(dir.Path.LocalPath, $"{imageName}.png");
188
- // // //logger.Debug($"Saving image to {path}");
189
- // // image.Save(path);
190
- // //}
191
- // }
192
- //}
193
-
194
- //[Reactive]
195
- //public ICommand ImportSoundCommand { get; set; }
196
-
197
- //[Reactive]
198
- //public ICommand ExportSoundCommand { get; set; }
40
+ public UIG1Element32 ( int imageIndex , string imageName , G1Element32 g1Element )
41
+ : this (
42
+ imageIndex ,
43
+ imageName ,
44
+ g1Element . Offset ,
45
+ g1Element . Width ,
46
+ g1Element . Height ,
47
+ g1Element . XOffset ,
48
+ g1Element . YOffset ,
49
+ g1Element . Flags ,
50
+ g1Element . ZoomOffset )
51
+ { }
199
52
}
200
53
201
54
public class ImageTableViewModel : ReactiveObject , IExtraContentViewModel
@@ -213,6 +66,8 @@ public ImageTableViewModel(ILocoObject parent, PaletteMap paletteMap)
213
66
. Subscribe ( _ => this . RaisePropertyChanged ( nameof ( Images ) ) ) ;
214
67
_ = this . WhenAnyValue ( o => o . Zoom )
215
68
. Subscribe ( _ => this . RaisePropertyChanged ( nameof ( Images ) ) ) ;
69
+ _ = this . WhenAnyValue ( o => o . SelectedImageIndex )
70
+ . Subscribe ( _ => this . RaisePropertyChanged ( nameof ( SelectedG1Element ) ) ) ;
216
71
217
72
ImportImagesCommand = ReactiveCommand . Create ( ImportImages ) ;
218
73
ExportImagesCommand = ReactiveCommand . Create ( ExportImages ) ;
@@ -283,8 +138,6 @@ public async Task ExportImages()
283
138
[ Reactive ]
284
139
public int Zoom { get ; set ; } = 1 ;
285
140
286
- // public Bitmap FirstImage => Images.FirstOrDefault();
287
-
288
141
public List < Bitmap > Images
289
142
{
290
143
get
@@ -300,6 +153,40 @@ public List<Bitmap> Images
300
153
}
301
154
List < Bitmap > images ;
302
155
156
+ [ Reactive ]
157
+ public int SelectedImageIndex { get ; set ; }
158
+
159
+ public UIG1Element32 ? SelectedG1Element
160
+ => SelectedImageIndex == - 1 || Parent . G1Elements . Count == 0 ? null : new UIG1Element32 ( SelectedImageIndex , GetImageName ( Parent , SelectedImageIndex ) , Parent . G1Elements [ SelectedImageIndex ] ) ;
161
+
162
+ [ Reactive ]
163
+ public Color BackgroundColour { get ; set ; } = Colors . Magenta ;
164
+
165
+ public static string GetImageName ( ILocoObject locoObj , int counter )
166
+ {
167
+ ILocoImageTableNames ? its = null ;
168
+ //var objectName = string.Empty;
169
+
170
+ if ( locoObj . Object is ILocoImageTableNames itss )
171
+ {
172
+ its = itss ;
173
+ //objectName = locoObj.DatFileInfo.S5Header.Name;
174
+ }
175
+ //else if (uiObj is UiG1 uiG1 && uiG1.G1 is ILocoImageTableNames itsg)
176
+ //{
177
+ // its = itsg;
178
+ // objectName = "g1.dat";
179
+ //}
180
+
181
+ if ( its != null && its . TryGetImageName ( counter , out var value ) && value != null )
182
+ {
183
+ return $ "{ value } ";
184
+ }
185
+
186
+ return "<unk>" ;
187
+ }
188
+
189
+
303
190
public static IEnumerable < Bitmap > CreateImages ( IEnumerable < G1Element32 > g1Elements , PaletteMap paletteMap , int zoom )
304
191
{
305
192
foreach ( var g1Element in g1Elements )
0 commit comments