static Dictionary <uint, Dictionary <uint, SqFile> > readIndex(string indexPath) { Dictionary <uint, Dictionary <uint, SqFile> > sqFiles = new Dictionary <uint, Dictionary <uint, SqFile> >(); using (FileStream fs = File.OpenRead(indexPath)) using (BinaryReader br = new BinaryReader(fs)) { br.BaseStream.Position = 0xc; int headerOffset = br.ReadInt32(); br.BaseStream.Position = headerOffset + 0x8; int fileOffset = br.ReadInt32(); int fileCount = br.ReadInt32() / 0x10; br.BaseStream.Position = fileOffset; for (int i = 0; i < fileCount; i++) { SqFile sqFile = new SqFile(); sqFile.Key = br.ReadUInt32(); sqFile.DirectoryKey = br.ReadUInt32(); sqFile.WrappedOffset = br.ReadInt32(); br.ReadInt32(); if (!sqFiles.ContainsKey(sqFile.DirectoryKey)) { sqFiles.Add(sqFile.DirectoryKey, new Dictionary <uint, SqFile>()); } sqFiles[sqFile.DirectoryKey].Add(sqFile.Key, sqFile); } } return(sqFiles); }
// Decode ExD from the raw data and create new ExD instance. public ExDFile(SqFile sqFile, ExHFile exHFile) { Header = exHFile; // Read the raw data from SqFile. byte[] data = sqFile.ReadData(); // Quit if no data is available. if (data == null || data.Length == 0) { return; } // Retrieve offset entries and total row sizes from the data header. int offsetEntriesSize = ToInt32(data, 0x8, true); int totalRowSize = ToInt32(data, 0xc, true); // Copy offset entries from raw data for easier manipulation. // Offset entries start from 0x20. byte[] offsetEntries = new byte[offsetEntriesSize]; Array.Copy(data, 0x20, offsetEntries, 0, offsetEntriesSize); // Copy all rows from raw data for easier manipulation. // Rows start right after offset entries. byte[] rawRows = new byte[totalRowSize]; Array.Copy(data, 0x20 + offsetEntriesSize, rawRows, 0, totalRowSize); // Going through each offset entry. // Offset entry size is 0x8. for (int i = 0; i < offsetEntriesSize; i += 0x8) { ExDRow row = new ExDRow(); row.Parent = this; // First 32 bits of the offset entry is the key for the row that we're reading. row.Key = ToInt32(offsetEntries, i, true); // Next 32 bits are the actual offset. row.Offset = ToInt32(offsetEntries, i + 0x4, true); // The offset includes the offset entries and 0x20 bytes for the raw header. // Actual position in the raw row byte array would be offset - raw header size - offset entries size. int rawRowPosition = row.Offset - 0x20 - offsetEntriesSize; // First 4 bytes in the raw row data is the size of the raw data. row.Size = ToInt32(rawRows, rawRowPosition, true); // Next 2 bytes are the check digit. row.CheckDigit = ToInt16(rawRows, rawRowPosition + 0x4, true); // After the size and the check digit, the raw data is actually divided into two sections. // First section is the fixed size data. // Copy it over to separate array for easier manipulation. byte[] fixedSizeData = new byte[exHFile.FixedSizeDataLength]; Array.Copy(rawRows, rawRowPosition + 0x6, fixedSizeData, 0, exHFile.FixedSizeDataLength); // Finally, rest of the raw row is the second section which contains the additional data that are used for some columns with varying lengths. (i.e. string type) byte[] additionalData = new byte[row.Size - exHFile.FixedSizeDataLength]; Array.Copy(rawRows, rawRowPosition + 0x6 + exHFile.FixedSizeDataLength, additionalData, 0, additionalData.Length); // Let's go through each column of this row now. foreach (ExHColumn column in exHFile.Columns) { ExDData parsedData = new ExDData(); switch (column.Type) { // String type. case 0x0: // The first 4 bytes of the fixed size data contain the starting offset for the additional data section. int valueStart = ToInt32(fixedSizeData, column.Offset, true); // The additional data is 0-terminated. int valueEnd = valueStart; while (valueEnd < additionalData.Length && additionalData[valueEnd] != 0) { valueEnd++; } // Copy the value from additional data to new byte array for decoding. byte[] field = new byte[valueEnd - valueStart]; Array.Copy(additionalData, valueStart, field, 0, field.Length); // Decode it as string. parsedData.Type = typeof(string); parsedData.Value = DecodeString(field); break; // Bool type. case 0x1: // The first byte of the fixed size data is the boolean value. parsedData.Type = typeof(bool); parsedData.Value = fixedSizeData[column.Offset] != 0; break; // Signed byte type. case 0x2: // The first byte of the fixed size data is the value. parsedData.Type = typeof(sbyte); parsedData.Value = (sbyte)fixedSizeData[column.Offset]; break; // Byte type. case 0x3: // The first byte of the fixed size data is the value. parsedData.Type = typeof(byte); parsedData.Value = fixedSizeData[column.Offset]; break; // Short type. case 0x4: // The first 2 bytes of the fixed size data are the value. parsedData.Type = typeof(short); parsedData.Value = ToInt16(fixedSizeData, column.Offset, true); break; // Ushort type. case 0x5: // The first 2 bytes of the fixed size data are the value. parsedData.Type = typeof(ushort); parsedData.Value = ToUInt16(fixedSizeData, column.Offset, true); break; // Int type. case 0x6: // The first 4 bytes of the fixed size data are the value. parsedData.Type = typeof(int); parsedData.Value = ToInt32(fixedSizeData, column.Offset, true); break; // Uint type. case 0x7: // The first 4 bytes of the fixed size data are the value. parsedData.Type = typeof(uint); parsedData.Value = ToUInt32(fixedSizeData, column.Offset, true); break; // Float type. case 0x9: // The first 4 bytes of the fixed size data are the value. parsedData.Type = typeof(float); parsedData.Value = ToSingle(fixedSizeData, column.Offset, true); break; // Long type. case 0xb: // The first 8 bytes of the fixed size data are the value. parsedData.Type = typeof(long); parsedData.Value = ToInt64(fixedSizeData, column.Offset, true); break; // Masked boolean types. // The first byte of the fixed size data contains the boolean value, where the bit offset is type value - 0x19 from right. case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x20: parsedData.Type = typeof(bool); parsedData.Value = (fixedSizeData[column.Offset] & (0x1 << (column.Type - 0x19))) != 0; break; // Unrecognized type. default: throw new Exception($"Unrecognized column type: 0x{column.Type.ToString("x")}"); } row.Data.Add(column, parsedData); } Rows.Add(row); } }
// Decode ExH from the raw data and create new ExH instance. public ExHFile(SqFile sqFile) { // Read the SqFile raw data. byte[] data = sqFile.ReadData(); // Quit if no data is available. if (data == null || data.Length == 0) { return; } // Some big endian values from fixed offset. FixedSizeDataLength = ToUInt16(data, 0x6, true); Variant = ToUInt16(data, 0x10, true); // Counts for various entries. ushort columnCount = ToUInt16(data, 0x8, true); ushort rangeCount = ToUInt16(data, 0xa, true); ushort langCount = ToUInt16(data, 0xc, true); // Only care about ExD data table type headers. if (Variant != 1) { return; } Columns = new ExHColumn[columnCount]; for (int i = 0; i < columnCount; i++) { // Column offsets start after the fixed offset values (0x20). // Each column offset is 4 bytes long. int columnOffset = 0x20 + i * 0x4; Columns[i] = new ExHColumn() { Type = ToUInt16(data, columnOffset, true), Offset = ToUInt16(data, columnOffset + 0x2, true) }; } Ranges = new ExHRange[rangeCount]; for (int i = 0; i < rangeCount; i++) { // Range offsets start after the column offsets. // Each range offset is 8 bytes long. int rangeOffset = (0x20 + columnCount * 0x4) + i * 0x8; Ranges[i] = new ExHRange() { Start = ToInt32(data, rangeOffset, true), Length = ToInt32(data, rangeOffset + 0x4, true) }; } Languages = new ExHLanguage[langCount]; for (int i = 0; i < langCount; i++) { // Language offsets start after the range offsets. // Each language offset is 2 bytes long. int langOffset = ((0x20 + columnCount * 0x4) + rangeCount * 0x8) + i * 0x2; Languages[i] = new ExHLanguage() { Value = data[langOffset] }; } }
static void Main(string[] args) { string baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string globalIndexPath = Path.Combine(baseDir, "global", "000000.win32.index"); string globalDatPath = Path.Combine(baseDir, "global", "000000.win32.dat0"); string koIndexPath = Path.Combine(baseDir, "korean", "000000.win32.index"); string koDatPath = Path.Combine(baseDir, "korean", "000000.win32.dat0"); string outputDir = Path.Combine(baseDir, "output"); if (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } Dictionary <uint, Dictionary <uint, SqFile> > globalSqFiles = readIndex(globalIndexPath); Dictionary <uint, Dictionary <uint, SqFile> > koSqFiles = readIndex(koIndexPath); string outputIndexPath = Path.Combine(outputDir, Path.GetFileName(globalIndexPath)); File.Copy(globalIndexPath, outputIndexPath, true); //File.Copy(koIndexPath, outputIndexPath, true); string outputDatPath = Path.Combine(outputDir, Path.GetFileName(globalDatPath)); File.Copy(globalDatPath, outputDatPath, true); string outputNewDatPath = Path.Combine(outputDir, "000000.win32.dat1"); File.Copy(koDatPath, outputNewDatPath, true); byte[] index = File.ReadAllBytes(outputIndexPath); /*Console.WriteLine(koSqFiles[Hash.Compute("common/font")].ContainsKey(Hash.Compute("KrnAxis_96.fdt"))); * Console.ReadLine();*/ /* * using (StreamWriter sw = new StreamWriter(Path.Combine(outputDir, "global_keys"), false)) * { * foreach (uint globalKey in globalSqFiles[Hash.Compute("common/font")].Keys) * { * sw.WriteLine(globalKey.ToString()); * } * } * * using (StreamWriter sw = new StreamWriter(Path.Combine(outputDir, "korean_keys"), false)) * { * foreach (uint koreanKey in koSqFiles[Hash.Compute("common/font")].Keys) * { * sw.WriteLine(koreanKey.ToString()); * } * } */ List <Exchange> exchanges = new List <Exchange>(); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("font1.tex"), GlobalNewKey = Hash.Compute("font1.tex"), KoreanKey = Hash.Compute("font_krn_1.tex") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("font2.tex"), GlobalNewKey = Hash.Compute("font2.tex"), KoreanKey = Hash.Compute("font_krn_2.tex") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("font3.tex"), GlobalNewKey = Hash.Compute("font3.tex"), KoreanKey = Hash.Compute("font_krn_3.tex") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("font_lobby1.tex"), GlobalNewKey = Hash.Compute("font_lobby1.tex"), KoreanKey = Hash.Compute("font_krn_1.tex") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("font_lobby2.tex"), GlobalNewKey = Hash.Compute("font_lobby2.tex"), KoreanKey = Hash.Compute("font_krn_2.tex") }); /*exchanges.Add(new Exchange() * { * GlobalKey = Hash.Compute("font_krn_3.tex"), * GlobalNewKey = Hash.Compute("font_lobby3.tex"), * KoreanKey = Hash.Compute("font_krn_3.tex") * });*/ exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_12.fdt"), GlobalNewKey = Hash.Compute("axis_12.fdt"), KoreanKey = Hash.Compute("KrnAxis_120.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_14.fdt"), GlobalNewKey = Hash.Compute("axis_14.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_18.fdt"), GlobalNewKey = Hash.Compute("axis_18.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_36.fdt"), GlobalNewKey = Hash.Compute("axis_36.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_96.fdt"), GlobalNewKey = Hash.Compute("axis_96.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_10.fdt"), GlobalNewKey = Hash.Compute("miedingermid_10.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_12.fdt"), GlobalNewKey = Hash.Compute("miedingermid_12.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_14.fdt"), GlobalNewKey = Hash.Compute("miedingermid_14.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_18.fdt"), GlobalNewKey = Hash.Compute("miedingermid_18.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("meidinger_16.fdt"), GlobalNewKey = Hash.Compute("meidinger_16.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("meidinger_20.fdt"), GlobalNewKey = Hash.Compute("meidinger_20.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("trumpgothic_23.fdt"), GlobalNewKey = Hash.Compute("trumpgothic_23.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("trumpgothic_34.fdt"), GlobalNewKey = Hash.Compute("trumpgothic_34.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("trumpgothic_184.fdt"), GlobalNewKey = Hash.Compute("trumpgothic_184.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_16.fdt"), GlobalNewKey = Hash.Compute("jupiter_16.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_20.fdt"), GlobalNewKey = Hash.Compute("jupiter_20.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_23.fdt"), GlobalNewKey = Hash.Compute("jupiter_23.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_45.fdt"), GlobalNewKey = Hash.Compute("jupiter_45.fdt"), KoreanKey = Hash.Compute("KrnAxis_180.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_12_lobby.fdt"), GlobalNewKey = Hash.Compute("axis_12_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_120.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_14_lobby.fdt"), GlobalNewKey = Hash.Compute("axis_14_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("axis_18_lobby.fdt"), GlobalNewKey = Hash.Compute("axis_18_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_10_lobby.fdt"), GlobalNewKey = Hash.Compute("miedingermid_10_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_12_lobby.fdt"), GlobalNewKey = Hash.Compute("miedingermid_12_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_14_lobby.fdt"), GlobalNewKey = Hash.Compute("miedingermid_14_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("miedingermid_18_lobby.fdt"), GlobalNewKey = Hash.Compute("miedingermid_18_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("meidinger_16_lobby.fdt"), GlobalNewKey = Hash.Compute("meidinger_16_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("meidinger_20_lobby.fdt"), GlobalNewKey = Hash.Compute("meidinger_20_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("trumpgothic_23_lobby.fdt"), GlobalNewKey = Hash.Compute("trumpgothic_23_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("trumpgothic_34_lobby.fdt"), GlobalNewKey = Hash.Compute("trumpgothic_34_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("trumpgothic_184_lobby.fdt"), GlobalNewKey = Hash.Compute("trumpgothic_184_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_16_lobby.fdt"), GlobalNewKey = Hash.Compute("jupiter_16_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_20_lobby.fdt"), GlobalNewKey = Hash.Compute("jupiter_20_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_23_lobby.fdt"), GlobalNewKey = Hash.Compute("jupiter_23_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); exchanges.Add(new Exchange() { GlobalKey = Hash.Compute("jupiter_45_lobby.fdt"), GlobalNewKey = Hash.Compute("jupiter_45_lobby.fdt"), KoreanKey = Hash.Compute("KrnAxis_140.fdt") }); foreach (Exchange exchange in exchanges) { SqFile globalSqFile = globalSqFiles[Hash.Compute("common/font")][exchange.GlobalKey]; SqFile koSqFile = koSqFiles[Hash.Compute("common/font")][exchange.KoreanKey]; koSqFile.DatFile = 1; int headerOffset = BitConverter.ToInt32(index, 0xc); index[headerOffset + 0x50] = 2; int fileOffset = BitConverter.ToInt32(index, headerOffset + 0x8); int fileCount = BitConverter.ToInt32(index, headerOffset + 0xc) / 0x10; for (int i = 0; i < fileCount; i++) { int keyOffset = fileOffset + i * 0x10; uint key = BitConverter.ToUInt32(index, keyOffset); uint directoryKey = BitConverter.ToUInt32(index, keyOffset + 0x4); if (key == globalSqFile.Key && directoryKey == globalSqFile.DirectoryKey) { Array.Copy(BitConverter.GetBytes(exchange.GlobalNewKey), 0, index, keyOffset, 0x4); Array.Copy(BitConverter.GetBytes(koSqFile.WrappedOffset), 0, index, keyOffset + 0x8, 0x4); } } } /* * List<KeyValuePair<string, string>> exchange = new List<KeyValuePair<string, string>>(); * exchange.Add(new KeyValuePair<string, string>("font_krn_1.tex", "font1.tex")); * exchange.Add(new KeyValuePair<string, string>("font_krn_2.tex", "font2.tex")); * exchange.Add(new KeyValuePair<string, string>("font_krn_3.tex", "font3.tex")); * exchange.Add(new KeyValuePair<string, string>("font_krn_1.tex", "font_lobby1.tex")); * exchange.Add(new KeyValuePair<string, string>("KrnAxis_120.fdt", "axis_12.fdt")); * exchange.Add(new KeyValuePair<string, string>("KrnAxis_120.fdt", "axis_12_lobby.fdt")); * exchange.Add(new KeyValuePair<string, string>("KrnAxis_140.fdt", "axis_14.fdt")); * exchange.Add(new KeyValuePair<string, string>("KrnAxis_140.fdt", "axis_14_lobby.fdt")); * exchange.Add(new KeyValuePair<string, string>("KrnAxis_180.fdt", "axis_18.fdt")); * exchange.Add(new KeyValuePair<string, string>("KrnAxis_180.fdt", "axis_18_lobby.fdt")); * * foreach (KeyValuePair<string, string> pair in exchange) * { * string koKey = pair.Key; * string globalKey = pair.Value; * * SqFile globalSqFile = globalSqFiles[Hash.Compute("common/font")][Hash.Compute(globalKey)]; * SqFile koSqFile = koSqFiles[Hash.Compute("common/font")][Hash.Compute(koKey)]; * koSqFile.DatFile = 1; * * int headerOffset = BitConverter.ToInt32(index, 0xc); * index[headerOffset + 0x50] = 2; * int fileOffset = BitConverter.ToInt32(index, headerOffset + 0x8); * int fileCount = BitConverter.ToInt32(index, headerOffset + 0xc) / 0x10; * for (int i = 0; i < fileCount; i++) * { * int keyOffset = fileOffset + i * 0x10; * uint key = BitConverter.ToUInt32(index, keyOffset); * uint directoryKey = BitConverter.ToUInt32(index, keyOffset + 0x4); * * if (key == globalSqFile.Key && directoryKey == globalSqFile.DirectoryKey) * { * Array.Copy(BitConverter.GetBytes(koSqFile.WrappedOffset), 0, index, keyOffset + 0x8, 0x4); * } * } * }*/ File.WriteAllBytes(outputIndexPath, index); }