public bool InitializePartition(string partition) { Files.Clear(); LSEntry resource = null; if ((resource = LS.TryGetValue(partition)) == null) { return(false); } File.WriteAllBytes(partition, GetFile(resource.DTOffset, resource.Size, resource.DTIndex)); RF = new RFFile(partition); if (partition.Contains("(")) { RF.Locale = partition.Substring(partition.IndexOf("(")); } RegionCode = RF.RegionCode; LSEntry lsentry = null; IndexedPath path = new IndexedPath($"data{RF.Locale}/"); foreach (ResourceEntry rsobj in RF.ResourceEntries) { if (rsobj == null) { continue; } path[rsobj.FolderDepth] = rsobj.EntryString; if (rsobj.Packed) { lsentry = LS.TryGetValue(path.ToString() + "packed"); } if (lsentry != null) { var tpl = new Tuple <LSEntry, ResourceEntry>(lsentry, rsobj); if (rsobj.Packed) { Files.Add(path.ToString() + "packed", tpl); } else { Files.Add(path.ToString(), tpl); } } } return(true); }
public bool InitializePartition(string partition) { LSEntry resource = null; if ((resource = LS.TryGetValue(partition)) == null) { return(false); } File.WriteAllBytes(partition, GetFile(resource.DTOffset, resource.Size, resource.DTIndex)); var rf = new RFFile(partition); if (partition.Contains("(")) { rf.Locale = partition.Substring(partition.IndexOf("(")); } return(InitializePartition(rf, partition)); }
public bool InitializePartition(RFFile rf, string partition) { Files.Clear(); RegionCode = rf.RegionCode; LSEntry lsentry = null; IndexedPath path = new IndexedPath($"data{RF.Locale}/"); foreach (ResourceEntry rsobj in RF.ResourceEntries) { if (rsobj == null) { continue; } path[rsobj.FolderDepth] = rsobj.EntryString; if (rsobj.Packed) { lsentry = LS.TryGetValue(path.ToString() + "packed"); } if (lsentry != null) { var tpl = new Tuple <LSEntry, ResourceEntry>(lsentry, rsobj); if (rsobj.Packed) { Files.Add(path.ToString() + "packed", tpl); } else { Files.Add(path.ToString(), tpl); } } } return(true); }
public void BuildPartitions(string folder) { if (DTFiles == null) { return; } FileStream dtstrm = File.Create("dt_rebuild"); string tmpfile = Path.GetTempFileName(); FileStream packstrm = File.Create(tmpfile); foreach (string str in Directory.EnumerateDirectories(folder)) { if (!str.Contains("data")) { return; } string partition = "resource"; if (str.Contains("(")) { partition += str.Substring(str.IndexOf("(")); } var rf = new RFFile(); LSEntry curPacked = null; int pad = 0; string packKey = ""; foreach (string key in Files.Keys) { Console.WriteLine(key); var tpl = Files[key]; if (tpl.Item2.Packed) { if (curPacked != null) { int aligned = (int)packstrm.Position.RoundUp(0x10) + 0x60; while (packstrm.Position < aligned) { packstrm.WriteByte(0xBB); } curPacked.Size = (int)packstrm.Length; LS.TrySetValue(packKey, curPacked); packstrm.WriteTo(dtstrm); packstrm.Close(); packstrm = File.Open(tmpfile, FileMode.Truncate); } curPacked = tpl.Item1; packKey = key; curPacked.DTOffset = (uint)dtstrm.Position; var data = File.ReadAllBytes($"{folder}/{key}"); for (pad = 0; pad < data.Length && data[pad] == 0xCC; pad++) { packstrm.WriteByte(0xCC); } } else if (!tpl.Item2.EntryString.EndsWith("/")) { var filedata = File.ReadAllBytes($"{folder}/{key}"); var cmp = Util.Compress(filedata); ResourceEntry rsobj = tpl.Item2; int rIndex = RF.ResourceEntries.IndexOf(rsobj); rsobj.OffInPack = (uint)packstrm.Position; rsobj.CmpSize = cmp.Length; rsobj.DecSize = filedata.Length; RF[rIndex] = rsobj; packstrm.Write(cmp, 0, cmp.Length); int aligned = (int)packstrm.Position.RoundUp(0x10) + 0x20; while (packstrm.Position % 0x10 > 0) { packstrm.WriteByte(0xCC); } } } if (curPacked != null) { curPacked.Size = (int)packstrm.Length; LS.TrySetValue(packKey, curPacked); packstrm.WriteTo(dtstrm); } RF.UpdateEntries(); byte[] full = RF.GetBytes(); var resource = LS.TryGetValue(partition); resource.DTOffset = (uint)dtstrm.Position; dtstrm.Write(full, 0, full.Length); resource.Size = full.Length; LS.TrySetValue(partition, resource); LS.UpdateEntries(); dtstrm.Close(); packstrm.Close(); } }
private static void Unpack_update(string resFile) { Console.WriteLine("Parsing resource file.."); RFFile rfFile = new RFFile(resFile); var pathParts = new string[20]; DataSource _curPacked = new DataSource(); string mainfolder = ""; string region = ""; if (resFile.Contains("(")) region = resFile.Substring(resFile.IndexOf("(", StringComparison.Ordinal), 7); 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 = $"data{region}/{string.Join("", pathParts)}"; if (rsobj.HasPack) { path += (rsobj.Compressed ? "packed" : ""); if (File.Exists(path)) { _curPacked = new DataSource(FileMap.FromFile(path)); mainfolder = path.Remove(path.Length - 6); } continue; } if (!(rsobj.inPatch && path.Contains(mainfolder) && !string.IsNullOrEmpty(mainfolder))) continue; if (path.EndsWith("/")) { if (!Directory.Exists(path)) Directory.CreateDirectory(path); } else { var fileData = new byte[0]; if (rsobj.CmpSize > 0) { byte[] tmp = _curPacked.Slice((int)rsobj.OffInPack, 4); if (tmp[0] == 0x78 && tmp[1] == 0x9c) fileData = Util.DeCompress(_curPacked.Slice((int)rsobj.OffInPack, (int)rsobj.CmpSize)); else fileData = _curPacked.Slice((int)rsobj.OffInPack, (int)rsobj.DecSize); } Console.WriteLine(path); Logstream.WriteLine($"{path} : size: {rsobj.DecSize:X8}"); if (fileData.Length != rsobj.DecSize) { Console.WriteLine("Error: File length doesn't match specified decompressed length, quiting"); Logstream.WriteLine("Error: File length doesn't match specified decompressed length, quiting"); return; } File.WriteAllBytes(path, fileData); } } Logstream.Close(); Console.WriteLine("Extraction finished."); if (File.Exists($"resource{region}.dec")) File.Delete($"resource{region}.dec"); }
/// <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"); }