Skip to content

Commit 96c6326

Browse files
committed
add image property viewing for image table
1 parent 8153d6a commit 96c6326

File tree

9 files changed

+316
-202
lines changed

9 files changed

+316
-202
lines changed

AvaGui/App.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<FluentTheme />
1515
<materialIcons:MaterialIconStyles />
1616
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
17+
<StyleInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml" />
1718
</Application.Styles>
1819

1920
</Application>

AvaGui/AvaGui.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
<ItemGroup>
3535
<PackageReference Include="Avalonia" Version="11.1.1" />
36+
<PackageReference Include="Avalonia.Controls.ColorPicker" Version="11.1.1" />
3637
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.1" />
3738
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.1.1" />
3839
<PackageReference Include="Avalonia.Desktop" Version="11.1.1" />

AvaGui/Models/UiSoundObject.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@
33

44
namespace AvaGui.Models
55
{
6-
[TypeConverter(typeof(ExpandableObjectConverter))]
7-
public class UiSoundObject
6+
public record UiSoundObject(string SoundName)
87
{
9-
public string SoundName { get; set; }
8+
public UiSoundObject(string soundName, RiffWavHeader header, byte[] data) : this(soundName)
9+
{
10+
Header = header;
11+
Data = data;
12+
Duration = $"{data.Length / (decimal)header.ByteRate:0.#}s";
13+
}
14+
15+
public string Duration { get; init; }
16+
1017
public RiffWavHeader Header { get; set; }
1118
public byte[] Data { get; set; }
1219
}

AvaGui/Typedefs.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
global using uint8_t = System.Byte;
2+
global using int8_t = System.SByte;
3+
global using uint16_t = System.UInt16;
4+
global using int16_t = System.Int16;
5+
global using int32_t = System.Int32;
6+
global using uint32_t = System.UInt32;
7+
global using size_t = System.UInt32;
8+
global using char_t = System.Byte;
9+
global using string_id = System.UInt16;
10+
global using image_id = System.UInt32;
11+
global using object_id = System.Byte;
12+
global using Speed16 = System.Int16;
13+
global using Speed32 = System.Int32;
14+
global using MicroZ = System.Byte;
15+
global using SoundObjectId = System.Byte;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace AvaGui.ViewModels
2+
{
3+
public interface IExtraContentViewModel
4+
{ }
5+
}

AvaGui/ViewModels/ImageTableViewModel.cs

Lines changed: 64 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -15,187 +15,40 @@
1515
using System.IO;
1616
using System.Threading.Tasks;
1717
using System.Windows.Input;
18-
using OpenLoco.ObjectEditor.Types;
1918
using System.ComponentModel;
20-
using Core.Objects.Sound;
2119
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;
2625

2726
namespace AvaGui.ViewModels
2827
{
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+
)
4839
{
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+
{ }
19952
}
20053

20154
public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel
@@ -213,6 +66,8 @@ public ImageTableViewModel(ILocoObject parent, PaletteMap paletteMap)
21366
.Subscribe(_ => this.RaisePropertyChanged(nameof(Images)));
21467
_ = this.WhenAnyValue(o => o.Zoom)
21568
.Subscribe(_ => this.RaisePropertyChanged(nameof(Images)));
69+
_ = this.WhenAnyValue(o => o.SelectedImageIndex)
70+
.Subscribe(_ => this.RaisePropertyChanged(nameof(SelectedG1Element)));
21671

21772
ImportImagesCommand = ReactiveCommand.Create(ImportImages);
21873
ExportImagesCommand = ReactiveCommand.Create(ExportImages);
@@ -283,8 +138,6 @@ public async Task ExportImages()
283138
[Reactive]
284139
public int Zoom { get; set; } = 1;
285140

286-
// public Bitmap FirstImage => Images.FirstOrDefault();
287-
288141
public List<Bitmap> Images
289142
{
290143
get
@@ -300,6 +153,40 @@ public List<Bitmap> Images
300153
}
301154
List<Bitmap> images;
302155

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+
303190
public static IEnumerable<Bitmap> CreateImages(IEnumerable<G1Element32> g1Elements, PaletteMap paletteMap, int zoom)
304191
{
305192
foreach (var g1Element in g1Elements)

AvaGui/ViewModels/ObjectEditorViewModel.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using System.Linq;
1212
using System.Threading.Tasks;
1313
using Core.Objects.Sound;
14+
using System.Drawing;
15+
using Avalonia.Media;
1416

1517
namespace AvaGui.ViewModels
1618
{
@@ -65,10 +67,16 @@ public void SelectedObjectChanged()
6567
{
6668
if (CurrentObject?.LocoObject != null)
6769
{
70+
var oldImageTableColour = Colors.Magenta;
71+
if (ExtraContentViewModel is ImageTableViewModel itvm)
72+
{
73+
oldImageTableColour = itvm.BackgroundColour;
74+
}
75+
6876
StringTableViewModel = new(CurrentObject.LocoObject.StringTable);
6977
ExtraContentViewModel = CurrentObject.LocoObject.Object is SoundObject
7078
? new SoundViewModel(CurrentObject.LocoObject)
71-
: new ImageTableViewModel(CurrentObject.LocoObject, Model.PaletteMap);
79+
: new ImageTableViewModel(CurrentObject.LocoObject, Model.PaletteMap) { BackgroundColour = oldImageTableColour };
7280
}
7381
else
7482
{

0 commit comments

Comments
 (0)