public void UpdateEntries() { VoidPtr addr = _workingSource.Address; addr += 0x08; for (int i = 0; i < _entryCount; i++) { LSEntryObject lsobj = Entries.Values[i]; if (Version == 1) { LSEntry_v1 *entry = (LSEntry_v1 *)(addr + (i * 0x0C)); * entry = new LSEntry_v1() { _crc = lsobj.FileNameCRC, _start = lsobj.DTOffset, _size = lsobj.Size }; LSEntry_v1 *entry2 = (LSEntry_v1 *)(addr + (i * 0x0C)); } else if (Version == 2) { LSEntry_v2 *entry = (LSEntry_v2 *)(addr + (i * 0x10)); * entry = new LSEntry_v2() { _crc = lsobj.FileNameCRC, _start = lsobj.DTOffset, _size = lsobj.Size, _dtIndex = lsobj.DTIndex, _padlen = lsobj.PaddingLength }; } } }
public void ConvertToV2() { int size = 0x08 + Entries.Count * 0x10; string path = _workingSource.Map.FilePath; _workingSource.Close(); _workingSource = new DataSource(FileMap.FromTempFile(size)); VoidPtr addr = _workingSource.Address; *(uint *)addr = 0x0002666f; *(int *)(addr + 4) = Entries.Count; addr += 0x08; for (int i = 0; i < Entries.Count; i++) { LSEntryObject lsobj = Entries.Values[i]; LSEntry_v2 * entry = (LSEntry_v2 *)(addr + (i * 0x10)); *entry = new LSEntry_v2() { _crc = lsobj.FileNameCRC, _start = lsobj.DTOffset, _size = lsobj.Size, _dtIndex = lsobj.DTIndex, _padlen = lsobj.PaddingLength }; } _workingSource.Export(path); }
public void Parse(string path) { _workingSource = new DataSource(FileMap.FromFile(path)); short tag = *(short*)_workingSource.Address; if (tag != 0x666f) return; _version = *(short*)(_workingSource.Address + 0x02); _entryCount = *(int*)(_workingSource.Address + 0x04); Entries = new SortedList<uint, LSEntryObject>(_entryCount); for (int i = 0; i < _entryCount; i++) { LSEntryObject lsobj = new LSEntryObject(); if (Version == 1) { LSEntry_v1 entry = *(LSEntry_v1*)(_workingSource.Address + 0x08 + (i * 0x0C)); lsobj.FileNameCRC = entry._crc; lsobj.DTOffset = entry._start; lsobj.Size = entry._size; } else if (Version == 2) { LSEntry_v2 entry = *(LSEntry_v2*)(_workingSource.Address + 0x08 + (i * 0x10)); lsobj.FileNameCRC = entry._crc; lsobj.DTOffset = entry._start; lsobj.Size = entry._size; lsobj.DTIndex = entry._dtIndex; lsobj.PaddingLength = entry._padlen; } Entries.Add(lsobj.FileNameCRC, lsobj); } }
public void Parse(string path) { _workingSource = new DataSource(FileMap.FromFile(path)); short tag = *(short *)_workingSource.Address; if (tag != 0x666f) { return; } _version = *(short *)(_workingSource.Address + 0x02); _entryCount = *(int *)(_workingSource.Address + 0x04); Entries = new SortedList <uint, LSEntryObject>(_entryCount); for (int i = 0; i < _entryCount; i++) { LSEntryObject lsobj = new LSEntryObject(); if (Version == 1) { LSEntry_v1 entry = *(LSEntry_v1 *)(_workingSource.Address + 0x08 + (i * 0x0C)); lsobj.FileNameCRC = entry._crc; lsobj.DTOffset = entry._start; lsobj.Size = entry._size; } else if (Version == 2) { LSEntry_v2 entry = *(LSEntry_v2 *)(_workingSource.Address + 0x08 + (i * 0x10)); lsobj.FileNameCRC = entry._crc; lsobj.DTOffset = entry._start; lsobj.Size = entry._size; lsobj.DTIndex = entry._dtIndex; lsobj.PaddingLength = entry._padlen; } Entries.Add(lsobj.FileNameCRC, lsobj); } }
/// <summary> /// Unpacks data from the game archive using the default resource file. /// </summary> /// <param name="resourceStr">The resource file (embedded or otherwise) to use in extraction</param> private static void Unpack_default(string resourceStr) { string region = ""; if (resourceStr.Contains("(")) region = resourceStr.Substring(resourceStr.IndexOf("(", StringComparison.Ordinal), 7); LSEntryObject _resource = lsFile.Entries[Util.calc_crc(resourceStr)]; File.WriteAllBytes(resourceStr, GetFileDataDecompressed(_resource.DTOffset + (uint)_resource.PaddingLength, _resource.Size, _resource.DTIndex)); Console.WriteLine($"Parsing {resourceStr} file.."); RFFile rfFile = new RFFile(resourceStr); var pathParts = new string[20]; var offsetParts = new LSEntryObject[20]; foreach (ResourceEntryObject rsobj in rfFile.ResourceEntries) { if (rsobj == null) continue; pathParts[rsobj.FolderDepth - 1] = rsobj.EntryString; Array.Clear(pathParts, rsobj.FolderDepth, pathParts.Length - (rsobj.FolderDepth + 1)); var path = string.Join("", pathParts); LSEntryObject fileEntry; if (rsobj.HasPack) { var crcPath = $"data/{path.TrimEnd('/') + (rsobj.Compressed ? "/packed" : "")}"; var crc = Util.calc_crc(crcPath); lsFile.Entries.TryGetValue(crc, out fileEntry); } else fileEntry = null; offsetParts[rsobj.FolderDepth - 1] = fileEntry; Array.Clear(offsetParts, rsobj.FolderDepth, offsetParts.Length - (rsobj.FolderDepth + 1)); var outfn = $"data{region}/{path}"; if (path.EndsWith("/")) { if (!Directory.Exists(outfn)) Directory.CreateDirectory(outfn); } else { LSEntryObject lsentry = offsetParts.Last(x => x != null); var fileData = new byte[0]; if (rsobj.CmpSize > 0) fileData = GetFileDataDecompressed(lsentry.DTOffset + rsobj.OffInPack, (uint)rsobj.CmpSize, lsentry.DTIndex); Console.WriteLine(outfn); Logstream.WriteLine($"{outfn} : size: {rsobj.DecSize:X8}"); if (fileData.Length != rsobj.DecSize) { Console.WriteLine("Error: File length doesn't match specified decompressed length, skipping"); Logstream.WriteLine("Error: File length doesn't match specified decompressed length, skipping"); } File.WriteAllBytes(outfn, fileData); } } // clean up Logstream.Close(); Console.WriteLine("Extraction finished"); if (File.Exists("resource.dec")) File.Delete("resource.dec"); if (File.Exists("resource")) File.Delete("resource"); }
private static unsafe void PatchArchive(string resourceString, string patchFolder) { LSEntryObject _resource = lsFile.Entries[Util.calc_crc(resourceString)]; byte[] resource = GetFileDataDecompressed(_resource.DTOffset + (uint)_resource.PaddingLength, _resource.Size, _resource.DTIndex); File.WriteAllBytes(resourceString, resource); Console.WriteLine($"Patching {resourceString}"); RFFile rfFile = new RFFile(resourceString); var pathParts = new string[20]; var offsetParts = new LSEntryObject[20]; foreach (ResourceEntryObject rsobj in rfFile.ResourceEntries) { if (rsobj == null) continue; pathParts[rsobj.FolderDepth - 1] = rsobj.EntryString; Array.Clear(pathParts, rsobj.FolderDepth, pathParts.Length - (rsobj.FolderDepth + 1)); var path = string.Join("", pathParts); LSEntryObject fileEntry; if (rsobj.HasPack) { var crcPath = $"data/{path.TrimEnd('/') + (rsobj.Compressed ? "/packed" : "")}"; var crc = Util.calc_crc(crcPath); lsFile.Entries.TryGetValue(crc, out fileEntry); } else fileEntry = null; offsetParts[rsobj.FolderDepth - 1] = fileEntry; Array.Clear(offsetParts, rsobj.FolderDepth, offsetParts.Length - (rsobj.FolderDepth + 1)); if (!path.EndsWith("/")) if (File.Exists($"{patchFolder}/{path}")) { Console.WriteLine($"Patch found: {patchFolder}/{path}"); Logstream.WriteLine($"Patch found: {patchFolder}/{path}"); LSEntryObject lsentry = offsetParts.Last(x => x != null); byte[] raw = File.ReadAllBytes($"{patchFolder}/{path}"); byte[] compressed = Util.Compress(raw); if (compressed.Length > rsobj.CmpSize + 0x10) { Console.WriteLine("Patching files larger than original not yet supported, skipping"); continue; } rsobj.CmpSize = (uint)compressed.Length; rsobj.DecSize = (uint)raw.Length; uint difference = 0; DataSource src = GetFileChunk(lsentry.DTOffset, lsentry.Size, lsentry.DTIndex, out difference); VoidPtr addr = src.Address + difference; addr += rsobj.OffInPack; for (int i = 0; i < compressed.Length; i++) *(byte*)(addr + i) = compressed[i]; // write 0xCC over unused bytes. addr += compressed.Length; int truncateBytes = (int)rsobj.CmpSize - compressed.Length; for (int i = 0; i < truncateBytes; i++) *(byte*)(addr + i) = 0xCC; src.Close(); } } // Update resource and LS files. rfFile.UpdateEntries(); byte[] dec = rfFile._workingSource.Slice((int)rfFile.Header.HeaderLen1, (int)(rfFile._workingSource.Length - rfFile.Header.HeaderLen1)); byte[] cmp = Util.Compress(dec); rfFile.Header.CompressedLen = (uint)cmp.Length; rfFile.Header.DecompressedLen = (uint)dec.Length; byte[] header = rfFile.Header.ToArray(); byte[] full = header.Concat(cmp).ToArray(); lsFile.Entries[Util.calc_crc(resourceString)].Size = (uint)full.Length; lsFile.UpdateEntries(); // Patch the resource data back into the DT file. uint diff; DataSource rSource = GetFileChunk(_resource.DTOffset, _resource.Size, _resource.DTIndex, out diff); VoidPtr curAddr = rSource.Address + diff; for (int i = 0; i < full.Length; i++) { *(byte*)(curAddr + i) = full[i]; } rSource.Close(); rfFile._workingSource.Close(); if (File.Exists(resourceString)) File.Delete(resourceString); if (File.Exists(resourceString + ".dec")) File.Delete(resourceString + ".dec"); }