예제 #1
0
        private void WriteEditedImg(FileStream tempFileStream, int editedFrameIndex, IReadOnlyList <FrameInfo> frameListOfImgEditing, FrameInfo frameMetadataOfFrameEditing, FrameInfo newFrameMetadata, byte[] newPixelData, int newPixelDataLength, byte[] frameMetadataBytes, List <NpkByteRange> frameLocationsOfImgEditing)
        {
            // Need to know exact ordering of files
            // Need to know ALL files, even non-image files
            // Need to update offset of all files with an offset greater or equal to the one we're changing
            // Need to update file size of file that we're changing and any that occupy the same space

            //.img files begin with "Neople Img File\0" in ASCII
            tempFileStream.Write(NpkReader.s_imgHeaderBytes, 0, NpkReader.s_imgHeaderBytes.Length);
            // field 1 - (36 * (# non-link frames) + 8 * (# link frames))
            //uint field1Value = (uint)(36 * numberOfNonLinkFramesInImgBeforeEdit + 8 * numberOfLinkFramesInImgBeforeEdit);
            uint field1Value = GetImgField1Value(frameListOfImgEditing, frameMetadataOfFrameEditing, newFrameMetadata);

            tempFileStream.WriteUnsigned32Le(field1Value, _intWriteBuffer);
            // field 2 - always 0
            tempFileStream.WriteUnsigned32Le(0, _intWriteBuffer);
            // field 3 - always 2
            tempFileStream.WriteUnsigned32Le(2, _intWriteBuffer);
            // frame count
            tempFileStream.WriteUnsigned32Le((uint)frameListOfImgEditing.Count, _intWriteBuffer);

            // frame metadata for each frame in order
            for (int frameIndexToWrite = 0; frameIndexToWrite < frameListOfImgEditing.Count; frameIndexToWrite++)
            {
                if (frameIndexToWrite != editedFrameIndex)
                {
                    FrameInfo frameToCopy   = frameListOfImgEditing[frameIndexToWrite];
                    byte[]    metadataBytes = GetFrameMetadataBytes(frameToCopy, frameToCopy.CompressedLength);
                    tempFileStream.Write(metadataBytes, 0, metadataBytes.Length);
                }
                else
                {
                    tempFileStream.Write(frameMetadataBytes, 0, frameMetadataBytes.Length);
                }
            }

            // frame pixel data for each non-link frame in order
            for (int frameIndexToWrite = 0; frameIndexToWrite < frameListOfImgEditing.Count; frameIndexToWrite++)
            {
                if (frameIndexToWrite != editedFrameIndex)
                {
                    FrameInfo frameToCopy = frameListOfImgEditing[frameIndexToWrite];
                    if (frameToCopy.PixelFormat != PixelDataFormat.Link)
                    {
                        NpkByteRange frameToWriteOriginalLocation = frameLocationsOfImgEditing[frameIndexToWrite];
                        _npkStream.Seek(frameToWriteOriginalLocation.FileOffset, SeekOrigin.Begin);
                        _npkStream.CopyToPartially(tempFileStream, frameToWriteOriginalLocation.Size);
                    }
                }
                else
                {
                    // Write the pixel data for the frame we're changing, if it's not a link frame
                    if (newFrameMetadata.PixelFormat != PixelDataFormat.Link)
                    {
                        tempFileStream.Write(newPixelData, 0, newPixelDataLength);
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Loads the pixels of a frame.
        /// </summary>
        /// <param name="imgPath">The Npk Path of the .img file, with the leading sprite/ if present</param>
        /// <param name="frameIndex"></param>
        /// <returns></returns>
        /// <exception cref="System.IO.FileNotFoundException">The img file does not exist in this .npk file
        /// or no frame with the given index exists in the img file.</exception>
        /// <exception cref="System.IO.IOException">An I/O error occurred.</exception>
        /// <exception cref="Dfo.Npk.NpkException">The .npk file is corrupt or the format changed.</exception>
        public Image GetImage(NpkPath imgPath, int frameIndex)
        {
            imgPath.ThrowIfNull("imgPath");

            try
            {
                PreLoadSpriteMetadata(imgPath);

                IList <FrameInfo> imgFrames = m_frames[imgPath];

                if (frameIndex >= imgFrames.Count || frameIndex < 0)
                {
                    throw new FileNotFoundException("Cannot get frame index {0} of {1}. It only has {2} frames."
                                                    .F(frameIndex, imgPath, imgFrames.Count));
                }

                FrameInfo frameData      = imgFrames[frameIndex];
                int       realFrameIndex = frameIndex;

                // Follow frame links
                if (frameData.LinkFrame != null)
                {
                    realFrameIndex = frameData.LinkFrame.Value;
                    if (realFrameIndex >= imgFrames.Count || realFrameIndex < 0)
                    {
                        throw new FileNotFoundException("Cannot get linked frame index {0} of {1}. It only has {2} frames."
                                                        .F(realFrameIndex, imgPath, imgFrames.Count));
                    }
                    frameData = imgFrames[realFrameIndex];

                    if (frameData.LinkFrame != null)
                    {
                        throw new NpkException(
                                  "There is a link frame to another link frame which is not allowed. {0} frame {1} links to frame {2}."
                                  .F(imgPath, frameIndex, realFrameIndex));
                    }
                }

                NpkByteRange pixelDataLocation = m_frameLocations[imgPath][realFrameIndex];
                // Seek to the pixel data and read it
                Seek(pixelDataLocation.FileOffset, SeekOrigin.Begin);
                byte[] pixelData = new byte[pixelDataLocation.Size];
                m_npkStream.ReadOrDie(pixelData, pixelData.Length);

                if (frameData.IsCompressed)
                {
                    using (MemoryStream pixelDataMemoryStream = new MemoryStream(pixelData))
                    {
                        try
                        {
                            using (InflaterInputStream decompressStream = new InflaterInputStream(pixelDataMemoryStream))
                            {
                                byte[] decompressedPixelData = decompressStream.ReadFully();
                                pixelData = decompressedPixelData;
                            }
                        }
                        catch (SharpZipBaseException ex)
                        {
                            throw new NpkException(string.Format("Inflate error: {0}", ex.Message), ex);
                        }
                    }
                }

                pixelData = ExpandPixelData(pixelData, frameData);

                return(new Image(pixelData, frameData));
            }
            catch (EndOfStreamException ex)
            {
                throw new NpkException("Unexpected end of file.", ex);
            }
        }
예제 #3
0
        /// <summary>
        /// Loads a sprite file's metadata, setting its value in m_images and
        /// m_frameLocations. The .npk file is assumed to be open.
        /// </summary>
        /// <param name="lookForErrors">Do some extra checks to look for errors in the NPK reading code. For use by automated tests.</param>
        /// <exception cref="System.IO.IOException">I/O error.</exception>
        /// <exception cref="Dfo.Npk.NpkException">The .npk file appears to be corrupt or the format has changed.</exception>
        private void LoadSpriteFileMetaData(NpkPath spriteFilePath, NpkByteRange spriteFileLocation)
        {
            // If already loaded, return
            if (m_frames.ContainsKey(spriteFilePath))
            {
                return;
            }

            try
            {
                // Seek to the sprite file's location in the .npk
                Seek(spriteFileLocation.FileOffset, SeekOrigin.Begin);

                // .img files begin with "Neople Img File\0" in ASCII
                byte[] headerBuffer = new byte[s_imgHeaderBytes.Length];
                m_npkStream.ReadOrDie(headerBuffer, headerBuffer.Length);
                string headerString = Encoding.ASCII.GetString(headerBuffer);
                if (!string.Equals(s_imgHeaderString, headerString, StringComparison.Ordinal))
                {
                    throw new NpkException("Did not find expected image file header when reading {0}.".F(spriteFilePath));
                }

                // 32-bit unsigned int - this field seems to be 36 * (# non-link frames) + 8 * (# link frames). A scan of all NPKs bears this out.
                uint field1 = GetUnsigned32Le();

                // 32-bit unsigned int - this field seems to be always 0. A scan of all NPKs bears this out.
                uint field2 = GetUnsigned32Le();

                if (DoExtraErrorChecks && field2 != 0)
                {
                    OnErrorDetected(string.Format("Field 2 in {0} is {1}, not 0 as expected.", spriteFilePath, field2));
                }

                // 32-bit unsigned int - this field seems to be always 2 (some sort of version number perhaps?). A scan of all NPKs bears this out.
                uint field3 = GetUnsigned32Le();

                if (DoExtraErrorChecks && field3 != 2)
                {
                    OnErrorDetected(string.Format("Field 3 in {0} is {1}, not 2 as expected.", spriteFilePath, field3));
                }

                // 32-bit unsigned int - number of frames in the .img file
                uint numFrames = GetUnsigned32Le();

                List <FrameInfo>    frames         = new List <FrameInfo>((int)numFrames);
                List <NpkByteRange> frameLocations = new List <NpkByteRange>((int)numFrames);

                // Next is each frame's metadata, one after the other.
                for (uint frameIndex = 0; frameIndex < numFrames; frameIndex++)
                {
                    FrameInfo frame = ReadFrameMetadata();
                    frames.Add(frame);
                }

                if (DoExtraErrorChecks)
                {
                    int numLinkFrames       = frames.Where(f => f.LinkFrame != null).Count();
                    int numNonLinkFrames    = frames.Count - numLinkFrames;
                    int expectedField1Value = 36 * numNonLinkFrames + 8 * numLinkFrames;
                    if (field1 != expectedField1Value)
                    {
                        OnErrorDetected(string.Format("Field 1 in {0} is {1}, not {2} as expected.", spriteFilePath, field1, expectedField1Value));
                    }
                }

                // Next is each non-reference frame's pixel data, one after the other.
                int currentFramePosition = (int)m_npkStream.Position;
                for (uint frameIndex = 0; frameIndex < numFrames; frameIndex++)
                {
                    FrameInfo frame = frames[(int)frameIndex];
                    if (frame.LinkFrame != null)
                    {
                        // Link frames have no pixel data
                        // Could set this to referenced frame's data to simplify code elsewhere?
                        frameLocations.Add(new NpkByteRange(0, 0));
                        continue;
                    }

                    NpkByteRange frameByteRange;
                    if (frame.IsCompressed)
                    {
                        frameByteRange = new NpkByteRange(currentFramePosition, frame.CompressedLength);
                    }
                    else
                    {
                        int length = frame.Width * frame.Height * s_formatToBytesPerPixel[frame.PixelFormat];
                        frameByteRange = new NpkByteRange(currentFramePosition, length);
                    }

                    frameLocations.Add(frameByteRange);

                    currentFramePosition += frameByteRange.Size;

                    // No need to seek through the pixel data normally.
                    // Do it when doing extra error checks to verify that after the pixel data of all the frames
                    // is either another img file or EOF.
                    if (DoExtraErrorChecks)
                    {
                        Seek(frameByteRange.Size, SeekOrigin.Current);
                    }
                }

                if (DoExtraErrorChecks)
                {
                    // Check for invalid link frames
                    for (uint frameIndex = 0; frameIndex < numFrames; frameIndex++)
                    {
                        FrameInfo frame = frames[(int)frameIndex];
                        if (frame.LinkFrame != null && (frame.LinkFrame.Value >= numFrames || frame.LinkFrame.Value < 0))
                        {
                            OnErrorDetected("{0}, invalid link frame index from {1} to {2}.".F(spriteFilePath, frameIndex, frame.LinkFrame.Value));
                        }

                        if (frame.LinkFrame != null)
                        {
                            FrameInfo linkedFrame = frames[frame.LinkFrame.Value];
                            if (linkedFrame.LinkFrame != null)
                            {
                                OnErrorDetected("{0}, link frame to a link frame, {1} to {2}.".F(spriteFilePath, frameIndex, frame.LinkFrame.Value));
                            }
                        }
                    }

                    // Should be "Neople Img File" or EOF
                    byte[] nextImgHeaderBuf = new byte[15];
                    int    bytesRead        = m_npkStream.Read(nextImgHeaderBuf, 0, 15);
                    if (bytesRead == 0)
                    {
                        // EOF, we're ok
                    }
                    else if (bytesRead != 15)
                    {
                        OnErrorDetected(string.Format("{0}, {1} bytes read instead of 15 or 0.", spriteFilePath.Path, bytesRead));
                    }
                    else
                    {
                        string nextImgHeader = Encoding.ASCII.GetString(nextImgHeaderBuf);
                        if (nextImgHeader != "Neople Img File")
                        {
                            OnErrorDetected(string.Format("{0}, header is not Neople Img File.", spriteFilePath.Path));
                        }
                    }
                }

                m_frames[spriteFilePath]         = frames;
                m_frameLocations[spriteFilePath] = frameLocations;
            }
            catch (EndOfStreamException ex)
            {
                throw new NpkException("Unexpected end of file.", ex);
            }
        }
예제 #4
0
        /// <summary>
        /// Helper function that loads the first part of the .npk header. m_imagefileLocations and
        /// m_soundFileLocations are loaded when this function completes. The stream's read pointer will
        /// be right after the file table part of the header after completion. This function is only
        /// intended to be called from LoadNpkHeader().
        /// </summary>
        /// <exception cref="System.IO.EndOfStreamException">Unexpected end of file.</exception>
        /// <exception cref="System.IO.IOException">I/O error.</exception>
        /// <exception cref="DFO.Npk.NpkException">The file is corrupt or the format has changed.</exception>
        private void LoadNpkFileTable()
        {
            // file starts with "NeoplePack_Bill\0" in ASCII
            try
            {
                string dirListingHeader = "NeoplePack_Bill\0";
                byte[] headerBuffer     = new byte[dirListingHeader.Length];
                m_npkStream.ReadOrDie(headerBuffer, headerBuffer.Length);
                string headerString = Encoding.ASCII.GetString(headerBuffer);
                if (!string.Equals(dirListingHeader, headerString, StringComparison.Ordinal))
                {
                    throw new NpkException("Did not find expected directory listing header.");
                }

                // Next is a 32-bit unsigned int that is the number of files packed in the .npk
                uint numFiles = GetUnsigned32Le();
                _files = new List <NpkFileTableEntry>((int)numFiles);

                byte[] subNameBuffer = new byte[256];
                // Next is a listing of all the files and their location inside the file.
                for (uint fileIndex = 0; fileIndex < numFiles; fileIndex++)
                {
                    // First is a 32-bit unsigned int that is the byte offset in the .npk of where the file is located.
                    uint absoluteLocation = GetUnsigned32Le();
                    // Followed by the size of the file in bytes
                    uint size = GetUnsigned32Le();
                    // And then the path of the file, including the prefix indicating whether it is an image
                    // (sprite/) or a sound (sounds/)
                    // There are always 256 bytes to be read here.
                    // Each byte read is XOR'ed with the corresponding byte in the key.
                    // Then the bytes can be treated as a null-terminated ASCII string.
                    m_npkStream.ReadOrDie(subNameBuffer, subNameBuffer.Length);

                    for (int keyIndex = 0; keyIndex < subNameBuffer.Length; keyIndex++)
                    {
                        subNameBuffer[keyIndex] ^= s_key[keyIndex];
                    }

                    string subNameString = Encoding.ASCII.GetString(subNameBuffer);
                    subNameString = subNameString.TrimEnd('\0');
                    NpkPath pathWithPrefix = new NpkPath(subNameString);

                    _files.Add(new NpkFileTableEntry(pathWithPrefix, new NpkByteRange((int)absoluteLocation, (int)size)));

                    // That gives a path like sprite/character/gunner/effect/aerialdashattack.img
                    IList <NpkPath> pathComponents = pathWithPrefix.GetPathComponents();
                    if (pathComponents.Count >= 1)
                    {
                        NpkByteRange fileLocation = new NpkByteRange((int)absoluteLocation, (int)size);
                        if (pathComponents[0].Equals("sprite"))
                        {
                            m_imageFileLocations[pathWithPrefix] = fileLocation;
                            m_imagesInFile[pathWithPrefix]       = true;
                        }
                        else if (pathComponents[0].Equals("sounds"))
                        {
                            m_soundFileLocations[pathWithPrefix] = fileLocation;
                        }
                        else
                        {
                            // Not an image or a sound. Ignore it I guess, no sense throwing an exception.
                            // Don't break any programs just because a new file type was added or something.
                            OnErrorDetected("Something other than a sprite or sounds file at packed file index {0}: {1}".F(fileIndex, pathComponents[0]));
                        }
                    }
                    else
                    {
                        // empty path? O_o Ignore it I guess.
                        OnErrorDetected("Empty path at packed file index {0}.".F(fileIndex));
                    }
                }
            }
            catch (EndOfStreamException ex)
            {
                throw new NpkException("Unexpected end of file.", ex);
            }
        }
예제 #5
0
 public NpkFileTableEntry(NpkPath name, NpkByteRange location)
 {
     _name     = name;
     _location = location;
 }