public override void Construct(BinaryEndianReader br, QbItemType type) { //System.Diagnostics.Debug.WriteLine(string.Format("{0} - 0x{1}", type.ToString(), (base.StreamPos(br) - 4).ToString("X").PadLeft(8, '0'))); base.Construct(br, type); _unknown = br.ReadUInt32(base.Root.PakFormat.EndianType); uint decompressedSize = br.ReadUInt32(base.Root.PakFormat.EndianType); uint compressedSize = br.ReadUInt32(base.Root.PakFormat.EndianType); // Get script data Lzss lz = new Lzss(); _scriptData = br.ReadBytes((int)compressedSize); if (compressedSize < decompressedSize) { _scriptData = lz.Decompress(_scriptData); } if (_scriptData.Length != decompressedSize) { throw new ApplicationException(string.Format("Location 0x{0}: Script decompressed to {1} bytes not {2}", (base.StreamPos(br) - compressedSize).ToString("X").PadLeft(8, '0'), _scriptData.Length.ToString(), decompressedSize.ToString())); } // Padding... if ((base.StreamPos(br) % 4) != 0) { br.BaseStream.Seek(4 - (base.StreamPos(br) % 4), SeekOrigin.Current); } base.ConstructEnd(br); }
/// <summary> /// Pass null for br if the data was passed as a byte array in the constructor /// </summary> /// <param name="br"></param> /// <param name="type"></param> public override void Construct(BinaryEndianReader br, QbItemType type) { //System.Diagnostics.Debug.WriteLine(string.Format("{0} - 0x????????", this.GetType().ToString())); if (br != null) { base.Construct(br, type); //base.Position = (uint)base.StreamPos(br); _unknownData = br.ReadBytes(_length); base.ConstructEnd(br); } }
private CompressionType xBoxUncompress(string compFilename, string uncompFilename) { if (uncompFilename.ToLower() == compFilename.ToLower()) //nothing to do { return(CompressionType.None); } if (File.Exists(uncompFilename)) { File.Delete(uncompFilename); } CompressionType ct = CompressionType.None; try { using (FileStream inFileStream = new FileStream(compFilename, FileMode.Open, FileAccess.Read)) { using (BinaryEndianReader br = new BinaryEndianReader(inFileStream)) { uint chunkLen = 0; while (chunkLen != 0xffffffff) { using (FileStream outFileStream = new FileStream(uncompFilename, chunkLen == 0 ? FileMode.Create : FileMode.Append)) { using (ZlibOutputStream outZStream = new ZlibOutputStream(outFileStream, Rebex.IO.Compression.CompressionMode.Decompress)) { //test for newer GHWT+ compression long pos = inFileStream.Position; if (Encoding.ASCII.GetString(br.ReadBytes(4)) == "CHNK") { ct = CompressionType.ZLibChunk; EndianType et = this.EndianType; //Decompress each section uint headerLen = br.ReadUInt32(et); uint blockLen = br.ReadUInt32(et); chunkLen = br.ReadUInt32(et); uint nextchunkLen = br.ReadUInt32(et); uint uncompressedLen = br.ReadUInt32(et); uint uncompressedPos = br.ReadUInt32(et); _zLibHeaderLen = headerLen; if (uncompressedLen > this._zLibChunkSize) { this._zLibChunkSize = uncompressedLen; } inFileStream.Seek(headerLen - (inFileStream.Position - pos), SeekOrigin.Current); outZStream.WriteByte(0x58); //Search deflate.cs for "Nanook" for the mod to stop it being written out outZStream.WriteByte(0x85); //this is the header MS uses, thie zlib deflate uses 78DA ?? copyStream(inFileStream, outZStream, (int)blockLen); if (chunkLen != 0xffffffff) { inFileStream.Seek((chunkLen - blockLen) - headerLen, SeekOrigin.Current); } } else { try { inFileStream.Seek(pos, SeekOrigin.Begin); outZStream.WriteByte(0x58); outZStream.WriteByte(0x85); copyStream(inFileStream, outZStream); ct = CompressionType.ZLib; break; } catch { break; //no compression??? } } outZStream.Flush(); outFileStream.Flush(); //outZStream.Close(); } } } } } } catch (Exception ex) { throw new ApplicationException(string.Format("Uncompress Failed: {0}", ex)); } //using (DeflateStream ds = new DeflateStream(File.Open(compFilename, FileMode.Open), CompressionMode.Decompress)) //{ // using (BinaryReader br = new BinaryReader(ds)) // { // using (FileStream ms = File.Create(uncompFilename)) // { // using (BinaryWriter bw = new BinaryWriter(ms)) // { // long l; // do // { // l = ms.Length; // bw.Write(br.ReadBytes(10000)); // } // while (l != ms.Length); // } // } // } //} return(ct); }
public override void Construct(BinaryEndianReader br, QbItemType type) { //System.Diagnostics.Debug.WriteLine(string.Format("{0} - 0x{1}", type.ToString(), (base.StreamPos(br) - 4).ToString("X").PadLeft(8, '0'))); _isUnicode = ((type == QbItemType.SectionStringW || type == QbItemType.ArrayStringW || type == QbItemType.StructItemStringW) && (base.Root.PakFormat.PakFormatType == PakFormatType.PC || base.Root.PakFormat.PakFormatType == PakFormatType.XBox)); byte[] bytes; base.Construct(br, type); this.Strings = new string[base.ItemCount]; _charWidth = !_isUnicode ? 1 : 2; if (base.ItemCount != 0) { //use pointers to read quickly if (base.ItemCount > 1) { for (int i = 0; i < base.ItemCount - 1; i++) { if (base.StreamPos(br) != base.Pointers[i]) //pointer test throw new ApplicationException(QbFile.FormatBadPointerExceptionMessage(this, base.StreamPos(br), base.Pointers[i])); bytes = br.ReadBytes((int)((base.Pointers[i + 1] - _charWidth) - base.StreamPos(br))); _strings[i] = bytesToString(bytes); //handles unicode if (!_isUnicode ? (br.ReadByte() != 0) : (br.ReadByte() != 0 || br.ReadByte() != 0)) throw new ApplicationException(string.Format("Null byte expected reading string array at 0x{0}", (base.StreamPos(br) - _charWidth).ToString("X").PadLeft(8, '0'))); } if (base.StreamPos(br) != base.Pointers[base.ItemCount - 1]) //pointer test throw new ApplicationException(QbFile.FormatBadPointerExceptionMessage(this, base.StreamPos(br), base.Pointers[base.ItemCount - 1])); } //use the slow method read the last string StringBuilder sb = new StringBuilder(); //if we have come from an array we must align our position to %4 int byteAmount = (int)(4 - (base.StreamPos(br) % 4)); do { bytes = br.ReadBytes(byteAmount); sb.Append(bytesToString(bytes)); byteAmount = 4; } while (!_isUnicode ? (bytes[bytes.Length - 1] != '\0') : (bytes[bytes.Length - 1] != '\0' || bytes[bytes.Length - 2] != '\0')); //get text and remove any trailing null bytes _strings[base.ItemCount - 1] = sb.ToString().TrimEnd(new char[] { '\0' }); } base.ConstructEnd(br); }
public static WavSingleChunkHeader ParseWavSingleChunkHeader(Stream wavStream) { WavSingleChunkHeader w = new WavSingleChunkHeader(); byte[] b; BinaryEndianReader r = new BinaryEndianReader(wavStream); b = r.ReadBytes(4); w.FileId = Encoding.Default.GetString(b); w.FileLength = r.ReadUInt32(EndianType.Little); b = r.ReadBytes(4); w.RiffType = Encoding.Default.GetString(b); b = r.ReadBytes(4); w.ChunkHeaderId = Encoding.Default.GetString(b); w.ChunkHeaderLength = r.ReadUInt32(EndianType.Little); long p = wavStream.Position; w.FormatTag = r.ReadUInt16(EndianType.Little); w.Channels = r.ReadUInt16(EndianType.Little); w.SamplesPerSec = r.ReadUInt32(EndianType.Little); w.AvgBytesPerSec = r.ReadUInt32(EndianType.Little); w.BlockAlign = r.ReadUInt16(EndianType.Little); w.BitsPerSample = r.ReadUInt16(EndianType.Little); if (wavStream.Position - p != w.ChunkHeaderLength) w.ExtraBytes = r.ReadBytes((int)(w.ChunkHeaderLength - (wavStream.Position - p))); b = r.ReadBytes(4); w.ChunkId = Encoding.Default.GetString(b); w.ChunkLength = r.ReadUInt32(EndianType.Little); w.DataOffset = (int)wavStream.Position; return w; }
private CompressionType xBoxUncompress(string compFilename, string uncompFilename) { if (uncompFilename.ToLower() == compFilename.ToLower()) //nothing to do return CompressionType.None; if (File.Exists(uncompFilename)) File.Delete(uncompFilename); CompressionType ct = CompressionType.None; try { using (FileStream inFileStream = new FileStream(compFilename, FileMode.Open, FileAccess.Read)) { using (BinaryEndianReader br = new BinaryEndianReader(inFileStream)) { uint chunkLen = 0; while (chunkLen != 0xffffffff) { using (FileStream outFileStream = new FileStream(uncompFilename, chunkLen == 0 ? FileMode.Create : FileMode.Append)) { using (ZlibOutputStream outZStream = new ZlibOutputStream(outFileStream, Rebex.IO.Compression.CompressionMode.Decompress)) { //test for newer GHWT+ compression long pos = inFileStream.Position; if (Encoding.ASCII.GetString(br.ReadBytes(4)) == "CHNK") { ct = CompressionType.ZLibChunk; EndianType et = this.EndianType; //Decompress each section uint headerLen = br.ReadUInt32(et); uint blockLen = br.ReadUInt32(et); chunkLen = br.ReadUInt32(et); uint nextchunkLen = br.ReadUInt32(et); uint uncompressedLen = br.ReadUInt32(et); uint uncompressedPos = br.ReadUInt32(et); _zLibHeaderLen = headerLen; if (uncompressedLen > this._zLibChunkSize) this._zLibChunkSize = uncompressedLen; inFileStream.Seek(headerLen - (inFileStream.Position - pos), SeekOrigin.Current); outZStream.WriteByte(0x58); //Search deflate.cs for "Nanook" for the mod to stop it being written out outZStream.WriteByte(0x85); //this is the header MS uses, thie zlib deflate uses 78DA ?? copyStream(inFileStream, outZStream, (int)blockLen); if (chunkLen != 0xffffffff) inFileStream.Seek((chunkLen - blockLen) - headerLen, SeekOrigin.Current); } else { try { inFileStream.Seek(pos, SeekOrigin.Begin); outZStream.WriteByte(0x58); outZStream.WriteByte(0x85); copyStream(inFileStream, outZStream); ct = CompressionType.ZLib; break; } catch { break; //no compression??? } } outZStream.Flush(); outFileStream.Flush(); //outZStream.Close(); } } } } } } catch (Exception ex) { throw new ApplicationException(string.Format("Uncompress Failed: {0}", ex)); } //using (DeflateStream ds = new DeflateStream(File.Open(compFilename, FileMode.Open), CompressionMode.Decompress)) //{ // using (BinaryReader br = new BinaryReader(ds)) // { // using (FileStream ms = File.Create(uncompFilename)) // { // using (BinaryWriter bw = new BinaryWriter(ms)) // { // long l; // do // { // l = ms.Length; // bw.Write(br.ReadBytes(10000)); // } // while (l != ms.Length); // } // } // } //} return ct; }
private void parsePak(PakFormat pakFormat, bool debugFile) { _debugFile = debugFile; _pakFormat = pakFormat; _pakFilename = (!_debugFile ? _pakFormat.FullPakFilename : _pakFormat.FullDebugFilename); Dictionary <uint, PakDbgQbKey> qbKeyFilenames = new Dictionary <uint, PakDbgQbKey>(); //if PC or xbox then we need to look up the filename from the debug file //create a PakEditor and load the debug pak, then load all internal debug files, add the first line to our filenames dictionary if (!debugFile) // && (_pakFormat.PakFormatType == PakFormatType.PC || _pakFormat.PakFormatType == PakFormatType.XBox)) { try { _pakFormat.Decompress(); _pakFilename = _pakFormat.FullPakFilename; } catch (Exception ex) { throw new Exception("Decompression Error", ex); } if (_pakFormat.DebugFileExists && _debugFile) // cancer { string debugFileContents; string filename; uint crc; PakEditor pakDebug = new PakEditor(new PakFormat(_pakFormat.FullDebugFilename, "", _pakFormat.FullDebugFilename, _pakFormat.PakFormatType), true); foreach (PakHeaderItem dphi in pakDebug.Headers.Values) { debugFileContents = string.Empty; filename = string.Empty; crc = 0; if (dphi.FullFilenameQbKey != 0) { crc = dphi.FullFilenameQbKey; } else if (dphi.PakFullFileNameQbKey != 0) { crc = dphi.PakFullFileNameQbKey; } else if (dphi.NameOnlyCrc != 0) { crc = dphi.NameOnlyCrc; } if (crc != 0) { filename = crc.ToString("X").PadLeft(8, '0'); if (pakDebug.Headers.ContainsKey(filename.ToLower())) { debugFileContents = pakDebug.ExtractFileToString(filename); if (debugFileContents.Length != 0) { addDebugFilename(debugFileContents, qbKeyFilenames, crc); } } } } } } long minOffset = uint.MaxValue; long maxOffset = 0; _pakHeaders = new Dictionary <string, PakHeaderItem>(); using (Stream st = File.Open(_pakFilename, FileMode.Open, FileAccess.Read)) { _pakFileLength = st.Length; using (BinaryEndianReader br = new BinaryEndianReader(st)) { PakHeaderItem phi = null; QbKey lastQbKey = QbKey.Create("last"); QbKey dotLastQbKey = QbKey.Create(".last"); do { phi = new PakHeaderItem(); phi.HeaderStart = (uint)st.Position; phi.IsStoredInPak = true; phi.FileType = QbKey.Create(br.ReadUInt32(_pakFormat.EndianType)); //if the entry has the file type of last then we're done if (phi.FileType == lastQbKey || phi.FileType == dotLastQbKey) { break; } phi.FileOffset = br.ReadUInt32(_pakFormat.EndianType); phi.FileLength = br.ReadUInt32(_pakFormat.EndianType); phi.PakFullFileNameQbKey = br.ReadUInt32(_pakFormat.EndianType); phi.FullFilenameQbKey = br.ReadUInt32(_pakFormat.EndianType); phi.NameOnlyCrc = br.ReadUInt32(_pakFormat.EndianType); phi.Unknown = br.ReadUInt32(_pakFormat.EndianType); phi.Flags = (PakHeaderFlags)br.ReadUInt32(_pakFormat.EndianType); if ((phi.Flags & PakHeaderFlags.Filename) != PakHeaderFlags.Filename) { uint crc = 0; //get any Crc if (phi.FullFilenameQbKey != 0) { crc = phi.FullFilenameQbKey; } else if (phi.PakFullFileNameQbKey != 0) { crc = phi.PakFullFileNameQbKey; } else if (phi.NameOnlyCrc != 0) { crc = phi.NameOnlyCrc; } if (!debugFile) { uint crc2 = 0; //get a crc that exists in the debug names if (qbKeyFilenames.ContainsKey(phi.FullFilenameQbKey)) { crc2 = phi.FullFilenameQbKey; } else if (qbKeyFilenames.ContainsKey(phi.PakFullFileNameQbKey)) { crc2 = phi.PakFullFileNameQbKey; } else if (qbKeyFilenames.ContainsKey(phi.NameOnlyCrc)) { crc2 = phi.NameOnlyCrc; } //if 0 then get any crc if (crc2 != 0) { phi.Filename = qbKeyFilenames[crc2].Filename; phi.DebugQbKey = qbKeyFilenames[crc2].DebugQbKey; crc = crc2; } } if (phi.Filename == null) { if (crc != 0) { phi.Filename = crc.ToString("X").PadLeft(8, '0'); } else { phi.Filename = string.Format("Unknown={0}", phi.HeaderStart.ToString("X").PadLeft(8, '0')); } } } else { phi.Filename = UTF8Encoding.UTF8.GetString(br.ReadBytes(PakHeaderItem.FileNameMaxLength)).TrimEnd(new char[] { '\0' }); } try { _pakHeaders.Add(phi.Filename.ToLower(), phi); } catch (Exception ex) { throw new ApplicationException(string.Format("Error adding '{0}' to headers: {1}", phi.Filename, ex.Message)); } if (phi.HeaderStart + phi.FileOffset < minOffset) { minOffset = phi.HeaderStart + phi.FileOffset; } if (phi.HeaderStart + phi.FileOffset > maxOffset) { maxOffset = phi.HeaderStart + phi.FileOffset; } }while (1 == 1); //minOffset > fs.Position + 0x100); //drop out if we reach the data //this is a simple hack/fix to cater for padding on PAK files, //it relies on the data starting at the top of the PAB file (which it always does, currently) _requiresPab = maxOffset >= st.Length; if (!debugFile) //only when loading pak { _pakFormat.PakPabMinDataOffset = minOffset; //remember this value } //detect GH5 PAB files if (_requiresPab) { foreach (PakHeaderItem ph in _pakHeaders.Values) { ph.FileOffset += (uint)(_pakFileLength - _pakFormat.PakPabMinDataOffset) - (_pakFormat.PakPabMinDataOffset == 0 ? ph.HeaderStart : 0); //gh5 doesn't hold the offset in relation to the data. } minOffset = _pakFileLength; } } } _qbFilenames = new string[_pakHeaders.Count]; int i = 0; foreach (PakHeaderItem hi in _pakHeaders.Values) { _qbFilenames[i++] = hi.Filename; } StructItemChildrenType s; if (!debugFile) { s = this.StructItemChildrenType; //auto detect the structitemchildren type } }
private void replaceFile(string qbFilename, long newLength, bool remove, WriteDataToStream callback) { int filePad = _pakFormat.FilePadSize; PakHeaderItem phi = null; if (_pakHeaders.ContainsKey(qbFilename.ToLower())) { phi = _pakHeaders[qbFilename.ToLower()]; } else { string fqk = QbKey.Create(qbFilename).Crc.ToString("X").PadLeft(8, '0').ToLower(); if (_pakHeaders.ContainsKey(fqk)) { phi = _pakHeaders[fqk]; } } if (phi != null) { long pad = filePad - (newLength % filePad); if (pad == filePad) { pad = 0; } string newPakFilename = string.Format("{0}_{1}", _pakFormat.FullPakFilename, Guid.NewGuid().ToString("N")); string newPabFilename = string.Format("{0}_{1}", _pakFormat.FullPabFilename, Guid.NewGuid().ToString("N")); uint minOffset = uint.MaxValue; bool itemFound = false; uint nextOffset = 0; foreach (PakHeaderItem ph in _pakHeaders.Values) { if (itemFound) { nextOffset = ph.FileOffset + ph.HeaderStart; itemFound = false; //don't enter this if again } if (object.ReferenceEquals(phi, ph)) { itemFound = true; } if (ph.HeaderStart + ph.FileOffset < minOffset) { minOffset = ph.HeaderStart + ph.FileOffset; } } uint lastItemPad = 0; int repPad = filePad - ((int)phi.FileLength % filePad); if (repPad == filePad) { repPad = 0; } //previously badly padded or last file if (nextOffset != 0 && (nextOffset - (phi.FileOffset + phi.HeaderStart)) % filePad != 0) { repPad = (int)((nextOffset - (phi.FileOffset + phi.HeaderStart)) - phi.FileLength); } //position of the LAST header item long lastHeaderPos = 0; //the length of all the headers (like pak when there's a pab) with padding long allHeadersLen = minOffset; //position in the input file where our file is to be replaced (not including header pos) long fileReplacePos = (phi.HeaderStart + phi.FileOffset) - allHeadersLen; //position in the input file after the file that is to be replaced long fileAfterReplacePos = fileReplacePos + phi.FileLength + repPad; //item size before modifying header //open input pak using (FileStream fsPakI = File.Open(_pakFilename, FileMode.Open, FileAccess.Read)) { using (BinaryEndianReader brPakI = new BinaryEndianReader(fsPakI)) { //open output pak temp file using (FileStream fsPakO = File.Create(newPakFilename)) { using (BinaryEndianWriter bwPakO = new BinaryEndianWriter(fsPakO)) { long diffLen = 0; //do not compensate for missing header when using zlib compression as the header is padded if (remove && _pakFormat.CompressionType != CompressionType.ZLibChunk) //we need to cater for the header being removed on all items before it. { diffLen = PakHeaderItem.FullHeaderLength; if ((phi.Flags & PakHeaderFlags.Filename) != PakHeaderFlags.Filename) { diffLen -= PakHeaderItem.FileNameMaxLength; } } //write out the headers foreach (PakHeaderItem ph in _pakHeaders.Values) { //apply offset change before finding file to be replaced //this will prevents the offset of the replaced file being changed ph.FileOffset = (uint)(ph.FileOffset - (long)diffLen); if (object.ReferenceEquals(phi, ph)) { if (remove) { long remPad = filePad - (phi.FileLength % filePad); if (remPad == filePad) { remPad = 0; } diffLen = phi.FileLength + remPad; //now cater for the difference in file size } else { diffLen = (long)((ph.FileLength + repPad) - (newLength + pad)); ph.FileLength = (uint)newLength; //0 for remove } } lastHeaderPos += PakHeaderItem.FullHeaderLength; if (!(remove && object.ReferenceEquals(phi, ph))) { writeHeaderItem(bwPakO, ph); } if ((ph.Flags & PakHeaderFlags.Filename) != PakHeaderFlags.Filename) { lastHeaderPos -= PakHeaderItem.FileNameMaxLength; } } //Move to the "last" header fsPakI.Seek(lastHeaderPos, SeekOrigin.Begin); //write out "last" HeaderType bwPakO.Write(brPakI.ReadBytes(4)); //Modify and write the offset of the "last" header's data uint lastOffset = brPakI.ReadUInt32(_pakFormat.EndianType); lastOffset = (uint)(lastOffset - (long)diffLen); //fix bad padding on last file if (nextOffset == 0 && ((lastOffset - (phi.FileOffset + phi.HeaderStart) % filePad) % filePad) != 0) { lastItemPad = (uint)filePad - (uint)(lastOffset % filePad); lastOffset += lastItemPad; } bwPakO.Write(lastOffset, _pakFormat.EndianType); //write out the end of the last header copyData(fsPakI, fsPakO, allHeadersLen - fsPakI.Position); } } } } //open input pak using (FileStream fsPakI = File.Open(_requiresPab ? _pakFormat.FullPabFilename : _pakFilename, FileMode.Open, FileAccess.Read)) { using (BinaryEndianReader brPakI = new BinaryEndianReader(fsPakI)) { //open output pak temp file using (FileStream fsPakO = _requiresPab ? File.Open(newPabFilename, FileMode.Create) : File.Open(newPakFilename, FileMode.Append)) { using (BinaryEndianWriter bwPakO = new BinaryEndianWriter(fsPakO)) { if (!_requiresPab) { fsPakI.Seek(allHeadersLen, SeekOrigin.Begin); } //Write out the data starting from the last header to the start of the file to be replaced (minus the length of the type and offset) copyData(fsPakI, fsPakO, fileReplacePos); //Write the new file into the pak file int pos = (int)fsPakO.Position; callback(fsPakO); if (pad != 0) { fsPakO.Write(new byte[pad], 0, (int)pad); } if (!_requiresPab) { fileAfterReplacePos += allHeadersLen; } if (lastItemPad != 0) { fileAfterReplacePos -= lastItemPad; // a bit of a hack as this was not applied when this var was set as we didn't know the values } //write the remainder of source the pak file fsPakI.Seek(fileAfterReplacePos, SeekOrigin.Begin); copyData(fsPakI, fsPakO, fsPakI.Length - fileAfterReplacePos); fsPakO.Flush(); } } } } fixUncompressedFileLengths(newPakFilename, newPabFilename); File.Delete(_pakFilename); File.Move(newPakFilename, _pakFilename); if (_requiresPab) { File.Delete(_pakFormat.FullPabFilename); File.Move(newPabFilename, _pakFormat.FullPabFilename); } _pakFormat.Compress(); } else { throw new ApplicationException(string.Format("'{0}' does not exist in '{1}'", qbFilename, _pakFilename)); } }
private void replaceFile(string qbFilename, long newLength, bool remove, WriteDataToStream callback) { int filePad = _pakFormat.FilePadSize; PakHeaderItem phi = null; if (_pakHeaders.ContainsKey(qbFilename.ToLower())) phi = _pakHeaders[qbFilename.ToLower()]; else { string fqk = QbKey.Create(qbFilename).Crc.ToString("X").PadLeft(8, '0').ToLower(); if (_pakHeaders.ContainsKey(fqk)) phi = _pakHeaders[fqk]; } if (phi != null) { long pad = filePad - (newLength % filePad); if (pad == filePad) pad = 0; string newPakFilename = string.Format("{0}_{1}", _pakFormat.FullPakFilename, Guid.NewGuid().ToString("N")); string newPabFilename = string.Format("{0}_{1}", _pakFormat.FullPabFilename, Guid.NewGuid().ToString("N")); uint minOffset = uint.MaxValue; bool itemFound = false; uint nextOffset = 0; foreach (PakHeaderItem ph in _pakHeaders.Values) { if (itemFound) { nextOffset = ph.FileOffset + ph.HeaderStart; itemFound = false; //don't enter this if again } if (object.ReferenceEquals(phi, ph)) itemFound = true; if (ph.HeaderStart + ph.FileOffset < minOffset) minOffset = ph.HeaderStart + ph.FileOffset; } uint lastItemPad = 0; int repPad = filePad - ((int)phi.FileLength % filePad); if (repPad == filePad) repPad = 0; //previously badly padded or last file if (nextOffset != 0 && (nextOffset - (phi.FileOffset + phi.HeaderStart)) % filePad != 0) repPad = (int)((nextOffset - (phi.FileOffset + phi.HeaderStart)) - phi.FileLength); //position of the LAST header item long lastHeaderPos = 0; //the length of all the headers (like pak when there's a pab) with padding long allHeadersLen = minOffset; //position in the input file where our file is to be replaced (not including header pos) long fileReplacePos = (phi.HeaderStart + phi.FileOffset) - allHeadersLen; //position in the input file after the file that is to be replaced long fileAfterReplacePos = fileReplacePos + phi.FileLength + repPad; //item size before modifying header //open input pak using (FileStream fsPakI = File.Open(_pakFilename, FileMode.Open, FileAccess.Read)) { using (BinaryEndianReader brPakI = new BinaryEndianReader(fsPakI)) { //open output pak temp file using (FileStream fsPakO = File.Create(newPakFilename)) { using (BinaryEndianWriter bwPakO = new BinaryEndianWriter(fsPakO)) { long diffLen = 0; //do not compensate for missing header when using zlib compression as the header is padded if (remove && _pakFormat.CompressionType != CompressionType.ZLibChunk) //we need to cater for the header being removed on all items before it. { diffLen = PakHeaderItem.FullHeaderLength; if ((phi.Flags & PakHeaderFlags.Filename) != PakHeaderFlags.Filename) diffLen -= PakHeaderItem.FileNameMaxLength; } //write out the headers foreach (PakHeaderItem ph in _pakHeaders.Values) { //apply offset change before finding file to be replaced //this will prevents the offset of the replaced file being changed ph.FileOffset = (uint)(ph.FileOffset - (long)diffLen); if (object.ReferenceEquals(phi, ph)) { if (remove) { long remPad = filePad - (phi.FileLength % filePad); if (remPad == filePad) remPad = 0; diffLen = phi.FileLength + remPad; //now cater for the difference in file size } else { diffLen = (long)((ph.FileLength + repPad) - (newLength + pad)); ph.FileLength = (uint)newLength; //0 for remove } } lastHeaderPos += PakHeaderItem.FullHeaderLength; if (!(remove && object.ReferenceEquals(phi, ph))) writeHeaderItem(bwPakO, ph); if ((ph.Flags & PakHeaderFlags.Filename) != PakHeaderFlags.Filename) lastHeaderPos -= PakHeaderItem.FileNameMaxLength; } //Move to the "last" header fsPakI.Seek(lastHeaderPos, SeekOrigin.Begin); //write out "last" HeaderType bwPakO.Write(brPakI.ReadBytes(4)); //Modify and write the offset of the "last" header's data uint lastOffset = brPakI.ReadUInt32(_pakFormat.EndianType); lastOffset = (uint)(lastOffset - (long)diffLen); //fix bad padding on last file if (nextOffset == 0 && ((lastOffset - (phi.FileOffset + phi.HeaderStart) % filePad) % filePad) != 0) { lastItemPad = (uint)filePad - (uint)(lastOffset % filePad); lastOffset += lastItemPad; } bwPakO.Write(lastOffset, _pakFormat.EndianType); //write out the end of the last header copyData(fsPakI, fsPakO, allHeadersLen - fsPakI.Position); } } } } //open input pak using (FileStream fsPakI = File.Open(_requiresPab ? _pakFormat.FullPabFilename : _pakFilename, FileMode.Open, FileAccess.Read)) { using (BinaryEndianReader brPakI = new BinaryEndianReader(fsPakI)) { //open output pak temp file using (FileStream fsPakO = _requiresPab ? File.Open(newPabFilename, FileMode.Create) : File.Open(newPakFilename, FileMode.Append)) { using (BinaryEndianWriter bwPakO = new BinaryEndianWriter(fsPakO)) { if (!_requiresPab) fsPakI.Seek(allHeadersLen, SeekOrigin.Begin); //Write out the data starting from the last header to the start of the file to be replaced (minus the length of the type and offset) copyData(fsPakI, fsPakO, fileReplacePos); //Write the new file into the pak file int pos = (int)fsPakO.Position; callback(fsPakO); if (pad != 0) fsPakO.Write(new byte[pad], 0, (int)pad); if (!_requiresPab) fileAfterReplacePos += allHeadersLen; if (lastItemPad != 0) fileAfterReplacePos -= lastItemPad; // a bit of a hack as this was not applied when this var was set as we didn't know the values //write the remainder of source the pak file fsPakI.Seek(fileAfterReplacePos, SeekOrigin.Begin); copyData(fsPakI, fsPakO, fsPakI.Length - fileAfterReplacePos); fsPakO.Flush(); } } } } fixUncompressedFileLengths(newPakFilename, newPabFilename); File.Delete(_pakFilename); File.Move(newPakFilename, _pakFilename); if (_requiresPab) { File.Delete(_pakFormat.FullPabFilename); File.Move(newPabFilename, _pakFormat.FullPabFilename); } _pakFormat.Compress(); } else throw new ApplicationException(string.Format("'{0}' does not exist in '{1}'", qbFilename, _pakFilename)); }
private void parsePak(PakFormat pakFormat, bool debugFile) { _debugFile = debugFile; _pakFormat = pakFormat; _pakFilename = (!_debugFile ? _pakFormat.FullPakFilename : _pakFormat.FullDebugFilename); Dictionary<uint, PakDbgQbKey> qbKeyFilenames = new Dictionary<uint, PakDbgQbKey>(); //if PC or xbox then we need to look up the filename from the debug file //create a PakEditor and load the debug pak, then load all internal debug files, add the first line to our filenames dictionary if (!debugFile) // && (_pakFormat.PakFormatType == PakFormatType.PC || _pakFormat.PakFormatType == PakFormatType.XBox)) { try { _pakFormat.Decompress(); _pakFilename = _pakFormat.FullPakFilename; } catch (Exception ex) { throw new Exception("Decompression Error", ex); } if (_pakFormat.DebugFileExists) { string debugFileContents; string filename; uint crc; PakEditor pakDebug = new PakEditor(new PakFormat(_pakFormat.FullDebugFilename, "", _pakFormat.FullDebugFilename, _pakFormat.PakFormatType), true); foreach (PakHeaderItem dphi in pakDebug.Headers.Values) { debugFileContents = string.Empty; filename = string.Empty; crc = 0; if (dphi.FullFilenameQbKey != 0) crc = dphi.FullFilenameQbKey; else if (dphi.PakFullFileNameQbKey != 0) crc = dphi.PakFullFileNameQbKey; else if (dphi.NameOnlyCrc != 0) crc = dphi.NameOnlyCrc; if (crc != 0) { filename = crc.ToString("X").PadLeft(8, '0'); if (pakDebug.Headers.ContainsKey(filename.ToLower())) { debugFileContents = pakDebug.ExtractFileToString(filename); if (debugFileContents.Length != 0) addDebugFilename(debugFileContents, qbKeyFilenames, crc); } } } } } long minOffset = uint.MaxValue; long maxOffset = 0; _pakHeaders = new Dictionary<string, PakHeaderItem>(); using (Stream st = File.Open(_pakFilename, FileMode.Open, FileAccess.Read)) { _pakFileLength = st.Length; using (BinaryEndianReader br = new BinaryEndianReader(st)) { PakHeaderItem phi = null; QbKey lastQbKey = QbKey.Create("last"); QbKey dotLastQbKey = QbKey.Create(".last"); do { phi = new PakHeaderItem(); phi.HeaderStart = (uint)st.Position; phi.IsStoredInPak = true; phi.FileType = QbKey.Create(br.ReadUInt32(_pakFormat.EndianType)); //if the entry has the file type of last then we're done if (phi.FileType == lastQbKey || phi.FileType == dotLastQbKey) break; phi.FileOffset = br.ReadUInt32(_pakFormat.EndianType); phi.FileLength = br.ReadUInt32(_pakFormat.EndianType); phi.PakFullFileNameQbKey = br.ReadUInt32(_pakFormat.EndianType); phi.FullFilenameQbKey = br.ReadUInt32(_pakFormat.EndianType); phi.NameOnlyCrc = br.ReadUInt32(_pakFormat.EndianType); phi.Unknown = br.ReadUInt32(_pakFormat.EndianType); phi.Flags = (PakHeaderFlags)br.ReadUInt32(_pakFormat.EndianType); if ((phi.Flags & PakHeaderFlags.Filename) != PakHeaderFlags.Filename) { uint crc = 0; //get any Crc if (phi.FullFilenameQbKey != 0) crc = phi.FullFilenameQbKey; else if (phi.PakFullFileNameQbKey != 0) crc = phi.PakFullFileNameQbKey; else if (phi.NameOnlyCrc != 0) crc = phi.NameOnlyCrc; if (!debugFile) { uint crc2 = 0; //get a crc that exists in the debug names if (qbKeyFilenames.ContainsKey(phi.FullFilenameQbKey)) crc2 = phi.FullFilenameQbKey; else if (qbKeyFilenames.ContainsKey(phi.PakFullFileNameQbKey)) crc2 = phi.PakFullFileNameQbKey; else if (qbKeyFilenames.ContainsKey(phi.NameOnlyCrc)) crc2 = phi.NameOnlyCrc; //if 0 then get any crc if (crc2 != 0) { phi.Filename = qbKeyFilenames[crc2].Filename; phi.DebugQbKey = qbKeyFilenames[crc2].DebugQbKey; crc = crc2; } } if (phi.Filename == null) { if (crc != 0) phi.Filename = crc.ToString("X").PadLeft(8, '0'); else phi.Filename = string.Format("Unknown={0}", phi.HeaderStart.ToString("X").PadLeft(8, '0')); } } else phi.Filename = UTF8Encoding.UTF8.GetString(br.ReadBytes(PakHeaderItem.FileNameMaxLength)).TrimEnd(new char[] { '\0' }); try { _pakHeaders.Add(phi.Filename.ToLower(), phi); } catch (Exception ex) { throw new ApplicationException(string.Format("Error adding '{0}' to headers: {1}", phi.Filename, ex.Message)); } if (phi.HeaderStart + phi.FileOffset < minOffset) minOffset = phi.HeaderStart + phi.FileOffset; if (phi.HeaderStart + phi.FileOffset > maxOffset) maxOffset = phi.HeaderStart + phi.FileOffset; } while (1 == 1); //minOffset > fs.Position + 0x100); //drop out if we reach the data //this is a simple hack/fix to cater for padding on PAK files, //it relies on the data starting at the top of the PAB file (which it always does, currently) _requiresPab = maxOffset >= st.Length; if (!debugFile) //only when loading pak _pakFormat.PakPabMinDataOffset = minOffset; //remember this value //detect GH5 PAB files if (_requiresPab) { foreach (PakHeaderItem ph in _pakHeaders.Values) ph.FileOffset += (uint)(_pakFileLength - _pakFormat.PakPabMinDataOffset) - (_pakFormat.PakPabMinDataOffset == 0 ? ph.HeaderStart : 0); //gh5 doesn't hold the offset in relation to the data. minOffset = _pakFileLength; } } } _qbFilenames = new string[_pakHeaders.Count]; int i = 0; foreach (PakHeaderItem hi in _pakHeaders.Values) _qbFilenames[i++] = hi.Filename; StructItemChildrenType s; if (!debugFile) s = this.StructItemChildrenType; //auto detect the structitemchildren type }
private void parse() { _datItems.Clear(); using (FileStream fs = File.OpenRead(_datFilename)) { using (BinaryEndianReader br = new BinaryEndianReader(fs)) { uint files = br.ReadUInt32(_endianType); _headerFileSize = br.ReadUInt32(_endianType); QbKey songQk; for (int i = 0; i < files; i++) { songQk = QbKey.Create(br.ReadUInt32(_endianType)); _datItems.Add(songQk.Crc, new DatItem(songQk, br.ReadUInt32(_endianType), br.ReadUInt32(_endianType), br.ReadBytes(8))); } } } }
public override void Construct(BinaryEndianReader br, QbItemType type) { //System.Diagnostics.Debug.WriteLine(string.Format("{0} - 0x{1}", type.ToString(), (base.StreamPos(br) - 4).ToString("X").PadLeft(8, '0'))); _isUnicode = ((type == QbItemType.SectionStringW || type == QbItemType.ArrayStringW || type == QbItemType.StructItemStringW) && (base.Root.PakFormat.PakFormatType == PakFormatType.PC || base.Root.PakFormat.PakFormatType == PakFormatType.XBox)); byte[] bytes; base.Construct(br, type); this.Strings = new string[base.ItemCount]; _charWidth = !_isUnicode ? 1 : 2; if (base.ItemCount != 0) { //use pointers to read quickly if (base.ItemCount > 1) { for (int i = 0; i < base.ItemCount - 1; i++) { if (base.StreamPos(br) != base.Pointers[i]) //pointer test { throw new ApplicationException(QbFile.FormatBadPointerExceptionMessage(this, base.StreamPos(br), base.Pointers[i])); } bytes = br.ReadBytes((int)((base.Pointers[i + 1] - _charWidth) - base.StreamPos(br))); _strings[i] = bytesToString(bytes); //handles unicode if (!_isUnicode ? (br.ReadByte() != 0) : (br.ReadByte() != 0 || br.ReadByte() != 0)) { throw new ApplicationException(string.Format("Null byte expected reading string array at 0x{0}", (base.StreamPos(br) - _charWidth).ToString("X").PadLeft(8, '0'))); } } if (base.StreamPos(br) != base.Pointers[base.ItemCount - 1]) //pointer test { throw new ApplicationException(QbFile.FormatBadPointerExceptionMessage(this, base.StreamPos(br), base.Pointers[base.ItemCount - 1])); } } //use the slow method read the last string StringBuilder sb = new StringBuilder(); //if we have come from an array we must align our position to %4 int byteAmount = (int)(4 - (base.StreamPos(br) % 4)); do { bytes = br.ReadBytes(byteAmount); sb.Append(bytesToString(bytes)); byteAmount = 4; }while (!_isUnicode ? (bytes[bytes.Length - 1] != '\0') : (bytes[bytes.Length - 1] != '\0' || bytes[bytes.Length - 2] != '\0')); //get text and remove any trailing null bytes _strings[base.ItemCount - 1] = sb.ToString().TrimEnd(new char[] { '\0' }); } base.ConstructEnd(br); }
public override void Construct(BinaryEndianReader br, QbItemType type) { //System.Diagnostics.Debug.WriteLine(string.Format("{0} - 0x{1}", type.ToString(), (base.StreamPos(br) - 4).ToString("X").PadLeft(8, '0'))); base.Construct(br, type); _unknown = br.ReadUInt32(base.Root.PakFormat.EndianType); uint decompressedSize = br.ReadUInt32(base.Root.PakFormat.EndianType); uint compressedSize = br.ReadUInt32(base.Root.PakFormat.EndianType); // Get script data Lzss lz = new Lzss(); _scriptData = br.ReadBytes((int)compressedSize); if (compressedSize < decompressedSize) _scriptData = lz.Decompress(_scriptData); if (_scriptData.Length != decompressedSize) throw new ApplicationException(string.Format("Location 0x{0}: Script decompressed to {1} bytes not {2}", (base.StreamPos(br) - compressedSize).ToString("X").PadLeft(8, '0'), _scriptData.Length.ToString(), decompressedSize.ToString())); // Padding... if ((base.StreamPos(br) % 4) != 0) br.BaseStream.Seek(4 - (base.StreamPos(br) % 4), SeekOrigin.Current); base.ConstructEnd(br); }