Exemplo n.º 1
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);
            }
        }
Exemplo n.º 2
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);
            }
        }
Exemplo n.º 3
0
 public NpkFileTableEntry(NpkPath name, NpkByteRange location)
 {
     _name = name;
     _location = location;
 }