private void WritedEditedFileTable(FileStream tempFileStream, NpkFileTableEntry entryOfImgEditing, int imgByteCountChange, byte[] imgPathBytes) { foreach (NpkFileTableEntry file in _reader.Files) { // uint - file location offset of file // uint - file size in bytes // 256 bytes for NPK path (eg sprite/foo/bar.img) - The path in ASCII is padded with 0 bytes to 256 bytes then xor'd with a static "key" if (file.Location.FileOffset <= entryOfImgEditing.Location.FileOffset) { // If this file's bytes comes before the file whose bytes we're changing, its offset does not change. // Same if it's the same file whose bytes we're changing or one that uses the same bytes. tempFileStream.WriteUnsigned32Le((uint)file.Location.FileOffset, _intWriteBuffer); } else { // otherwise, add the difference (possibly negative) between sizes of new file and old file int newOffset = file.Location.FileOffset + imgByteCountChange; tempFileStream.WriteUnsigned32Le((uint)newOffset, _intWriteBuffer); } if (file.Location.FileOffset != entryOfImgEditing.Location.FileOffset) { // If this file is not the file we're changing or one that uses the same bytes, the size does not change tempFileStream.WriteUnsigned32Le((uint)file.Location.Size, _intWriteBuffer); } else { int newImgSize = file.Location.Size + imgByteCountChange; tempFileStream.WriteUnsigned32Le((uint)newImgSize, _intWriteBuffer); } for (int i = 0; i < 256; i++) { imgPathBytes[i] = 0; } Encoding.ASCII.GetBytes(file.Name.Path, 0, file.Name.Path.Length, imgPathBytes, 0); for (int i = 0; i < 256; i++) { imgPathBytes[i] ^= NpkReader.s_key[i]; } tempFileStream.Write(imgPathBytes, 0, 256); } }
private void WriteEditedNPK(FileStream tempFileStream, NpkPath imgPath, NpkFileTableEntry entryOfImgEditing, int editedFrameIndex, IReadOnlyList <FrameInfo> frameListOfImgEditing, FrameInfo frameMetadataOfFrameEditing, FrameInfo newFrameMetadata, byte[] newPixelData, int newPixelDataLength, byte[] frameMetadataBytes) { int imgByteCountChange = GetImgByteCountChange(frameMetadataOfFrameEditing, newFrameMetadata, newPixelDataLength); List <NpkByteRange> frameLocationsOfImgEditing = _reader.FrameLocations[imgPath]; // Header is same tempFileStream.Write(NpkReader.s_headerBytes, 0, NpkReader.s_headerBytes.Length); // number of files is same tempFileStream.WriteUnsigned32Le((uint)_reader.Files.Count, _intWriteBuffer); byte[] imgPathBytes = new byte[256]; // Write file table WritedEditedFileTable(tempFileStream, entryOfImgEditing, imgByteCountChange, imgPathBytes); // Write from original stream until offset of file we're changing // Write from current until currentEntry.Location.FileOffset _npkStream.Seek(tempFileStream.Position, SeekOrigin.Begin); int numBytesToCopy = (int)(entryOfImgEditing.Location.FileOffset - tempFileStream.Position); _npkStream.CopyToPartially(tempFileStream, numBytesToCopy); // Now write the new .img WriteEditedImg(tempFileStream, editedFrameIndex, frameListOfImgEditing, frameMetadataOfFrameEditing, newFrameMetadata, newPixelData, newPixelDataLength, frameMetadataBytes, frameLocationsOfImgEditing); // now write the rest of the original file that's after this .img if (entryOfImgEditing.Location.FileOffset + entryOfImgEditing.Location.Size < _npkStream.Length) { _npkStream.Seek(entryOfImgEditing.Location.FileOffset + entryOfImgEditing.Location.Size, SeekOrigin.Begin); numBytesToCopy = (int)_npkStream.Length - (entryOfImgEditing.Location.FileOffset + entryOfImgEditing.Location.Size); _npkStream.CopyToPartially(tempFileStream, numBytesToCopy); } }
/// <summary> /// /// </summary> /// <param name="imgPath"></param> /// <param name="frameIndex"></param> /// <param name="newFrameMetadata">If this indicates a link frame, <paramref name="newFramePixels"/> is ignored. The IsCompressed flag is honored, compressing the image if it is set. The CompressedLength field is not used.</param> /// <param name="newFramePixels">Readable stream consisting solely of the pixel data, in the format indicated by the metadata.</param> public void EditFrame(NpkPath imgPath, int frameIndex, FrameInfo newFrameMetadata, Stream newFramePixels) { ThrowIfDisposed(); ThrowIfNoFileOpen(); if (!_reader.Frames.ContainsKey(imgPath)) { throw new ArgumentException("{0} is not in the NPK.".F(imgPath)); } if (frameIndex >= _reader.Frames[imgPath].Count) { throw new ArgumentException("{0} does not have a frame {1}.".F(imgPath, frameIndex)); } NpkFileTableEntry entryOfImgEditing = _reader.Files.Where(f => f.Name.Equals(imgPath)).First(); IReadOnlyList <FrameInfo> frameListOfImgEditing = _reader.Frames[imgPath]; FrameInfo frameMetadataOfFrameEditing = frameListOfImgEditing[frameIndex]; // Render the new frame in memory // pixelData is null if it's a link frame // pixelData length may be bigger than the actual pixel data. Use newPixelDataLength instead of newPixelData.Length int newPixelDataLength; byte[] newPixelData = GetPixelData(newFrameMetadata, newFramePixels, out newPixelDataLength); byte[] frameMetadataBytes = GetFrameMetadataBytes(newFrameMetadata, newPixelDataLength); string tempNpkPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".NPK"); using (FileStream tempFileStream = File.OpenWrite(tempNpkPath)) { WriteEditedNPK(tempFileStream, imgPath, entryOfImgEditing, frameIndex, frameListOfImgEditing, frameMetadataOfFrameEditing, newFrameMetadata, newPixelData, newPixelDataLength, frameMetadataBytes); } // temp file now has the new NPK! // close _reader // close _npkStream // delete original file // move temp file // TODO: "refresh" it _reader.Dispose(); _npkStream.Dispose(); // TODO: Error handling File.Delete(_openFilePath); File.Move(tempNpkPath, _openFilePath); // reopen try { _reader = new NpkReader(_openFilePath); } catch (Exception) { _openFilePath = null; _reader = null; _npkStream = null; throw; } try { _npkStream = new FileStream(_openFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (Exception) { _openFilePath = null; _reader.Dispose(); _reader = null; _npkStream = null; throw; } }