static void MetatadaToINI(byte[] file, string filename) { DLCMetadata header = new DLCMetadata(); //Get title byte[] title_b = GetTextItem(ref file, 0, 16); byte[] description_b = GetTextItem(ref file, 0x10, 32); byte[] appname_b = GetTextItem(ref file, 0x30, 16); header.description = System.Text.Encoding.GetEncoding(932).GetString(description_b); header.title = System.Text.Encoding.GetEncoding(932).GetString(title_b); header.appname = System.Text.Encoding.GetEncoding(932).GetString(appname_b); //Get items int item_pointer = BitConverter.ToInt32(file, 0x280); header.dlc_id = BitConverter.ToUInt32(file, item_pointer); Console.WriteLine("Identifier: {0}", header.dlc_id); if ((file[item_pointer + 4] & 0xF) > 0) { header.chars_sonic = true; } if ((file[item_pointer + 4] >> 4) > 0) { header.chars_tails = true; } if ((file[item_pointer + 5] & 0xF) > 0) { header.chars_knuckles = true; } if ((file[item_pointer + 5] >> 4) > 0) { header.chars_e102 = true; } if ((file[item_pointer + 6] & 0xF) > 0) { header.chars_amy = true; } if ((file[item_pointer + 6] >> 4) > 0) { header.chars_big = true; } Console.Write("Characters: "); if (header.chars_sonic) { Console.Write("Sonic "); } if (header.chars_tails) { Console.Write("Tails "); } if (header.chars_knuckles) { Console.Write("Knuckles "); } if (header.chars_amy) { Console.Write("Amy "); } if (header.chars_big) { Console.Write("Big "); } if (header.chars_e102) { Console.Write("Gamma"); } Console.Write(System.Environment.NewLine); header.whatever = file[item_pointer + 7]; header.region = (DLCRegionLocks)BitConverter.ToInt32(file, item_pointer + 8); int item_count = BitConverter.ToInt32(file, 0x284); Console.WriteLine("Item table at {0}, count {1} (size {2} + 12)", item_pointer.ToString("X"), item_count, item_count * 30); header.items = new List <DLCObjectData>(item_count); item_pointer += 12; //Skip 12-byte item section header for (int u = 0; u < item_count; u++) { DLCObjectData dlcitem = new DLCObjectData(); dlcitem.level = file[item_pointer + u * 30]; dlcitem.act = file[item_pointer + u * 30 + 1]; dlcitem.scale_x = file[item_pointer + u * 30 + 2]; dlcitem.scale_y = file[item_pointer + u * 30 + 3]; dlcitem.scale_z = file[item_pointer + u * 30 + 4]; dlcitem.rotspeed_x = (sbyte)file[item_pointer + u * 30 + 5]; dlcitem.rotspeed_y = (sbyte)file[item_pointer + u * 30 + 6]; dlcitem.rotspeed_z = (sbyte)file[item_pointer + u * 30 + 7]; dlcitem.objecttype = (DLCObjectTypes)file[item_pointer + u * 30 + 8]; dlcitem.textureid = file[item_pointer + u * 30 + 9]; dlcitem.flags = (DLCObjectFlags)(BitConverter.ToUInt16(file, item_pointer + u * 30 + 10)); dlcitem.objectid = file[item_pointer + u * 30 + 12]; dlcitem.unknown3 = file[item_pointer + u * 30 + 13]; dlcitem.message = file[item_pointer + u * 30 + 14]; dlcitem.radius = file[item_pointer + u * 30 + 15]; dlcitem.warplevel = file[item_pointer + u * 30 + 16]; dlcitem.sound = file[item_pointer + u * 30 + 17]; dlcitem.rot_x = BitConverter.ToUInt16(file, item_pointer + u * 30 + 18); dlcitem.rot_y = BitConverter.ToUInt16(file, item_pointer + u * 30 + 20); dlcitem.rot_z = BitConverter.ToUInt16(file, item_pointer + u * 30 + 22); dlcitem.x = BitConverter.ToInt16(file, item_pointer + u * 30 + 24); dlcitem.y = BitConverter.ToInt16(file, item_pointer + u * 30 + 26); dlcitem.z = BitConverter.ToInt16(file, item_pointer + u * 30 + 28); header.items.Add(dlcitem); } //Get strings header.JapaneseStrings = new string[16]; header.EnglishStrings = new string[16]; header.FrenchStrings = new string[16]; header.SpanishStrings = new string[16]; header.GermanStrings = new string[16]; int text_pointer = BitConverter.ToInt32(file, 0x288); int text_count = BitConverter.ToInt32(file, 0x28C); Console.WriteLine("String table at {0}, count {1} (size {2})", text_pointer.ToString("X"), text_count, text_count * 64); List <string> strings = new List <string>(); for (int u = 0; u < text_count; u++) { byte[] arr = new byte[64]; Array.Copy(file, text_pointer + 64 * u, arr, 0, 64); int charcount = 0; for (int a = 0; a < arr.Length; a++) { if (arr[a] == 0) { break; } charcount++; } System.Text.Encoding enc = System.Text.Encoding.GetEncoding(932); if (u >= 32) { enc = System.Text.Encoding.GetEncoding(1252); } string str = enc.GetString(arr, 0, charcount); strings.Add(str); } //Process special characters List <string> strings_new = new List <string>(); foreach (string str in strings) { string newstr = System.String.Empty; for (int s = 0; s < str.Length; s++) { if (s == 0 && (str[s] == '~' || str[s] == 't')) { newstr += ("\t"); } else if (str[s] == 't' && str[0] == 't') { newstr += ("\t"); } else if (str[s] == '~' && str[0] == '~') { newstr += ("\t"); } else if ((str[s] == 'n' && str[0] == 't') || (str[s] == '@' && str[0] == '~')) { newstr += ("\n"); } else { newstr += str[s]; } } strings_new.Add(newstr); } string[] stringarr = strings_new.ToArray(); for (int u = 0; u < 16; u++) { if (u >= text_count) { break; } header.JapaneseStrings[u] = stringarr[u]; if (text_count <= 16) { continue; } header.EnglishStrings[u] = stringarr[u + 16]; if (text_count <= 32) { continue; } header.FrenchStrings[u] = stringarr[u + 32]; if (text_count <= 48) { continue; } header.SpanishStrings[u] = stringarr[u + 48]; if (text_count <= 64) { continue; } header.GermanStrings[u] = stringarr[u + 64]; } //Check if an MLT is present int mlt_value = BitConverter.ToInt32(file, 0x2A0); if (mlt_value != 0) { header.has_mlt = true; } IniSerializer.Serialize(header, filename); }
static byte[] ConvertMetadata(string filename) { Console.WriteLine("Converting DLC metadata: {0}", filename); DLCMetadata header = IniSerializer.Deserialize <DLCMetadata>(filename); byte[] mlt = new byte[0]; byte[] prs; byte[] pvm = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), Path.GetFileNameWithoutExtension(filename) + ".pvm")); if (File.Exists(Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), Path.GetFileNameWithoutExtension(filename) + ".bin"))) { prs = FraGag.Compression.Prs.Compress(File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), Path.GetFileNameWithoutExtension(filename) + ".bin"))); Console.WriteLine("Using binary data instead of SA1MDL file"); } else { prs = ConvertModel(Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), Path.GetFileNameWithoutExtension(filename) + ".sa1mdl")); } if (header.has_mlt) { mlt = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), Path.GetFileNameWithoutExtension(filename) + ".mlt")); } List <byte> result = new List <byte>(); //Convert item table header List <byte> itemtable = new List <byte>(); itemtable.AddRange(BitConverter.GetBytes(header.dlc_id)); Console.WriteLine("Identifier: {0}", header.dlc_id); byte chars_sonictails = 0; byte chars_knuxe102 = 0; byte chars_amybig = 0; if (header.chars_sonic) { chars_sonictails |= 0x1; } if (header.chars_tails) { chars_sonictails |= 0x10; } itemtable.Add(chars_sonictails); if (header.chars_knuckles) { chars_knuxe102 |= 0x1; } if (header.chars_e102) { chars_knuxe102 |= 0x10; } itemtable.Add(chars_knuxe102); if (header.chars_amy) { chars_amybig |= 0x1; } if (header.chars_big) { chars_amybig |= 0x10; } itemtable.Add(chars_amybig); itemtable.Add(header.whatever); itemtable.AddRange(BitConverter.GetBytes((int)(header.region))); Console.Write("Characters: "); if (header.chars_sonic) { Console.Write("Sonic "); } if (header.chars_tails) { Console.Write("Tails "); } if (header.chars_knuckles) { Console.Write("Knuckles "); } if (header.chars_amy) { Console.Write("Amy "); } if (header.chars_big) { Console.Write("Big "); } if (header.chars_e102) { Console.Write("Gamma"); } Console.Write(System.Environment.NewLine); //Convert item table foreach (DLCObjectData item in header.items) { itemtable.Add(item.level); itemtable.Add(item.act); itemtable.Add(item.scale_x); itemtable.Add(item.scale_y); itemtable.Add(item.scale_z); itemtable.Add((byte)item.rotspeed_x); itemtable.Add((byte)item.rotspeed_y); itemtable.Add((byte)item.rotspeed_z); itemtable.Add((byte)item.objecttype); itemtable.Add(item.textureid); itemtable.AddRange((BitConverter.GetBytes((ushort)item.flags))); itemtable.Add(item.objectid); itemtable.Add(item.unknown3); itemtable.Add(item.message); itemtable.Add(item.radius); itemtable.Add(item.warplevel); itemtable.Add(item.sound); itemtable.AddRange(BitConverter.GetBytes(item.rot_x)); itemtable.AddRange(BitConverter.GetBytes(item.rot_y)); itemtable.AddRange(BitConverter.GetBytes(item.rot_z)); itemtable.AddRange(BitConverter.GetBytes(item.x)); itemtable.AddRange(BitConverter.GetBytes(item.y)); itemtable.AddRange(BitConverter.GetBytes(item.z)); } do { itemtable.Add(0); }while (itemtable.Count % 16 != 0); //Convert Japanese strings List <byte> stringtable = new List <byte>(); foreach (string str in header.JapaneseStrings) { for (int s = 0; s < str.Length; s++) { if (str[s] == '\t') { stringtable.AddRange(System.Text.Encoding.GetEncoding(932).GetBytes("t")); } else if (str[s] == '\n') { stringtable.AddRange(System.Text.Encoding.GetEncoding(932).GetBytes("n")); } else { stringtable.AddRange(System.Text.Encoding.GetEncoding(932).GetBytes(str[s].ToString())); } } do { stringtable.Add(0); }while (stringtable.Count % 64 != 0); } //Convert European strings stringtable.AddRange(ProcessEuropeanStrings(header.EnglishStrings, 932)); //English uses the same character set as Japanese stringtable.AddRange(ProcessEuropeanStrings(header.FrenchStrings)); stringtable.AddRange(ProcessEuropeanStrings(header.SpanishStrings)); stringtable.AddRange(ProcessEuropeanStrings(header.GermanStrings)); //Set size int fullsize = mlt.Length + prs.Length + pvm.Length + itemtable.Count + stringtable.Count + 64; //64 is sections table at 0x280 w/checksum + 16 bytes of padding if ((fullsize + 640) % 512 != 0) { do { fullsize++; }while ((fullsize + 640) % 512 != 0); } //Convert title byte[] title_b = new byte[16]; byte[] title = System.Text.Encoding.GetEncoding(932).GetBytes(header.title); Array.Copy(title, 0, title_b, 0, title.Length); result.AddRange(title_b); byte[] desc_b = new byte[32]; byte[] desc = System.Text.Encoding.GetEncoding(932).GetBytes(header.description); Array.Copy(desc, 0, desc_b, 0, desc.Length); result.AddRange(desc_b); byte[] app_b = new byte[16]; byte[] app = System.Text.Encoding.GetEncoding(932).GetBytes(header.appname); Array.Copy(app, 0, app_b, 0, app.Length); result.AddRange(app_b); result.AddRange(BitConverter.GetBytes((ushort)1)); //Number of icons result.AddRange(BitConverter.GetBytes((ushort)1)); //Animation speed result.AddRange(BitConverter.GetBytes((ushort)0)); //Eyecatch type result.AddRange(BitConverter.GetBytes((ushort)0)); //CRC (unused) result.AddRange(BitConverter.GetBytes((uint)fullsize)); //Size of the entire thing without VMS header for (int u = 0; u < 20; u++) { result.Add(0); } result.AddRange(ConvertIcon(Path.Combine(Environment.CurrentDirectory, Path.GetFileNameWithoutExtension(filename), Path.GetFileNameWithoutExtension(filename) + ".bmp"))); result.AddRange(BitConverter.GetBytes((uint)0x2C0)); //Item layout table pointer Console.WriteLine("Item table at 2C0, count {0} (size {1} + 12)", header.items.Count, header.items.Count * 30); result.AddRange(BitConverter.GetBytes((uint)header.items.Count)); int textpointer = 704 + itemtable.Count; result.AddRange(BitConverter.GetBytes((uint)textpointer)); //Text table pointer int textcount = header.JapaneseStrings.Length + header.EnglishStrings.Length + header.FrenchStrings.Length + header.GermanStrings.Length + header.SpanishStrings.Length; result.AddRange(BitConverter.GetBytes((uint)textcount)); Console.WriteLine("String table at {0}, count {1} (size {2})", textpointer.ToString("X"), textcount, textcount * 64); int pvmpointer = textpointer + 64 * textcount; result.AddRange(BitConverter.GetBytes((uint)pvmpointer)); result.AddRange(BitConverter.GetBytes((uint)1)); //PVM count ushort numtextures = BitConverter.ToUInt16(pvm, 0xA); result.AddRange(BitConverter.GetBytes((uint)numtextures)); Console.WriteLine("PVM at {0}, number of textures {1} (size {2})", pvmpointer.ToString("X"), numtextures, pvm.Length); int mltpointer = pvmpointer + pvm.Length; result.AddRange(BitConverter.GetBytes((uint)mltpointer)); if (header.has_mlt) { result.AddRange(BitConverter.GetBytes((uint)1)); Console.WriteLine("MLT at {0} (size {1})", mltpointer.ToString("X"), mlt.Length); } else { result.AddRange(BitConverter.GetBytes((uint)0)); } int prspointer = mltpointer + mlt.Length; result.AddRange(BitConverter.GetBytes((uint)prspointer)); result.AddRange(BitConverter.GetBytes((uint)1)); //PRS count List <byte> final = new List <byte>(); Console.WriteLine("PRS at {0} (size {1})", prspointer.ToString("X"), prs.Length); final.AddRange(itemtable.ToArray()); final.AddRange(stringtable.ToArray()); final.AddRange(pvm); if (header.has_mlt) { final.AddRange(mlt); } final.AddRange(prs); byte[] finalarr = final.ToArray(); uint checksum = CalculateChecksum(ref finalarr, 0, finalarr.Length); result.AddRange(BitConverter.GetBytes(checksum)); Console.WriteLine("Checksum: {0} ({1})", checksum.ToString("X8"), (int)checksum); for (int u = 0; u < 16; u++) { result.Add(0); } result.AddRange(final); if (result.Count % 512 != 0) { do { result.Add(0); }while (result.Count % 512 != 0); } return(result.ToArray()); }