diff --git a/Collections/Collection.cs b/Collections/Collection.cs index a0a6382..adf5716 100644 --- a/Collections/Collection.cs +++ b/Collections/Collection.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text.Json.Serialization; using TaikoSoundEditor.Commons.IO; @@ -27,11 +28,24 @@ public static Collection FromJson(string json, Type expectedItemType) } public static Type MakeGeneric(Type type) => typeof(Collection<>).MakeGenericType(type); + public static object Instantiate(Type type) => Activator.CreateInstance(MakeGeneric(type)); } public class Collection { [JsonPropertyName("items")] - public List Items { get; set; } = new List(); + public List Items { get; set; } = new List(); + + public object Cast(Type type) + { + var col = Collections.Instantiate(type); + var itemsProp = col.GetType().GetProperty("Items"); + var add = itemsProp.PropertyType.GetMethod("Add"); + var items = itemsProp.GetValue(col); + foreach (var item in Items) + add.Invoke(items, new object[] { Convert.ChangeType(item, type) }); + + return col; + } } } diff --git a/Commons/Emit/DatatableEntityTypeBuilder.cs b/Commons/Emit/DatatableEntityTypeBuilder.cs index 09cfc81..4e808c2 100644 --- a/Commons/Emit/DatatableEntityTypeBuilder.cs +++ b/Commons/Emit/DatatableEntityTypeBuilder.cs @@ -211,6 +211,9 @@ private void GenerateProperty(TypeBuilder tb, ILGenerator ilg, EntityPropertyInf if (property.JsonPropertyName != null) AddAttribute(pb, typeof(JsonPropertyNameAttribute), property.JsonPropertyName); + else + AddAttribute(pb, typeof(JsonIgnoreAttribute), new object[0]); + if (property.IsReadOnly) AddAttribute(pb, typeof(ReadOnlyAttribute), true); } diff --git a/Commons/Emit/EntityPropertyInfo.cs b/Commons/Emit/EntityPropertyInfo.cs index a2c6362..0894e7c 100644 --- a/Commons/Emit/EntityPropertyInfo.cs +++ b/Commons/Emit/EntityPropertyInfo.cs @@ -9,7 +9,7 @@ internal class EntityPropertyInfo public Type Type { get; } public string JsonPropertyName { get; } public bool IsReadOnly { get; } - public object DefaultValue { get; } + public object DefaultValue { get; } public EntityPropertyInfo(string name, Type type, string jsonPropertyName = null, bool isReadOnly = false, object defaultValue = null) { diff --git a/Commons/IO/Json.cs b/Commons/IO/Json.cs index f9524a4..230f964 100644 --- a/Commons/IO/Json.cs +++ b/Commons/IO/Json.cs @@ -34,5 +34,16 @@ public static string Serialize(T item, bool indented = true) }); } + public static string DynamicSerialize(object item, bool indented = true) + { + Logger.Info($"Serializing dynamic {item.GetType()}:\n{item}"); + + return JsonSerializer.Serialize(item, item.GetType(), new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), + WriteIndented = indented + }); + } + } } diff --git a/Commons/Utils/DatatableIO.cs b/Commons/Utils/DatatableIO.cs index 1d64ac9..c206c02 100644 --- a/Commons/Utils/DatatableIO.cs +++ b/Commons/Utils/DatatableIO.cs @@ -48,6 +48,22 @@ public T Deserialize(string path) } } + public void DynamicSerialize(string path, object item, bool indented = false, bool fixBools = false) + { + var str = JsonFix(Json.DynamicSerialize(item, indented)); + if (fixBools) + { + str = str + .Replace("\"new\": true,", "\"new\":true,") + .Replace("\"new\": false,", "\"new\":false,"); // is this still needed? + } + + if (IsEncrypted) + File.WriteAllBytes(path, SSL.EncryptDatatable(GZ.CompressToBytes(str))); + else + File.WriteAllBytes(path, GZ.CompressToBytes(str)); + } + public void Serialize(string path, T item, bool indented = false, bool fixBools = false) { var str = JsonFix(Json.Serialize(item, indented)); diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs index ae8c139..18c9f05 100644 --- a/MainForm.Designer.cs +++ b/MainForm.Designer.cs @@ -34,6 +34,9 @@ private void InitializeComponent() this.TabControl = new System.Windows.Forms.TabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); this.panel1 = new System.Windows.Forms.Panel(); + this.groupBox13 = new System.Windows.Forms.GroupBox(); + this.label22 = new System.Windows.Forms.Label(); + this.DatatableDef = new TaikoSoundEditor.Commons.Controls.PathSelector(); this.groupBox12 = new System.Windows.Forms.GroupBox(); this.UseEncryptionBox = new System.Windows.Forms.CheckBox(); this.FumenKeyBox = new System.Windows.Forms.TextBox(); @@ -140,6 +143,7 @@ private void InitializeComponent() this.TabControl.SuspendLayout(); this.tabPage1.SuspendLayout(); this.panel1.SuspendLayout(); + this.groupBox13.SuspendLayout(); this.groupBox12.SuspendLayout(); this.groupBox2.SuspendLayout(); this.groupBox1.SuspendLayout(); @@ -204,15 +208,49 @@ private void InitializeComponent() // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.Controls.Add(this.groupBox13); this.panel1.Controls.Add(this.groupBox12); this.panel1.Controls.Add(this.OkButton); this.panel1.Controls.Add(this.groupBox2); this.panel1.Controls.Add(this.groupBox1); - this.panel1.Location = new System.Drawing.Point(125, 49); + this.panel1.Location = new System.Drawing.Point(125, 7); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(321, 298); + this.panel1.Size = new System.Drawing.Size(321, 376); this.panel1.TabIndex = 1; // + // groupBox13 + // + this.groupBox13.Controls.Add(this.label22); + this.groupBox13.Controls.Add(this.DatatableDef); + this.groupBox13.Location = new System.Drawing.Point(3, 273); + this.groupBox13.Name = "groupBox13"; + this.groupBox13.Size = new System.Drawing.Size(315, 75); + this.groupBox13.TabIndex = 13; + this.groupBox13.TabStop = false; + this.groupBox13.Text = "Config"; + // + // label22 + // + this.label22.AutoSize = true; + this.label22.Location = new System.Drawing.Point(6, 22); + this.label22.Name = "label22"; + this.label22.Size = new System.Drawing.Size(71, 13); + this.label22.TabIndex = 14; + this.label22.Text = "Datatable def"; + // + // DatatableDef + // + this.DatatableDef.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.DatatableDef.Filter = "JSON files(*.json)|*.json|All files(*.*)|*.*"; + this.DatatableDef.Location = new System.Drawing.Point(83, 19); + this.DatatableDef.Name = "DatatableDef"; + this.DatatableDef.Path = ""; + this.DatatableDef.SelectsFolder = false; + this.DatatableDef.Size = new System.Drawing.Size(226, 20); + this.DatatableDef.TabIndex = 13; + this.DatatableDef.PathChanged += new TaikoSoundEditor.Commons.Controls.PathSelector.OnPathChanged(this.DatatableDef_PathChanged); + // // groupBox12 // this.groupBox12.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -280,7 +318,7 @@ private void InitializeComponent() // OkButton // this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkButton.Location = new System.Drawing.Point(225, 276); + this.OkButton.Location = new System.Drawing.Point(225, 354); this.OkButton.Name = "OkButton"; this.OkButton.Size = new System.Drawing.Size(93, 20); this.OkButton.TabIndex = 10; @@ -413,7 +451,7 @@ private void InitializeComponent() this.MusicAttributePathSelector.Name = "MusicAttributePathSelector"; this.MusicAttributePathSelector.Path = ""; this.MusicAttributePathSelector.SelectsFolder = false; - this.MusicAttributePathSelector.Size = new System.Drawing.Size(210, 20); + this.MusicAttributePathSelector.Size = new System.Drawing.Size(208, 20); this.MusicAttributePathSelector.TabIndex = 9; // // label8 @@ -1357,6 +1395,8 @@ private void InitializeComponent() this.TabControl.ResumeLayout(false); this.tabPage1.ResumeLayout(false); this.panel1.ResumeLayout(false); + this.groupBox13.ResumeLayout(false); + this.groupBox13.PerformLayout(); this.groupBox12.ResumeLayout(false); this.groupBox12.PerformLayout(); this.groupBox2.ResumeLayout(false); @@ -1512,5 +1552,8 @@ private void InitializeComponent() private Label label21; private TextBox DatatableKeyBox; private CheckBox UseEncryptionBox; + private GroupBox groupBox13; + private Label label22; + private PathSelector DatatableDef; } } \ No newline at end of file diff --git a/MainForm.Exports.cs b/MainForm.Exports.cs index ba94d40..baf324e 100644 --- a/MainForm.Exports.cs +++ b/MainForm.Exports.cs @@ -2,9 +2,11 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Windows.Forms; using TaikoSoundEditor.Collections; using TaikoSoundEditor.Commons; +using TaikoSoundEditor.Commons.IO; using TaikoSoundEditor.Commons.Utils; using TaikoSoundEditor.Data; @@ -27,12 +29,12 @@ private void ExportDatatable(string path) mo.Items.AddRange(MusicOrderViewer.SongCards.Select(_ => _.MusicOrder)); var wl = new WordList(); - wl.Items.AddRange(WordList.Items); + wl.Items.AddRange(WordList.Items); - Config.DatatableIO.Serialize(Path.Combine(path, "musicinfo.bin"), mi, indented: !DatatableSpaces.Checked); - Config.DatatableIO.Serialize(Path.Combine(path, "music_attribute.bin"), ma, fixBools: true); - Config.DatatableIO.Serialize(Path.Combine(path, "music_order.bin"), mo); - Config.DatatableIO.Serialize(Path.Combine(path, "wordlist.bin"), wl, indented: true); + Config.DatatableIO.DynamicSerialize(Path.Combine(path, "musicinfo.bin"), mi.Cast(DatatableTypes.MusicInfo) , indented: !DatatableSpaces.Checked); + Config.DatatableIO.DynamicSerialize(Path.Combine(path, "music_attribute.bin"), ma.Cast(DatatableTypes.MusicAttribute), fixBools: true); + Config.DatatableIO.DynamicSerialize(Path.Combine(path, "music_order.bin"), mo.Cast(DatatableTypes.MusicOrder)); + Config.DatatableIO.DynamicSerialize(Path.Combine(path, "wordlist.bin"), wl.Cast(DatatableTypes.Word), indented: true); } private void ExportNusBanks(string path) @@ -54,26 +56,33 @@ private void ExportSoundBinaries(string path) if (!Directory.Exists(sdir)) Directory.CreateDirectory(sdir); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_e.bin"), ns.EBin); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_n.bin"), ns.NBin); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_h.bin"), ns.HBin); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_m.bin"), ns.MBin); + void Save(string suffix, byte[] bytes) + { + if (UseEncryptionBox.Checked) + bytes = SSL.EncryptFumen(bytes); + File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_{suffix}.bin"), bytes); + } + + Save("e", ns.EBin); + Save("n", ns.NBin); + Save("h", ns.HBin); + Save("m", ns.MBin); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_e_1.bin"), ns.EBin1); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_n_1.bin"), ns.NBin1); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_h_1.bin"), ns.HBin1); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_m_1.bin"), ns.MBin1); + Save("e_1", ns.EBin1); + Save("n_1", ns.NBin1); + Save("h_1", ns.HBin1); + Save("m_1", ns.MBin1); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_e_2.bin"), ns.EBin2); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_n_2.bin"), ns.NBin2); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_h_2.bin"), ns.HBin2); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_m_2.bin"), ns.MBin2); + Save("e_2", ns.EBin2); + Save("n_2", ns.NBin2); + Save("h_2", ns.HBin2); + Save("m_2", ns.MBin2); if (ns.MusicAttribute.CanPlayUra) { - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_x.bin"), ns.XBin); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_x_1.bin"), ns.XBin1); - File.WriteAllBytes(Path.Combine(sdir, $"{ns.Id}_x_2.bin"), ns.XBin2); + Save("x", ns.XBin); + Save("x_1", ns.XBin1); + Save("x_2", ns.XBin2); } } } diff --git a/MainForm.cs b/MainForm.cs index c40bf4d..f6c947d 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using System.Linq; using System.Windows.Forms; using TaikoSoundEditor.Commons.Controls; @@ -28,7 +29,8 @@ public MainForm() DatatableKeyBox.Text = Config.IniFile.Read("DatatableKey"); FumenKeyBox.Text = Config.IniFile.Read("FumenKey"); - LoadPreferences(); + LoadPreferences(); + DatatableDef.Path = Config.DatatableDefPath; //SortByGenreToolStripMenuItem.RadioCheck = true; } @@ -426,7 +428,20 @@ private void LoadedMusicBox_DrawItem(object sender, DrawItemEventArgs e) var selItem = LoadedMusicBox.Items[e.Index] as IMusicInfo; TextRenderer.DrawText(e.Graphics, $"{selItem.UniqueId}. {selItem.Id}", Font, e.Bounds, e.ForeColor, e.BackColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter); e.DrawFocusRectangle(); + } + private void DatatableDef_PathChanged(object sender, EventArgs args) + { + try + { + var json = File.ReadAllText(DatatableDef.Path); + DatatableTypes.LoadFromJson(json); + Config.DatatableDefPath = DatatableDef.Path; + } + catch(Exception e) + { + MessageBox.Show(e.Message); + } } - + } } \ No newline at end of file diff --git a/Resources/datatable_def_32_09.txt b/Resources/datatable_def_32_09.txt new file mode 100644 index 0000000..6f11149 --- /dev/null +++ b/Resources/datatable_def_32_09.txt @@ -0,0 +1,121 @@ +{ + "types": [ + { + "name": "Word", + "properties": [ + { "name": "Key", "type": "String", "isReadOnly": true, "defaultValue": "song_..." }, + { "name": "JapaneseText", "type": "String", "defaultValue": "text.." }, + { "name": "JapaneseFontType", "type": "Int32", "defaultValue": "0" } + ], + "interface": "TaikoSoundEditor.Data.IWord" + }, + + { + "name": "MusicOrder", + "properties": [ + { "name": "GenreNo", "type": "Int32", "defaultValue": "0" }, + { "name": "Id", "type": "String", "defaultValue": "ABCDEF", "isReadOnly":true }, + { "name": "UniqueId", "type": "Int32", "defaultValue": "0", "isReadOnly":true}, + { "name": "CloseDispType", "type": "Int32", "defaultValue": "0" } + ], + "interface": "TaikoSoundEditor.Data.IMusicOrder" + }, + + { + "name": "MusicAttribute", + "properties": [ + { "name": "Id", "type": "String", "defaultValue": "ABCDEF", "isReadOnly":true }, + { "name": "UniqueId", "type": "Int32", "defaultValue": "0", "isReadOnly":true}, + { "name": "New", "type": "Boolean", "defaultValue": "false"}, + { "name": "DoublePlay", "type": "Boolean", "defaultValue": "false"}, + { "name": "IsNotCopyright", "type": "Boolean", "defaultValue": "true"}, + { "name": "Tag1", "type": "String", "defaultValue": ""}, + { "name": "Tag2", "type": "String", "defaultValue": ""}, + { "name": "Tag3", "type": "String", "defaultValue": ""}, + { "name": "Tag4", "type": "String", "defaultValue": ""}, + { "name": "Tag5", "type": "String", "defaultValue": ""}, + { "name": "Tag6", "type": "String", "defaultValue": ""}, + { "name": "Tag7", "type": "String", "defaultValue": ""}, + { "name": "Tag8", "type": "String", "defaultValue": ""}, + { "name": "Tag9", "type": "String", "defaultValue": ""}, + { "name": "Tag10", "type": "String", "defaultValue": ""}, + { "name": "ensoPartsID1", "type": "Int32", "defaultValue": "0"}, + { "name": "ensoPartsID2", "type": "Int32", "defaultValue": "0"}, + { "name": "DonBg1p", "type": "String", "defaultValue": ""}, + { "name": "DonBg2p", "type": "String", "defaultValue": ""}, + { "name": "DancerDai", "type": "String", "defaultValue": ""}, + { "name": "Dancer", "type": "String", "defaultValue": ""}, + { "name": "DanceNormalBg", "type": "String", "defaultValue": ""}, + { "name": "DanceFeverBg", "type": "String", "defaultValue": ""}, + { "name": "RendaEffect", "type": "String", "defaultValue": ""}, + { "name": "Fever", "type": "String", "defaultValue": ""}, + { "name": "DonBg1p1", "type": "String", "defaultValue": ""}, + { "name": "DonBg2p1", "type": "String", "defaultValue": ""}, + { "name": "DancerDai1", "type": "String", "defaultValue": ""}, + { "name": "Dancer1", "type": "String", "defaultValue": ""}, + { "name": "DanceNormalBg1", "type": "String", "defaultValue": ""}, + { "name": "DanceFeverBg1", "type": "String", "defaultValue": ""}, + { "name": "RendaEffect1", "type": "String", "defaultValue": ""}, + { "name": "Fever1", "type": "String", "defaultValue": ""} + ], + "interface": "TaikoSoundEditor.Data.IMusicAttribute" + }, + { + "name": "MusicInfo", + "properties": [ + { "name": "Id", "type": "String", "defaultValue": "ABCDEF", "isReadOnly":true }, + { "name": "UniqueId", "type": "Int32", "defaultValue": "0", "isReadOnly":true}, + { "name": "GenreNo", "type": "Int32", "defaultValue": "0"}, + { "name": "SongFileName", "type": "String", "defaultValue": ""}, + { "name": "Papamama", "type": "Boolean", "defaultValue": "false"}, + { "name": "BranchEasy", "type": "Boolean", "defaultValue": "false"}, + { "name": "BranchNormal", "type": "Boolean", "defaultValue": "false"}, + { "name": "BranchHard", "type": "Boolean", "defaultValue": "false"}, + { "name": "BranchMania", "type": "Boolean", "defaultValue": "false"}, + { "name": "BranchUra", "type": "Boolean", "defaultValue": "false"}, + { "name": "StarEasy", "type": "Int32", "defaultValue": "0"}, + { "name": "StarNormal", "type": "Int32", "defaultValue": "0"}, + { "name": "StarHard", "type": "Int32", "defaultValue": "0"}, + { "name": "StarMania", "type": "Int32", "defaultValue": "0"}, + { "name": "StarUra", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiEasy", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiNormal", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiHard", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiMania", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiUra", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiEasyDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiNormalDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiHardDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiManiaDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiUraDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreEasy", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreNormal", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreHard", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreMania", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreUra", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreEasyDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreNormalDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreHardDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreManiaDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "ShinutiScoreUraDuet", "type": "Int32", "defaultValue": "0"}, + { "name": "EasyOnpuNum", "type": "Int32", "defaultValue": "0"}, + { "name": "NormalOnpuNum", "type": "Int32", "defaultValue": "0"}, + { "name": "HardOnpuNum", "type": "Int32", "defaultValue": "0"}, + { "name": "ManiaOnpuNum", "type": "Int32", "defaultValue": "0"}, + { "name": "UraOnpuNum", "type": "Int32", "defaultValue": "0"}, + { "name": "RendaTimeEasy", "type": "Double", "defaultValue": "0"}, + { "name": "RendaTimeNormal", "type": "Double", "defaultValue": "0"}, + { "name": "RendaTimeHard", "type": "Double", "defaultValue": "0"}, + { "name": "RendaTimeMania", "type": "Double", "defaultValue": "0"}, + { "name": "RendaTimeUra", "type": "Double", "defaultValue": "0"}, + { "name": "RendaTimeUra", "type": "Double", "defaultValue": "0"}, + { "name": "FuusenTotalEasy", "type": "Int32", "defaultValue": "0"}, + { "name": "FuusenTotalNormal", "type": "Int32", "defaultValue": "0"}, + { "name": "FuusenTotalHard", "type": "Int32", "defaultValue": "0"}, + { "name": "FuusenTotalMania", "type": "Int32", "defaultValue": "0"}, + { "name": "FuusenTotalUra", "type": "Int32", "defaultValue": "0"} + ], + "interface": "TaikoSoundEditor.Data.IMusicInfo" + } + ] +} \ No newline at end of file