protected V3Format(SPCEntry entry) { this.entry = entry; if (entry.CmpFlag == 0x02) // Decompress data if needed { byte[] result = SPC.DecompressEntry(entry.Contents); if (result.Length != entry.DecSize) { throw new Exception($"Size mismatch: Size was {result.Length} but should be {entry.DecSize}"); } entry.Contents = result; entry.CmpFlag = 0x01; entry.CmpSize = entry.Contents.Length; } }
public WRD(byte[] bytes, string spcName, string wrdName) { BinaryReader reader = new BinaryReader(new MemoryStream(bytes), Encoding.UTF8); ushort stringCount = reader.ReadUInt16(); ushort labelCount = reader.ReadUInt16(); ushort paramCount = reader.ReadUInt16(); ushort sublabelCount = reader.ReadUInt16(); reader.BaseStream.Seek(4, SeekOrigin.Current); uint sublabelOffsetsPointer = reader.ReadUInt32(); uint labelOffsetsPointer = reader.ReadUInt32(); uint labelNamesPointer = reader.ReadUInt32(); uint paramsPointer = reader.ReadUInt32(); uint stringsPointer = reader.ReadUInt32(); Code = new List <WRDCmd>(); // We need at least 2 bytes for each command while (reader.BaseStream.Position + 1 < sublabelOffsetsPointer) { byte b = reader.ReadByte(); if (b != 0x70) { throw new InvalidDataException(string.Format("Error parsing WRD file: Expected opcode header byte 0x70, but got {0}", b)); } WRDCmd cmd = new WRDCmd(); cmd.Opcode = reader.ReadByte(); // Read command arguments, if any List <ushort> argList = new List <ushort>(); while (reader.BaseStream.Position + 1 < sublabelOffsetsPointer) { byte[] arg = reader.ReadBytes(2); if (arg[0] == 0x70) { reader.BaseStream.Seek(-2, SeekOrigin.Current); break; } argList.Add(BitConverter.ToUInt16(arg.Reverse().ToArray(), 0)); } cmd.ArgData = argList.ToArray(); Code.Add(cmd); } Labels = new List <string>(); reader.BaseStream.Seek(labelNamesPointer, SeekOrigin.Begin); for (ushort l = 0; l < labelCount; l++) { Labels.Add(reader.ReadString()); reader.ReadByte(); // Skip null terminator } Params = new List <string>(); reader.BaseStream.Seek(paramsPointer, SeekOrigin.Begin); for (ushort p = 0; p < paramCount; p++) { Params.Add(reader.ReadString()); reader.ReadByte(); // Skip null terminator } externalStrings = (stringsPointer == 0); // Read dialogue text strings Strings = new List <string>(); if (stringCount > 0) { // If we already know that there are no strings, // there's no need to go through the work to find them. if (externalStrings) { // Strings are stored in the "(current spc name)_text_(region).spc" file, // within an STX file with the same name as the current WRD file. string textSPCName = textSPCName = spcName.Insert(spcName.LastIndexOf('.'), string.Format("_text_{0}", MainForm.RegionString)); if (!File.Exists(textSPCName)) { // If the first filename fails, we probably need to remove a duplicate // region tag from the filename before "_text_". textSPCName = textSPCName.Remove(textSPCName.LastIndexOf("_text_") - 3, 3); if (!File.Exists(textSPCName)) { // If the file still doesn't exist, it's probably not // there and the strings should just be abandoned. System.Windows.Forms.MessageBox.Show($"{spcName} does not have an associated .stx text file.", "Missing .stx file", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning, System.Windows.Forms.MessageBoxDefaultButton.Button1); return; } } string stxName = wrdName.Replace(".wrd", ".stx"); byte[] spcData = File.ReadAllBytes(textSPCName); SPC textSPC = new SPC(spcData, textSPCName); Strings = new STX(textSPC.Entries[stxName].Contents).Strings; } else { reader.BaseStream.Seek(stringsPointer, SeekOrigin.Begin); for (ushort i = 0; i < stringCount; ++i) { short stringLen = 0; // The string length is a signed byte, so if it's larger than 0x7F, // that means the length is actually stored in a signed short, // since we can't have a negative string length. // ┐(´∀`)┌ if ((byte)reader.PeekChar() >= 0x80) { stringLen = reader.ReadInt16(); } else { stringLen = reader.ReadByte(); } stringLen += 2; // Null terminator List <char> stringData = new List <char>(stringLen / 2); for (int j = 0; j < (stringLen / 2); ++j) { char c = Convert.ToChar(reader.ReadUInt16()); stringData.Add(c); // We can't always trust stringLen apparently, so break if we've hit a null terminator. if (c == 0) { break; } } string str = new string(stringData.ToArray()); str = str.Replace("\r", "\\r"); str = str.Replace("\n", "\\n"); Strings.Add(str); } } } }