public override byte[] ToRaw() { MemoryStream rebuildingFile = new MemoryStream(); BinaryWriter rebuildingWriter = new BinaryWriter(rebuildingFile); rebuildingWriter.Write(0x32425354); //TSB2 int currentStart = 0xC; rebuildingFile.Seek(0xC, SeekOrigin.Begin); for (int i = 0; i < subroutines.Count; i++) { rebuildingFile.Seek(4, SeekOrigin.Current); subroutine temp = (subroutine)subroutines[i]; rebuildingWriter.Write(ASCIIEncoding.UTF8.GetBytes(temp.name.PadRight(0x20, '\0'))); int headerLoc = (int)rebuildingFile.Position; rebuildingFile.Seek(0xC, SeekOrigin.Current); //rebuildingFile.Write(temp.subType); Fill this in with the rest! ArrayList localVariables = new ArrayList(); int[] opcodeLocs = new int[temp.opcodes.Count]; int writeStart = (int)rebuildingFile.Position; for (int j = 0; j < temp.opcodes.Count; j++) { opcodeLocs[j] = (int)rebuildingFile.Position - writeStart; operation tempOpcode = (operation)temp.opcodes[j]; rebuildingWriter.Write(tempOpcode.opcode); if (tempOpcode.opcode == 0x46) //Only one with an actual string argument { byte[] convertedString = ASCIIEncoding.GetEncoding("shift-jis").GetBytes(tempOpcode.strArg + '\0'); if (convertedString.Length % 4 != 0) { Array.Resize(ref convertedString, (convertedString.Length + 3) & 0xFFFFFFC); } tempOpcode.intArg = convertedString.Length / 4; rebuildingWriter.Write(tempOpcode.intArg); rebuildingWriter.Write(convertedString); } else if (opcodeTypes[tempOpcode.opcode] == 4) //These are the local variable ones! { if (!stringNames.Contains(tempOpcode.strArg)) { Array.Resize(ref stringNames, stringNames.Length + 1); stringNames[stringNames.Length - 1] = tempOpcode.strArg; } localVariables.Add(Array.IndexOf(stringNames, tempOpcode.strArg)); localVariables.Add((int)((rebuildingFile.Position - 0xC - headerLoc) / 4)); rebuildingWriter.Write((int)(-1));//Array.IndexOf(stringNames, tempOpcode.strArg)); } else if (opcodeTypes[tempOpcode.opcode] == 3) { rebuildingWriter.Write(tempOpcode.floatArg); } else if (opcodeTypes[tempOpcode.opcode] == 2) { rebuildingWriter.Write(tempOpcode.intArg); } } int localVarLoc = (int)rebuildingFile.Position; for (int j = 0; j < opcodeLocs.Length; j++) { if (((operation)temp.opcodes[j]).opcode > 0x2B && ((operation)temp.opcodes[j]).opcode < 0x2F) { int branchSource = opcodeLocs[j] + 0x8; int realDest = opcodeLocs[((operation)temp.opcodes[j]).intArg]; rebuildingFile.Seek(writeStart + opcodeLocs[j] + 0x4, SeekOrigin.Begin); rebuildingWriter.Write((int)(realDest - branchSource) >> 2); } } rebuildingFile.Seek(localVarLoc, SeekOrigin.Begin); for (int j = 0; j < localVariables.Count; j += 2) { rebuildingWriter.Write((int)localVariables[localVariables.Count - 2 - j]); rebuildingWriter.Write((int)localVariables[localVariables.Count - 1 - j]); } int nextSubLoc = (int)rebuildingFile.Position; rebuildingFile.Seek(headerLoc, SeekOrigin.Begin); rebuildingWriter.Write((int)localVarLoc - headerLoc - 0xC); rebuildingWriter.Write(temp.subType); rebuildingWriter.Write(localVariables.Count / 2); rebuildingFile.Seek(currentStart, SeekOrigin.Begin); if (i + 1 < subroutines.Count) { rebuildingWriter.Write(nextSubLoc - 0xC); } else { rebuildingWriter.Write((int)0); } rebuildingFile.Seek(nextSubLoc, SeekOrigin.Begin); currentStart = nextSubLoc; } int stringListLoc = (int)rebuildingFile.Position; for (int i = 0; i < stringNames.Length; i++) { rebuildingWriter.Write(ASCIIEncoding.UTF8.GetBytes(stringNames[i].PadRight(0x20, '\0'))); } rebuildingFile.Seek(4, SeekOrigin.Begin); rebuildingWriter.Write(stringListLoc - 0xC); rebuildingWriter.Write(stringNames.Length * 0x20); byte[] compressedFile = PrsCompDecomp.compress(rebuildingFile.ToArray()); byte[] toReturn = new byte[compressedFile.Length + 0x1C]; Array.Copy(BitConverter.GetBytes(rebuildingFile.Length), 0, toReturn, 0, 4); Array.Copy(BitConverter.GetBytes(compressedFile.Length), 0, toReturn, 4, 4); Array.Copy(compressedFile, 0, toReturn, 0x1C, compressedFile.Length); return(toReturn); }
public byte[] SaveFile(bool discardChanges) { //First, combine all the files. MemoryStream groupFileStream = new MemoryStream(); MemoryStream groupHeaderStream = new MemoryStream(); BinaryWriter groupHeaderWriter; if (bigEndian) { groupHeaderWriter = new BigEndianBinaryWriter(groupHeaderStream); } else { groupHeaderWriter = new BinaryWriter(groupHeaderStream); } int paddingAmount = versionNumber == 0x1002 ? 0x3F : 0x7FF; uint mask = versionNumber == 0x1002 ? 0xFFFFFFC0 : 0xFFFFF800; if (this.chunkID.StartsWith("NML")) { groupHeaderStream.Seek(0x30, SeekOrigin.Begin); } else if (this.chunkID.StartsWith("TML")) { groupHeaderStream.Seek(0x20, SeekOrigin.Begin); } NblLoader.FileHeader[] headers = new NblLoader.FileHeader[this.fileContents.Count]; List <RawFile> savedFiles = new List <RawFile>(this.fileContents); List <int> pointers = new List <int>(); //Annoying, this one has to be a running tally, can't do it any other way. ushort filenamelength = 0; for (int i = 0; i < fileContents.Count; i++) { //Figure out whether to take the cached copy or the original if (this.loadedFileCache.ContainsKey(fileContents[i].filename) && !discardChanges) { savedFiles[i] = loadedFileCache[fileContents[i].filename].ToRawFile((uint)groupFileStream.Position); savedFiles[i].chunkSize = fileContents[i].chunkSize; } else if (savedFiles[i].fileOffset != (uint)groupFileStream.Position) { savedFiles[i].RebaseFile((uint)groupFileStream.Position); } //Let's not use a FileHeader any more. Just put all the data straight into the file. //Guessing on identifier--this SHOULD be true, generally, but...? //This needs to be stored in the file class. string identifier = "STD\0"; if (this.chunkID.StartsWith("TML")) { identifier = "NNVR"; } else if (savedFiles[i].subHeader != null && savedFiles[i].subHeader[0] == 0x4E) //'N'--so hopefully just NXIF or NUIF { identifier = Path.GetExtension(savedFiles[i].filename).Substring(1).ToUpper().PadRight(4, '\0'); } groupHeaderWriter.Write(ASCIIEncoding.ASCII.GetBytes(identifier)); groupHeaderWriter.Write(savedFiles[i].chunkSize); groupHeaderWriter.Write((int)0); //"unknown1" groupHeaderWriter.Write((int)0); //"unknown2" groupHeaderWriter.Write(ASCIIEncoding.ASCII.GetBytes(savedFiles[i].filename.PadRight(0x20, '\0'))); groupHeaderWriter.Write(savedFiles[i].fileOffset); groupHeaderWriter.Write(savedFiles[i].fileContents.Length); groupHeaderWriter.Write(pointers.Count * 4); groupHeaderWriter.Write(savedFiles[i].pointers.Count * 4); if (savedFiles[i].subHeader == null) { groupHeaderWriter.Write(new byte[0x20]); } else { groupHeaderWriter.Write(savedFiles[i].subHeader); } //Update filename length (include \0 terminator). if (this.chunkID.StartsWith("NML")) { filenamelength += (ushort)(savedFiles[i].filename.Length + 1); } //Now put the data into the file pieces. groupFileStream.Write(savedFiles[i].fileContents, 0, savedFiles[i].fileContents.Length); //Padding out to nearest 0x10 groupFileStream.Seek((int)(groupFileStream.Position + 0x1F) & 0xFFFFFFE0, SeekOrigin.Begin); pointers.AddRange(savedFiles[i].pointers); } int headerLength = (int)groupHeaderStream.Position; groupHeaderStream.Seek((groupHeaderStream.Position + paddingAmount) & mask, SeekOrigin.Begin); int uncompressedSize = (int)groupFileStream.Position; byte[] rawData; if (compressed) { if (versionNumber == 0x1002) // PSP2 files use Deflate. { groupFileStream.Seek(0, SeekOrigin.Begin); MemoryStream compressedStream = new MemoryStream(); using (DeflateStream ds = new DeflateStream(compressedStream, CompressionMode.Compress)) { groupFileStream.CopyTo(ds); } rawData = compressedStream.ToArray(); } else //PSU uses PRS. { rawData = PrsCompDecomp.compress(groupFileStream.ToArray()); } } else { rawData = groupFileStream.ToArray(); } int fileLength = rawData.Length; groupHeaderWriter.Write(rawData); //Write out pointers (if applicable) groupHeaderStream.Seek((groupHeaderStream.Position + paddingAmount) & mask, SeekOrigin.Begin); for (int i = 0; i < pointers.Count; i++) { groupHeaderWriter.Write(pointers[i]); } groupHeaderWriter.Write(new byte[((groupHeaderStream.Position + paddingAmount) & mask) - groupHeaderStream.Position]); //Now fill in the header (leaving the space if necessary). groupHeaderStream.Seek(0, SeekOrigin.Begin); groupHeaderWriter.Write(ASCIIEncoding.ASCII.GetBytes(this.chunkID)); groupHeaderWriter.Write(this.versionNumber); groupHeaderWriter.Write(filenamelength); groupHeaderWriter.Write(headerLength); groupHeaderWriter.Write(this.fileContents.Count); groupHeaderWriter.Write(uncompressedSize); if (compressed) { groupHeaderWriter.Write(rawData.Length); } else { groupHeaderWriter.Write((int)0); } groupHeaderWriter.Write(pointers.Count * 4); groupHeaderWriter.Write((int)0); //Still enforcing no encryption. return(groupHeaderStream.ToArray()); }