public VMSChallengeResult(byte[] htmlpage) { byte[] decr = VMSFile.GetDataFromHTML(htmlpage); DataType = (DataIDs)BitConverter.ToUInt32(decr, 0x04); UserData = new PDATA(decr, 0x08); ResultData = new EventResultDataChild(decr, 0x44); }
public static byte[] GetDataFromHTML(byte[] file) { for (int u = 0; u < file.Length - 15; u++) { if (System.Text.Encoding.GetEncoding(932).GetString(file, u, 15) == "NAME=\"chaodata\"") { // Load Base64 StringBuilder b64str = new StringBuilder(); for (int k = u + 23; k < file.Length - 2; k++) { if (file[k] == 0x22 && file[k + 1] == 0x3E) { break; } else { b64str.Append((Convert.ToChar(file[k])).ToString()); } } // Decode Base64, decrypt byte[] data = Convert.FromBase64String(b64str.ToString()); VMSFile.DecryptData(ref data); return(data); } } return(null); }
private void SaveDecodedData(string source) { byte[] decr = VMSFile.GetDataFromHTML(File.ReadAllBytes(source)); string filename = Path.Combine(Path.GetDirectoryName(source), Path.GetFileNameWithoutExtension(source + "_data.bin")); File.WriteAllBytes(filename, decr); System.Windows.Forms.MessageBox.Show("Decrypted data saved as " + filename + "."); }
static void GetPdata(string filepath) { byte[] file = File.ReadAllBytes(filepath); byte[] data = VMSFile.GetDataFromHTML(file); PDATA pd = new PDATA(data, 8); File.WriteAllBytes(Path.ChangeExtension(filepath, ".pd"), pd.GetBytes()); Application.Exit(); }
public VMIFile(VMSFile data, string ResName) { if (ResName.Length > 8) { System.Windows.Forms.MessageBox.Show("For the VMI file to work correctly, VMS filename should be 8 characters or less.", "DLC Tool Warning", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning); } Description = data.Description; Copyright = data.AppName; Year = (ushort)DateTime.Now.Year; Month = (byte)DateTime.Now.Month; Day = (byte)DateTime.Now.Day; Hour = (byte)DateTime.Now.Hour; Minute = (byte)DateTime.Now.Minute; Second = (byte)DateTime.Now.Second; switch (DateTime.Now.DayOfWeek) { case DayOfWeek.Sunday: Weekday = 0; break; case DayOfWeek.Monday: Weekday = 1; break; case DayOfWeek.Tuesday: Weekday = 2; break; case DayOfWeek.Wednesday: Weekday = 3; break; case DayOfWeek.Thursday: Weekday = 4; break; case DayOfWeek.Friday: Weekday = 5; break; case DayOfWeek.Saturday: Weekday = 6; break; } FileID = 1; ResourceName = ResName; if (data is VMS_DLC dlc) { FileName = "SONICADV_" + dlc.Identifier.ToString("D3"); Size = (uint)dlc.GetBytes().Length; } else { FileName = "VMSDATA"; Size = data.GetLength(); } }
public static void ProcessVMS(ref byte[] input) { int aniFrames = input[0x40] + (input[0x41] << 8); int dataStart = 0x80 + (aniFrames * 0x200); byte[] encrypted = new byte[input.Length - dataStart]; Array.Copy(input, dataStart, encrypted, 0, encrypted.Length); VMSFile.DecryptData(ref encrypted); Array.Copy(encrypted, 0, input, dataStart, encrypted.Length); }
public void LoadWorldRankFile(string filename) { byte[] file = File.ReadAllBytes(filename); // Get region radioButtonJapan.Checked = BitConverter.ToUInt32(file, 0) == 0xC0B0DEC3; radioButtonInternational.Checked = Encoding.GetEncoding(932).GetString(file, 0, 11) == "DATA_UPLOAD"; // Decrypt data DecryptedData = VMSFile.GetDataFromHTML(file); // Set fields labelPlayTime.Text = "Total Time: " + FramesToTimeString(BitConverter.ToInt32(DecryptedData, 0x48)); textBoxIndividualID.Text = VMSFile.GetFieldFromHTML(file, "dcid"); textBoxSubmitted.Text = VMSFile.GetFieldFromHTML(file, "mailid"); saveAsToolStripMenuItem.Enabled = true; }
private void decodeUploadDataToolStripMenuItem_Click(object sender, EventArgs e) { using (OpenFileDialog od = new OpenFileDialog() { DefaultExt = "vms", Filter = "VMS Files|*.vms|All Files|*.*" }) if (od.ShowDialog(this) == DialogResult.OK) { byte[] data = File.ReadAllBytes(od.FileName); byte[] result = VMSFile.GetDataFromHTML(data); string outpath = Path.Combine(Path.GetDirectoryName(od.FileName), Path.GetFileNameWithoutExtension(od.FileName)) + "_dec.bin"; File.WriteAllBytes(outpath, result); MessageBox.Show(this, "Binary file saved as:\n" + outpath, "VMS Editor", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
public byte[] GetBytes() { List <byte> result = new List <byte>(); result.AddRange(BitConverter.GetBytes((uint)0)); // Checksum result.AddRange(BitConverter.GetBytes((uint)DataType)); // Hardcoded data ID result.AddRange(UserData.GetBytes()); result.AddRange(ResultData.GetBytes()); byte[] end = result.ToArray(); int checksum = VMSFile.CalculateUploadCRC(ref end); result.RemoveRange(0, 4); result.InsertRange(0, BitConverter.GetBytes(checksum)); return(result.ToArray()); }
private void LoadEventResult(string filename) { byte[] file = File.ReadAllBytes(filename); VMSChallengeResult result = new VMSChallengeResult(file); numericUpDownEventID.Value = result.ResultData.EventID; numericUpDownFrames.Value = result.ResultData.EventTime; comboBoxCharacter.SelectedIndex = (int)result.ResultData.Character; labelTime.Text = "Total Time: " + FramesToTimeString((int)result.ResultData.EventTime); radioButtonCart.Checked = result.DataType == DataIDs.CartResultChecksum; radioButtonEvent.Checked = result.DataType == DataIDs.EventResultChecksum; textBoxIndividualID.Text = VMSFile.GetFieldFromHTML(file, "dcid"); textBoxSubmitted.Text = VMSFile.GetFieldFromHTML(file, "mailid"); radioButtonJapan.Checked = (BitConverter.ToUInt32(file, 0) == 0xDDDECDB2) || (BitConverter.ToUInt32(file, 0) == 0xC0C4B0B6); radioButtonInternational.Checked = System.Text.Encoding.GetEncoding(932).GetString(file, 0, 12) == "EVENT_RESULT" || System.Text.Encoding.GetEncoding(932).GetString(file, 0, 9) == "CART_TIME"; }
private void vMSFileToolStripMenuItem_Click(object sender, EventArgs e) { using (OpenFileDialog od = new OpenFileDialog() { DefaultExt = "vms", Filter = "VMS Files|*.vms|All Files|*.*" }) if (od.ShowDialog(this) == DialogResult.OK) { byte[] data = File.ReadAllBytes(od.FileName); int aniFrames = data[0x40] + (data[0x41] << 8); int dataStart = 0x80 + (aniFrames * 0x200); byte[] encrypted = new byte[data.Length - dataStart]; Array.Copy(data, dataStart, encrypted, 0, encrypted.Length); VMSFile.DecryptData(ref encrypted); Array.Copy(encrypted, 0, data, dataStart, encrypted.Length); string outpath = Path.Combine(Path.GetDirectoryName(od.FileName), Path.GetFileNameWithoutExtension(od.FileName)) + "_dec.vms"; File.WriteAllBytes(outpath, data); MessageBox.Show(this, "VMS file saved as:\n" + outpath, "VMS Editor", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
public void LoadVMS_DLC(string filename) { byte[] file = File.ReadAllBytes(filename); // Get metadata and icon meta = new VMS_DLC(file); metaBackup = new VMS_DLC(meta); meta.Icon = GetIconFromFile(file); // Get PVM pointer uint pvm_pointer = BitConverter.ToUInt32(file, 0x290); int pvm_value = BitConverter.ToInt32(file, 0x294); int pvm_count = BitConverter.ToInt32(file, 0x298); if (pvm_value != 0) { Console.WriteLine("PVM at {0}, number of textures {1}", pvm_pointer.ToString("X"), pvm_count); } checkBoxEnableTextures.Checked = pvm_value != 0; // Get MLT pointer uint mlt_pointer = BitConverter.ToUInt32(file, 0x29C); int mlt_value = BitConverter.ToInt32(file, 0x2A0); if (mlt_value != 0) { Console.WriteLine("MLT at {0}", mlt_pointer.ToString("X")); } // Get PRS pointer uint prs_pointer = BitConverter.ToUInt32(file, 0x2A4); int prs_value = BitConverter.ToInt32(file, 0x2A8); if (prs_value != 0) { Console.WriteLine("PRS at {0}", prs_pointer.ToString("X")); } // Checksum uint crc = VMSFile.CalculateChecksum(ref file, 0x2C0, file.Length); Console.WriteLine("Checksum file / calculated: {0} ({1}) / {2} ({3})", BitConverter.ToInt32(file, 0x2AC).ToString("X"), BitConverter.ToInt32(file, 0x2AC), crc.ToString("X"), (int)crc); // Retrieve sections // Get PVM int pvm_size = (int)mlt_pointer - (int)pvm_pointer; if (pvm_size > 0 && pvm_size + pvm_pointer <= file.Length) { meta.TextureData = new byte[pvm_size]; Array.Copy(file, pvm_pointer, meta.TextureData, 0, pvm_size); UpdateTextures(); } // Get MLT int mlt_size = (int)prs_pointer - (int)mlt_pointer; if (mlt_size > 0 && mlt_pointer + mlt_size <= file.Length) { buttonLoadMLT.Enabled = buttonSaveMLT.Enabled = checkBoxEnableSound.Checked = true; meta.SoundData = new byte[mlt_size]; Array.Copy(file, mlt_pointer, meta.SoundData, 0, mlt_size); //File.WriteAllBytes(Path.Combine(dir, fname + ".mlt"), mltdata); labelSoundSectionSize.Text = "Section size: " + meta.SoundData.Length.ToString() + " bytes"; } uint sectionsize = BitConverter.ToUInt32(file, 0x48); int text_count = BitConverter.ToInt32(file, 0x28C); int item_count = BitConverter.ToInt32(file, 0x284); int item_size = (item_count * 30 + 12); // 12-byte header do { item_size++; }while (item_size % 16 != 0); int prs_size = 0; if (prs_value != 0) { prs_size = file.Length - (int)prs_pointer; } Console.WriteLine("Headerless size {0}, item size {1}, text size {2}, PVM size {3}, MLT size {4}, PRS size {5}", sectionsize, item_size, text_count * 64, pvm_size, mlt_size, prs_size); // Get Model if (prs_size > 0 && prs_pointer + prs_size <= file.Length) { byte[] prsdata = new byte[prs_size]; //Console.WriteLine("Copy from array of size {0} from {1} to array size {2}", file.Length, prs_pointer, prsdata.Length); Array.Copy(file, prs_pointer, prsdata, 0, prs_size); prsdata = FraGag.Compression.Prs.Decompress(prsdata); // Model pointer uint modelpointer = BitConverter.ToUInt32(prsdata, 0) - 0xCCA4000; //Console.WriteLine("Model pointer: {0}", modelpointer.ToString("X")); labelModelSectionSize.Text = "Section size: " + prsdata.Length.ToString() + " bytes"; try { NJS_OBJECT mdl = new NJS_OBJECT(prsdata, (int)modelpointer, 0xCCA4000, ModelFormat.Basic, null); labelModelInfo.Text = GetModelInfo(mdl); } catch (Exception) { labelModelInfo.Text = "Error getting model information."; } checkBoxEnableModel.Checked = true; } UpdateGeneralInfo(); currentFilename = toolStripStatusFile.Text = Path.GetFileName(filename); currentFullPath = filename; saveToolStripMenuItem.Enabled = true; // Add data for empty strings for (int i = 0; i < 16; i++) { if (meta.JapaneseStrings[i] == null) { meta.JapaneseStrings[i] = ""; } if (meta.EnglishStrings[i] == null) { meta.EnglishStrings[i] = ""; } if (meta.FrenchStrings[i] == null) { meta.FrenchStrings[i] = ""; } if (meta.GermanStrings[i] == null) { meta.GermanStrings[i] = ""; } if (meta.SpanishStrings[i] == null) { meta.SpanishStrings[i] = ""; } } UpdateMessage(); UpdateSize(); }
/// <summary> /// Save to a byte array (VMS file) /// </summary> public byte[] GetBytes() { List <byte> result = new List <byte>(); // Convert item table header List <byte> itemtable = new List <byte>(); itemtable.AddRange(BitConverter.GetBytes(Identifier)); byte chars_sonictails = 0; byte chars_knuxe102 = 0; byte chars_amybig = 0; byte chars_whatev = 0; if (EnableSonic) { chars_sonictails |= 0x1; } if (EnableTails) { chars_sonictails |= 0x10; } itemtable.Add(chars_sonictails); if (EnableKnuckles) { chars_knuxe102 |= 0x1; } if (EnableGamma) { chars_knuxe102 |= 0x10; } itemtable.Add(chars_knuxe102); if (EnableAmy) { chars_amybig |= 0x1; } if (EnableBig) { chars_amybig |= 0x10; } itemtable.Add(chars_amybig); if (EnableWhatever1) { chars_whatev |= 0x1; } if (EnableWhatever2) { chars_whatev |= 0x10; } itemtable.Add(chars_whatev); itemtable.AddRange(BitConverter.GetBytes((int)Region)); // Convert item table if (Items != null && Items.Count > 0) { foreach (DLCObjectData item in Items) { itemtable.Add(item.Level); itemtable.Add(item.Act); itemtable.Add(item.ScaleX); itemtable.Add(item.ScaleY); itemtable.Add(item.ScaleZ); itemtable.Add((byte)item.RotSpeedX); itemtable.Add((byte)item.RotSpeedY); itemtable.Add((byte)item.RotSpeedZ); itemtable.Add((byte)item.ObjectType); itemtable.Add(item.Texture); itemtable.AddRange((BitConverter.GetBytes((ushort)item.Flags))); itemtable.Add(item.InternalID); itemtable.Add(item.Unknown3); itemtable.Add(item.Message); itemtable.Add(item.TriggerDistance); itemtable.Add(item.WarpLevelOrSoundbank); itemtable.Add(item.WarpActOrSoundID); itemtable.AddRange(BitConverter.GetBytes(item.RotationX)); itemtable.AddRange(BitConverter.GetBytes(item.RotationY)); itemtable.AddRange(BitConverter.GetBytes(item.RotationZ)); itemtable.AddRange(BitConverter.GetBytes(item.X)); itemtable.AddRange(BitConverter.GetBytes(item.Y)); itemtable.AddRange(BitConverter.GetBytes(item.Z)); } } if (itemtable.Count % 32 != 0) { do { itemtable.Add(0); }while (itemtable.Count % 32 != 0); } // Convert Japanese strings List <byte> stringtable = new List <byte>(); foreach (string str in JapaneseStrings) { if (str != null) { 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 if (EnglishStrings != null) { stringtable.AddRange(ProcessStrings(EnglishStrings, 932)); // English uses the same character set as Japanese stringtable.AddRange(ProcessStrings(FrenchStrings)); stringtable.AddRange(ProcessStrings(SpanishStrings)); stringtable.AddRange(ProcessStrings(GermanStrings)); } // Set size int fullsize = SoundData.Length + ModelData.Length + TextureData.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, description etc. byte[] title_b = new byte[16]; byte[] title = System.Text.Encoding.GetEncoding(932).GetBytes(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(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(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(GetIconBytes(Icon)); result.AddRange(BitConverter.GetBytes((uint)0x2C0)); // Item layout table pointer result.AddRange(BitConverter.GetBytes((uint)Items.Count)); int textpointer = 704 + itemtable.Count; result.AddRange(BitConverter.GetBytes((uint)textpointer)); // Text table pointer int textcount = JapaneseStrings.Length + EnglishStrings.Length + FrenchStrings.Length + GermanStrings.Length + SpanishStrings.Length; result.AddRange(BitConverter.GetBytes((uint)textcount)); int pvmpointer = textpointer + 64 * textcount; result.AddRange(BitConverter.GetBytes((uint)pvmpointer)); int pvmcount = (TextureData != null && TextureData.Length != 0) ? 1 : 0; result.AddRange(BitConverter.GetBytes((uint)pvmcount)); // PVM count ushort numtextures = 0; if (pvmcount != 0) { numtextures = BitConverter.ToUInt16(TextureData, 0xA); } result.AddRange(BitConverter.GetBytes((uint)numtextures)); // Number of textures in the PVM int mltpointer = pvmpointer + TextureData.Length; result.AddRange(BitConverter.GetBytes((uint)mltpointer)); if (ContainsSound) { result.AddRange(BitConverter.GetBytes((uint)1)); // MLT count } else { result.AddRange(BitConverter.GetBytes((uint)0)); } int prspointer = mltpointer + SoundData.Length; result.AddRange(BitConverter.GetBytes((uint)prspointer)); if (ModelData != null && ModelData.Length > 0) { result.AddRange(BitConverter.GetBytes((uint)1)); // Number of PRS } else { result.AddRange(BitConverter.GetBytes((uint)0)); } List <byte> final = new List <byte>(); final.AddRange(itemtable.ToArray()); final.AddRange(stringtable.ToArray()); if (TextureData != null && TextureData.Length > 0) { final.AddRange(TextureData); } if (ContainsSound) { final.AddRange(SoundData); } final.AddRange(ModelData); byte[] finalarr = final.ToArray(); uint checksum = VMSFile.CalculateChecksum(ref finalarr, 0, finalarr.Length); result.AddRange(BitConverter.GetBytes(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); } // Encrypt byte[] resdata = result.ToArray(); ProcessVMS(ref resdata); return(resdata); }
/// <summary> /// Load from a byte array (VMS file) /// </summary> public VMS_DLC(byte[] file) { // Decrypt if encrypted if (BitConverter.ToUInt32(file, 0x280) != 0x2C0) { int aniFrames = file[0x40] + (file[0x41] << 8); int dataStart = 0x80 + (aniFrames * 0x200); byte[] encrypted = new byte[file.Length - dataStart]; Array.Copy(file, dataStart, encrypted, 0, encrypted.Length); VMSFile.DecryptData(ref encrypted); Array.Copy(encrypted, 0, file, dataStart, encrypted.Length); } // Get title and other stuff byte[] title_b = GetTextItem(ref file, 0, 16); byte[] description_b = GetTextItem(ref file, 0x10, 32); byte[] appname_b = GetTextItem(ref file, 0x30, 16); Description = System.Text.Encoding.GetEncoding(932).GetString(description_b); Title = System.Text.Encoding.GetEncoding(932).GetString(title_b); AppName = System.Text.Encoding.GetEncoding(932).GetString(appname_b); Icon = GetIconFromFile(file); // Get items int item_pointer = BitConverter.ToInt32(file, 0x280); if (item_pointer != 0x2C0) { System.Windows.Forms.MessageBox.Show("Unable to find SA1 DLC data.", "DLC Tool Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); Items = new List <DLCObjectData>(); JapaneseStrings = new string[16]; EnglishStrings = new string[16]; FrenchStrings = new string[16]; GermanStrings = new string[16]; SpanishStrings = new string[16]; TextureData = new byte[0]; SoundData = new byte[0]; ModelData = new byte[0]; return; } Identifier = BitConverter.ToUInt32(file, item_pointer); if ((file[item_pointer + 4] & 0xF) > 0) { EnableSonic = true; } if ((file[item_pointer + 4] >> 4) > 0) { EnableTails = true; } if ((file[item_pointer + 5] & 0xF) > 0) { EnableKnuckles = true; } if ((file[item_pointer + 5] >> 4) > 0) { EnableGamma = true; } if ((file[item_pointer + 6] & 0xF) > 0) { EnableAmy = true; } if ((file[item_pointer + 6] >> 4) > 0) { EnableBig = true; } if ((file[item_pointer + 7] & 0xF) > 0) { EnableWhatever1 = true; } if ((file[item_pointer + 7] >> 4) > 0) { EnableWhatever2 = true; } Region = (DLCRegionLocks)BitConverter.ToInt32(file, item_pointer + 8); // Item table int item_count = BitConverter.ToInt32(file, 0x284); 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.ScaleX = file[item_pointer + u * 30 + 2]; dlcitem.ScaleY = file[item_pointer + u * 30 + 3]; dlcitem.ScaleZ = file[item_pointer + u * 30 + 4]; dlcitem.RotSpeedX = (sbyte)file[item_pointer + u * 30 + 5]; dlcitem.RotSpeedY = (sbyte)file[item_pointer + u * 30 + 6]; dlcitem.RotSpeedX = (sbyte)file[item_pointer + u * 30 + 7]; dlcitem.ObjectType = (DLCObjectTypes)file[item_pointer + u * 30 + 8]; dlcitem.Texture = file[item_pointer + u * 30 + 9]; dlcitem.Flags = (DLCObjectFlags)(BitConverter.ToUInt16(file, item_pointer + u * 30 + 10)); dlcitem.InternalID = file[item_pointer + u * 30 + 12]; dlcitem.Unknown3 = file[item_pointer + u * 30 + 13]; dlcitem.Message = file[item_pointer + u * 30 + 14]; dlcitem.TriggerDistance = file[item_pointer + u * 30 + 15]; dlcitem.WarpLevelOrSoundbank = file[item_pointer + u * 30 + 16]; dlcitem.WarpActOrSoundID = file[item_pointer + u * 30 + 17]; dlcitem.RotationX = BitConverter.ToUInt16(file, item_pointer + u * 30 + 18); dlcitem.RotationY = BitConverter.ToUInt16(file, item_pointer + u * 30 + 20); dlcitem.RotationZ = 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); Items.Add(dlcitem); } // Get strings JapaneseStrings = new string[16]; EnglishStrings = new string[16]; FrenchStrings = new string[16]; SpanishStrings = new string[16]; GermanStrings = new string[16]; int text_pointer = BitConverter.ToInt32(file, 0x288); int text_count = BitConverter.ToInt32(file, 0x28C); 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; } JapaneseStrings[u] = stringarr[u]; if (text_count <= 16) { continue; } EnglishStrings[u] = stringarr[u + 16]; if (text_count <= 32) { continue; } FrenchStrings[u] = stringarr[u + 32]; if (text_count <= 48) { continue; } SpanishStrings[u] = stringarr[u + 48]; if (text_count <= 64) { continue; } GermanStrings[u] = stringarr[u + 64]; } TextureData = new byte[0]; SoundData = new byte[0]; ModelData = new byte[0]; // Get PVM pointer uint pvm_pointer = BitConverter.ToUInt32(file, 0x290); // Number of PVMs at 0x294, number of textures at 0x298 // Get MLT pointer uint mlt_pointer = BitConverter.ToUInt32(file, 0x29C); int nummlt = BitConverter.ToInt32(file, 0x2A0); if (nummlt != 0) { ContainsSound = true; } // Get PRS pointer uint prs_pointer = BitConverter.ToUInt32(file, 0x2A4); // Number of PRSes at 0x2A8 // Get PVM data int pvm_size = (int)mlt_pointer - (int)pvm_pointer; if (pvm_size > 0) { TextureData = new byte[pvm_size]; Array.Copy(file, pvm_pointer, TextureData, 0, pvm_size); } // Get MLT data int mlt_size = (int)prs_pointer - (int)mlt_pointer; if (mlt_size > 0) { SoundData = new byte[mlt_size]; Array.Copy(file, mlt_pointer, SoundData, 0, mlt_size); } int item_size = (item_count * 30 + 12); // 12-byte header, headerless section size at 0x48 do { item_size++; }while (item_size % 16 != 0); // The size of the PRS cannot be determined reliably because the file might be padded to have a size divisible by 512 // To get the correct size, the PRS chunk is read until the end of file or a sequence of 16 empty bytes int prs_size = 0; for (int indx = (int)prs_pointer; indx < file.Length; indx += 16) { bool valid = false; for (int u = 0; u < 16; u++) { if (file[indx + u] != 0) { valid = true; break; } } if (valid) { prs_size += 16; } else { break; } } // Get PRS data if (prs_size > 0) { ModelData = new byte[prs_size]; Array.Copy(file, prs_pointer, ModelData, 0, prs_size); } }