Example #1
0
        private static Bitmap ConvertIndicesToRgbaBitmap(int width, int height, JJ2Block block, bool removeShadow)
        {
            byte[] data = block.AsByteArray();

            Bitmap result = new Bitmap(width, height, PixelFormat.Format32bppArgb);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int index = data[y * width + x];
                    // Use menu palette here
                    ColorRgba color;
                    if (removeShadow && (index == 63 || index == 143))
                    {
                        // Remove original shadow pixels
                        color = new ColorRgba(0);
                    }
                    else
                    {
                        color = JJ2DefaultPalette.Menu[index];
                    }

                    result.SetPixel(x, y, Color.FromArgb(color.A, color.R, color.G, color.B));
                }
            }

            return(result);
        }
Example #2
0
        private void LoadImageData(JJ2Block imageBlock, JJ2Block alphaBlock)
        {
            const int BlockSize = 32;

            for (int i = 0; i < tiles.Length; i++)
            {
                ref TilesetTileSection tile = ref tiles[i];
                tile.Image = new Bitmap(BlockSize, BlockSize);

                byte[] imageData     = imageBlock.ReadRawBytes(BlockSize * BlockSize, tile.ImageDataOffset);
                byte[] alphaMaskData = alphaBlock.ReadRawBytes(128, tile.AlphaDataOffset);
                for (int j = 0; j < (BlockSize * BlockSize); j++)
                {
                    byte  idx = imageData[j];
                    Color color;
                    if (alphaMaskData.Length > 0 && ((alphaMaskData[j / 8] >> (j % 8)) & 0x01) == 0x00)
                    {
                        //color = Color.Transparent;
                        color = JJ2DefaultPalette.ByIndex[0];
                    }
                    else
                    {
                        //color = palette[idx];
                        color = JJ2DefaultPalette.ByIndex[idx];
                    }

                    tile.Image.SetPixel(j % BlockSize, j / BlockSize, color);
                }
            }
Example #3
0
        public static JJ2DataFile Open(string path, bool strictParser)
        {
            using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                using (BinaryReader r = new BinaryReader(s)) {
                    JJ2DataFile j2d = new JJ2DataFile();

                    uint magic = r.ReadUInt32();
                    if (magic != 0x42494C50 /*PLIB*/)
                    {
                        throw new InvalidOperationException("Invalid magic string");
                    }

                    uint signature = r.ReadUInt32();
                    if (signature != 0xBEBAADDE)
                    {
                        throw new InvalidOperationException("Invalid signature");
                    }

                    uint version = r.ReadUInt32();

                    uint recordedSize = r.ReadUInt32();
                    if (strictParser && s.Length != recordedSize)
                    {
                        throw new InvalidOperationException("Unexpected file size");
                    }

                    uint recordedCRC             = r.ReadUInt32();
                    int  headerBlockPackedSize   = r.ReadInt32();
                    int  headerBlockUnpackedSize = r.ReadInt32();

                    JJ2Block headerBlock = new JJ2Block(s, headerBlockPackedSize, headerBlockUnpackedSize);

                    try {
                        while (true)
                        {
                            string name = headerBlock.ReadString(32, true);

                            uint type             = headerBlock.ReadUInt32();
                            uint offset           = headerBlock.ReadUInt32();
                            uint fileCRC          = headerBlock.ReadUInt32();
                            int  filePackedSize   = headerBlock.ReadInt32();
                            int  fileUnpackedSize = headerBlock.ReadInt32();

                            //Console.WriteLine(name + " | " + type.ToString("X") + " | " + fileUnpackedSize + " | " + offset);

                            s.Position = offset;
                            JJ2Block fileBlock = new JJ2Block(s, filePackedSize, fileUnpackedSize);
                            byte[]   data      = fileBlock.AsByteArray();
                        }
                    } catch (EndOfStreamException) {
                        // End of file list
                    }

                    // ToDo: Extract files, but it's not needed for now...

                    return(j2d);
                }
        }
Example #4
0
        private void LoadMetadata(JJ2Block block)
        {
            palette = new Color[256];

            for (int i = 0; i < 256; i++)
            {
                byte red   = block.ReadByte();
                byte green = block.ReadByte();
                byte blue  = block.ReadByte();
                byte alpha = block.ReadByte();
                palette[i] = Color.FromArgb(255 - alpha, red, green, blue);
            }

            tileCount = block.ReadInt32();

            int maxTiles = MaxSupportedTiles;

            tiles = new TilesetTileSection[maxTiles];

            for (int i = 0; i < maxTiles; ++i)
            {
                tiles[i].Opaque = block.ReadBool();
            }

            // Block of unknown values, skip
            block.DiscardBytes(maxTiles);

            for (int i = 0; i < maxTiles; ++i)
            {
                tiles[i].ImageDataOffset = block.ReadUInt32();
            }

            // Block of unknown values, skip
            block.DiscardBytes(4 * maxTiles);

            for (int i = 0; i < maxTiles; ++i)
            {
                tiles[i].AlphaDataOffset = block.ReadUInt32();
            }

            // Block of unknown values, skip
            block.DiscardBytes(4 * maxTiles);

            for (int i = 0; i < maxTiles; ++i)
            {
                tiles[i].MaskDataOffset = block.ReadUInt32();
            }

            // We don't care about the flipped masks, those are generated on runtime
            block.DiscardBytes(4 * maxTiles);
        }
Example #5
0
        public static void Convert(string path, string targetPath, bool isPlus)
        {
            JJ2Version              version;
            RawList <AnimSection>   anims   = new RawList <AnimSection>();
            RawList <SampleSection> samples = new RawList <SampleSection>();

            using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                using (BinaryReader r = new BinaryReader(s)) {
                    Log.Write(LogType.Info, "Reading compressed stream...");
                    Log.PushIndent();

                    bool seemsLikeCC = false;

                    uint magic = r.ReadUInt32();
                    if (magic != 0x42494C41 /*ALIB*/)
                    {
                        throw new InvalidOperationException("Invalid magic string");
                    }

                    uint signature = r.ReadUInt32();
                    if (signature != 0x00BEBA00)
                    {
                        throw new InvalidOperationException("Invalid signature");
                    }

                    uint headerLen = r.ReadUInt32();

                    uint magicUnknown = r.ReadUInt32();
                    if (magicUnknown != 0x18080200)
                    {
                        throw new InvalidOperationException("Invalid magic number");
                    }

                    uint   fileLen      = r.ReadUInt32();
                    uint   crc          = r.ReadUInt32();
                    int    setCnt       = r.ReadInt32();
                    uint[] setAddresses = new uint[setCnt];

                    for (uint i = 0; i < setCnt; i++)
                    {
                        setAddresses[i] = r.ReadUInt32();
                    }

                    if (headerLen != s.Position)
                    {
                        throw new InvalidOperationException("Header size mismatch");
                    }

                    // Read content
                    bool isStreamComplete = true;
                    {
                        int i = 0;
                        try {
                            for (; i < setCnt; i++)
                            {
                                uint   magicANIM           = r.ReadUInt32();
                                byte   animCount           = r.ReadByte();
                                byte   sndCount            = r.ReadByte();
                                ushort frameCount          = r.ReadUInt16();
                                uint   cumulativeSndIndex  = r.ReadUInt32();
                                int    infoBlockLenC       = r.ReadInt32();
                                int    infoBlockLenU       = r.ReadInt32();
                                int    frameDataBlockLenC  = r.ReadInt32();
                                int    frameDataBlockLenU  = r.ReadInt32();
                                int    imageDataBlockLenC  = r.ReadInt32();
                                int    imageDataBlockLenU  = r.ReadInt32();
                                int    sampleDataBlockLenC = r.ReadInt32();
                                int    sampleDataBlockLenU = r.ReadInt32();

                                JJ2Block infoBlock       = new JJ2Block(s, infoBlockLenC, infoBlockLenU);
                                JJ2Block frameDataBlock  = new JJ2Block(s, frameDataBlockLenC, frameDataBlockLenU);
                                JJ2Block imageDataBlock  = new JJ2Block(s, imageDataBlockLenC, imageDataBlockLenU);
                                JJ2Block sampleDataBlock = new JJ2Block(s, sampleDataBlockLenC, sampleDataBlockLenU);

                                if (magicANIM != 0x4D494E41)
                                {
                                    Log.Write(LogType.Warning, "Header for set " + i + " is incorrect (bad magic value)! Skipping the subfile.");
                                    continue;
                                }

                                List <AnimSection> setAnims = new List <AnimSection>();

                                for (ushort j = 0; j < animCount; j++)
                                {
                                    AnimSection anim = new AnimSection();
                                    anim.Set        = i;
                                    anim.Anim       = j;
                                    anim.FrameCount = infoBlock.ReadUInt16();
                                    anim.FrameRate  = infoBlock.ReadUInt16();
                                    anim.Frames     = new AnimFrameSection[anim.FrameCount];

                                    // Skip the rest, seems to be 0x00000000 for all headers
                                    infoBlock.DiscardBytes(4);

                                    anims.Add(anim);
                                    setAnims.Add(anim);
                                }

                                if (i == 65 && setAnims.Count > 5)
                                {
                                    seemsLikeCC = true;
                                }

                                if (frameCount > 0)
                                {
                                    if (setAnims.Count == 0)
                                    {
                                        throw new InvalidOperationException("Set has frames but no anims");
                                    }

                                    short lastColdspotX = 0, lastColdspotY = 0;
                                    short lastHotspotX = 0, lastHotspotY = 0;
                                    short lastGunspotX = 0, lastGunspotY = 0;

                                    AnimSection currentAnim = setAnims[0];
                                    ushort      currentAnimIdx = 0, currentFrame = 0;

                                    for (ushort j = 0; j < frameCount; j++)
                                    {
                                        if (currentFrame >= currentAnim.FrameCount)
                                        {
                                            currentAnim  = setAnims[++currentAnimIdx];
                                            currentFrame = 0;

                                            while (currentAnim.FrameCount == 0 && currentAnimIdx < setAnims.Count)
                                            {
                                                currentAnim = setAnims[++currentAnimIdx];
                                            }
                                        }

                                        ref AnimFrameSection frame = ref currentAnim.Frames[currentFrame];

                                        frame.SizeX     = frameDataBlock.ReadInt16();
                                        frame.SizeY     = frameDataBlock.ReadInt16();
                                        frame.ColdspotX = frameDataBlock.ReadInt16();
                                        frame.ColdspotY = frameDataBlock.ReadInt16();
                                        frame.HotspotX  = frameDataBlock.ReadInt16();
                                        frame.HotspotY  = frameDataBlock.ReadInt16();
                                        frame.GunspotX  = frameDataBlock.ReadInt16();
                                        frame.GunspotY  = frameDataBlock.ReadInt16();

                                        frame.ImageAddr = frameDataBlock.ReadInt32();
                                        frame.MaskAddr  = frameDataBlock.ReadInt32();

                                        // Adjust normalized position
                                        // In the output images, we want to make the hotspot and image size constant.
                                        currentAnim.NormalizedHotspotX = (short)Math.Max(-frame.HotspotX, currentAnim.NormalizedHotspotX);
                                        currentAnim.NormalizedHotspotY = (short)Math.Max(-frame.HotspotY, currentAnim.NormalizedHotspotY);

                                        currentAnim.LargestOffsetX = (short)Math.Max(frame.SizeX + frame.HotspotX, currentAnim.LargestOffsetX);
                                        currentAnim.LargestOffsetY = (short)Math.Max(frame.SizeY + frame.HotspotY, currentAnim.LargestOffsetY);

                                        currentAnim.AdjustedSizeX = (short)Math.Max(
                                            currentAnim.NormalizedHotspotX + currentAnim.LargestOffsetX,
                                            currentAnim.AdjustedSizeX
                                            );
                                        currentAnim.AdjustedSizeY = (short)Math.Max(
                                            currentAnim.NormalizedHotspotY + currentAnim.LargestOffsetY,
                                            currentAnim.AdjustedSizeY
                                            );

#if DEBUG
                                        if (currentFrame > 0)
                                        {
                                            int diffPrevX, diffPrevY, diffNextX, diffNextY;

                                            if (frame.ColdspotX != 0 && frame.ColdspotY != 0)
                                            {
                                                diffPrevX = (lastColdspotX - lastHotspotX);
                                                diffPrevY = (lastColdspotY - lastHotspotY);
                                                diffNextX = (frame.ColdspotX - frame.HotspotX);
                                                diffNextY = (frame.ColdspotY - frame.HotspotY);

                                                if (diffPrevX != diffNextX || diffPrevY != diffNextY)
                                                {
                                                    Log.Write(LogType.Warning, "Animation " + currentAnim.Anim + " coldspots in set " + currentAnim.Set + " are different!");
                                                    Log.PushIndent();
                                                    Log.Write(LogType.Warning, "Frame #" + (currentFrame - 1) + ": " + diffPrevX + "," + diffPrevY + "  |  " + "Frame #" + currentFrame + ": " + diffNextX + "," + diffNextY);
                                                    Log.PopIndent();
                                                }
                                            }

                                            if (frame.GunspotX != 0 && frame.GunspotY != 0)
                                            {
                                                diffPrevX = (lastGunspotX - lastHotspotX);
                                                diffPrevY = (lastGunspotY - lastHotspotY);
                                                diffNextX = (frame.GunspotX - frame.HotspotX);
                                                diffNextY = (frame.GunspotY - frame.HotspotY);

                                                if (diffPrevX != diffNextX || diffPrevY != diffNextY)
                                                {
                                                    Log.Write(LogType.Warning, "Animation " + currentAnim.Anim + " gunspots in set " + currentAnim.Set + " are different!");
                                                    Log.PushIndent();
                                                    Log.Write(LogType.Warning, "Frame #" + (currentFrame - 1) + ": " + diffPrevX + "," + diffPrevY + "  |  " + "Frame #" + currentFrame + ": " + diffNextX + "," + diffNextY);
                                                    Log.PopIndent();
                                                }
                                            }
                                        }
#endif

                                        lastColdspotX = frame.ColdspotX; lastColdspotY = frame.ColdspotY;
                                        lastHotspotX  = frame.HotspotX; lastHotspotY = frame.HotspotY;
                                        lastGunspotX  = frame.GunspotX; lastGunspotY = frame.GunspotY;

                                        currentFrame++;
                                    }

                                    // Read the image data for each animation frame
                                    for (ushort j = 0; j < setAnims.Count; j++)
                                    {
                                        AnimSection anim = setAnims[j];

                                        if (anim.FrameCount < anim.Frames.Length)
                                        {
                                            Log.Write(LogType.Error, "Animation " + j + " frame count in set " + i + " doesn't match! Expected "
                                                      + anim.FrameCount + " frames, but read " + anim.Frames.Length + " instead.");

                                            throw new InvalidOperationException();
                                        }

                                        for (ushort frame = 0; frame < anim.FrameCount; ++frame)
                                        {
                                            int dpos = (anim.Frames[frame].ImageAddr + 4);

                                            imageDataBlock.SeekTo(dpos - 4);
                                            ushort width2 = imageDataBlock.ReadUInt16();
                                            imageDataBlock.SeekTo(dpos - 2);
                                            ushort height2 = imageDataBlock.ReadUInt16();

                                            ref AnimFrameSection frameData = ref anim.Frames[frame];
                                            frameData.DrawTransparent = (width2 & 0x8000) > 0;

                                            int  pxRead      = 0;
                                            int  pxTotal     = (frameData.SizeX * frameData.SizeY);
                                            bool lastOpEmpty = true;

                                            List <byte> imageData = new List <byte>(pxTotal);

                                            while (pxRead < pxTotal)
                                            {
                                                if (dpos > 0x10000000)
                                                {
                                                    Log.Write(LogType.Error, "Loading of animation " + j + " in set " + i + " failed! Aborting.");
                                                    break;
                                                }
                                                imageDataBlock.SeekTo(dpos);
                                                byte op = imageDataBlock.ReadByte();
                                                //if (op == 0) {
                                                //    Console.WriteLine("[" + i + ":" + j + "] Next image operation should probably not be 0x00.");
                                                //}

                                                if (op < 0x80)
                                                {
                                                    // Skip the given number of pixels, writing them with the transparent color 0
                                                    pxRead += op;
                                                    while (op-- > 0)
                                                    {
                                                        imageData.Add((byte)0x00);
                                                    }
                                                    dpos++;
                                                }
                                                else if (op == 0x80)
                                                {
                                                    // Skip until the end of the line
                                                    ushort linePxLeft = (ushort)(frameData.SizeX - pxRead % frameData.SizeX);
                                                    if (pxRead % anim.Frames[frame].SizeX == 0 && !lastOpEmpty)
                                                    {
                                                        linePxLeft = 0;
                                                    }

                                                    pxRead += linePxLeft;
                                                    while (linePxLeft-- > 0)
                                                    {
                                                        imageData.Add((byte)0x00);
                                                    }
                                                    dpos++;
                                                }
                                                else
                                                {
                                                    // Copy specified amount of pixels (ignoring the high bit)
                                                    ushort bytesToRead = (ushort)(op & 0x7F);
                                                    imageDataBlock.SeekTo(dpos + 1);
                                                    byte[] nextData = imageDataBlock.ReadRawBytes(bytesToRead);
                                                    imageData.AddRange(nextData);
                                                    pxRead += bytesToRead;
                                                    dpos   += bytesToRead + 1;
                                                }

                                                lastOpEmpty = (op == 0x80);
                                            }

                                            frameData.ImageData = imageData.ToArray();

                                            frameData.MaskData = new BitArray(pxTotal, false);
                                            dpos   = frameData.MaskAddr;
                                            pxRead = 0;

                                            // No mask
                                            if (dpos == unchecked ((int)0xFFFFFFFF))
                                            {
                                                continue;
                                            }

                                            while (pxRead < pxTotal)
                                            {
                                                imageDataBlock.SeekTo(dpos);
                                                byte b = imageDataBlock.ReadByte();
                                                for (byte bit = 0; bit < 8 && (pxRead + bit) < pxTotal; ++bit)
                                                {
                                                    frameData.MaskData[pxRead + bit] = ((b & (1 << (7 - bit))) != 0);
                                                }
                                                pxRead += 8;
                                            }
                                        }
                                    }
                                }

                                for (ushort j = 0; j < sndCount; ++j)
                                {
                                    SampleSection sample;
                                    //sample.Id = (ushort)(cumulativeSndIndex + j);
                                    sample.IdInSet = j;
                                    sample.Set     = i;

                                    int  totalSize = sampleDataBlock.ReadInt32();
                                    uint magicRIFF = sampleDataBlock.ReadUInt32();
                                    int  chunkSize = sampleDataBlock.ReadInt32();
                                    // "ASFF" for 1.20, "AS  " for 1.24
                                    uint format = sampleDataBlock.ReadUInt32();
                                    bool isASFF = (format == 0x46465341);

                                    uint magicSAMP = sampleDataBlock.ReadUInt32();
                                    uint sampSize  = sampleDataBlock.ReadUInt32();
                                    // Padding/unknown data #1
                                    // For set 0 sample 0:
                                    //       1.20                           1.24
                                    //  +00  00 00 00 00 00 00 00 00   +00  40 00 00 00 00 00 00 00
                                    //  +08  00 00 00 00 00 00 00 00   +08  00 00 00 00 00 00 00 00
                                    //  +10  00 00 00 00 00 00 00 00   +10  00 00 00 00 00 00 00 00
                                    //  +18  00 00 00 00               +18  00 00 00 00 00 00 00 00
                                    //                                 +20  00 00 00 00 00 40 FF 7F
                                    sampleDataBlock.DiscardBytes(40 - (isASFF ? 12 : 0));
                                    if (isASFF)
                                    {
                                        // All 1.20 samples seem to be 8-bit. Some of them are among those
                                        // for which 1.24 reads as 24-bit but that might just be a mistake.
                                        sampleDataBlock.DiscardBytes(2);

                                        sample.Multiplier = 0;
                                    }
                                    else
                                    {
                                        // for 1.24. 1.20 has "20 40" instead in s0s0 which makes no sense
                                        sample.Multiplier = sampleDataBlock.ReadUInt16();
                                    }
                                    // Unknown. s0s0 1.20: 00 80, 1.24: 80 00
                                    sampleDataBlock.DiscardBytes(2);

                                    uint payloadSize = sampleDataBlock.ReadUInt32();
                                    // Padding #2, all zeroes in both
                                    sampleDataBlock.DiscardBytes(8);

                                    sample.SampleRate = sampleDataBlock.ReadUInt32();
                                    int actualDataSize = chunkSize - 76 + (isASFF ? 12 : 0);

                                    sample.Data = sampleDataBlock.ReadRawBytes(actualDataSize);
                                    // Padding #3
                                    sampleDataBlock.DiscardBytes(4);

                                    if (magicRIFF != 0x46464952 || magicSAMP != 0x504D4153)
                                    {
                                        throw new InvalidOperationException("Sample has invalid header");
                                    }

                                    if (sample.Data.Length < actualDataSize)
                                    {
                                        Log.Write(LogType.Warning, "Sample " + j + " in set " + i + " was shorter than expected! Expected "
                                                  + actualDataSize + " bytes, but read " + sample.Data.Length + " instead.");
                                    }

                                    if (totalSize > chunkSize + 12)
                                    {
                                        // Sample data is probably aligned to X bytes since the next sample doesn't always appear right after the first ends.
                                        Log.Write(LogType.Warning, "Adjusting read offset of sample " + j + " in set " + i + " by " + (totalSize - chunkSize - 12) + " bytes.");

                                        sampleDataBlock.DiscardBytes(totalSize - chunkSize - 12);
                                    }

                                    samples.Add(sample);
                                }
                            }
                        } catch (EndOfStreamException) {
Example #6
0
        public static JJ2Episode Open(string path)
        {
            using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                using (BinaryReader r = new BinaryReader(s)) {
                    JJ2Episode episode = new JJ2Episode();

                    episode.episodeToken = Path.GetFileNameWithoutExtension(path).ToLower(CultureInfo.InvariantCulture);

                    // ToDo: Implement JJ2+ extended data, but I haven't seen it anywhere yet
                    // the condition of unlocking (currently only defined for 0 meaning "always unlocked"
                    // and 1 meaning "requires the previous episode to be finished", stored as a 4-byte-long
                    // integer starting at byte 0x4), binary flags of various purpose (currently supported
                    // flags are 1 and 2 used to reset respectively player ammo and lives when the episode
                    // begins; stored as a 4-byte-long integer starting at byte 0x8), file name of the preceding
                    // episode (used mostly to determine whether the episode should be locked, stored
                    // as a 32-byte-long chain of characters starting at byte 0x4C), file name of the following
                    // episode (that is cycled to after the episode ends, stored as a 32-byte-long
                    // chain of characters starting at byte 0x6C)

                    // Header (208 bytes)
                    int headerSize = r.ReadInt32();
                    episode.position     = r.ReadInt32();
                    episode.isRegistered = (r.ReadInt32() != 0);
                    int unknown1 = r.ReadInt32();

                    // Episode name
                    {
                        byte[] episodeNameRaw = r.ReadBytes(128);
                        episode.episodeName = Encoding.ASCII.GetString(episodeNameRaw);
                        int i = episode.episodeName.IndexOf('\0');
                        if (i != -1)
                        {
                            episode.episodeName = episode.episodeName.Substring(0, i);
                        }
                    }
                    // First level
                    {
                        byte[] firstLevelRaw = r.ReadBytes(32);
                        episode.firstLevel = Encoding.ASCII.GetString(firstLevelRaw);
                        int i = episode.firstLevel.IndexOf('\0');
                        if (i != -1)
                        {
                            episode.firstLevel = episode.firstLevel.Substring(0, i);
                        }
                    }

                    // ToDo: Episode images are not supported yet
                    int width    = r.ReadInt32();
                    int height   = r.ReadInt32();
                    int unknown2 = r.ReadInt32();
                    int unknown3 = r.ReadInt32();

                    int titleWidth  = r.ReadInt32();
                    int titleHeight = r.ReadInt32();
                    int unknown4    = r.ReadInt32();
                    int unknown5    = r.ReadInt32();

                    {
                        int      imagePackedSize   = r.ReadInt32();
                        int      imageUnpackedSize = width * height;
                        JJ2Block imageBlock        = new JJ2Block(s, imagePackedSize, imageUnpackedSize);
                        //episode.image = ConvertIndicesToRgbaBitmap(width, height, imageBlock, false);
                    }
                    {
                        int      titleLightPackedSize   = r.ReadInt32();
                        int      titleLightUnpackedSize = titleWidth * titleHeight;
                        JJ2Block titleLightBlock        = new JJ2Block(s, titleLightPackedSize, titleLightUnpackedSize);
                        episode.titleLight = ConvertIndicesToRgbaBitmap(titleWidth, titleHeight, titleLightBlock, true);
                    }
                    //{
                    //    int titleDarkPackedSize = r.ReadInt32();
                    //    int titleDarkUnpackedSize = titleWidth * titleHeight;
                    //    JJ2Block titleDarkBlock = new JJ2Block(s, titleDarkPackedSize, titleDarkUnpackedSize);
                    //    episode.titleDark = ConvertIndicesToRgbaBitmap(titleWidth, titleHeight, titleDarkBlock, true);
                    //}

                    return(episode);
                }
        }
Example #7
0
        public static JJ2Tileset Open(string path, bool strictParser)
        {
            using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) {
                // Skip copyright notice
                s.Seek(180, SeekOrigin.Current);

                JJ2Tileset tileset = new JJ2Tileset();

                JJ2Block headerBlock = new JJ2Block(s, 262 - 180);

                uint magic = headerBlock.ReadUInt32();
                if (magic != 0x454C4954 /*TILE*/)
                {
                    throw new InvalidOperationException("Invalid magic string");
                }

                uint signature = headerBlock.ReadUInt32();
                if (signature != 0xAFBEADDE)
                {
                    throw new InvalidOperationException("Invalid signature");
                }

                tileset.name = headerBlock.ReadString(32, true);

                ushort versionNum = headerBlock.ReadUInt16();
                tileset.version = (versionNum <= 512 ? JJ2Version.BaseGame : JJ2Version.TSF);

                int recordedSize = headerBlock.ReadInt32();
                if (strictParser && s.Length != recordedSize)
                {
                    throw new InvalidOperationException("Unexpected file size");
                }

                // Get the CRC; would check here if it matches if we knew what variant it is AND what it applies to
                // Test file across all CRC32 variants + Adler had no matches to the value obtained from the file
                // so either the variant is something else or the CRC is not applied to the whole file but on a part
                int recordedCRC = headerBlock.ReadInt32();

                // Read the lengths, uncompress the blocks and bail if any block could not be uncompressed
                // This could look better without all the copy-paste, but meh.
                int infoBlockPackedSize    = headerBlock.ReadInt32();
                int infoBlockUnpackedSize  = headerBlock.ReadInt32();
                int imageBlockPackedSize   = headerBlock.ReadInt32();
                int imageBlockUnpackedSize = headerBlock.ReadInt32();
                int alphaBlockPackedSize   = headerBlock.ReadInt32();
                int alphaBlockUnpackedSize = headerBlock.ReadInt32();
                int maskBlockPackedSize    = headerBlock.ReadInt32();
                int maskBlockUnpackedSize  = headerBlock.ReadInt32();

                JJ2Block infoBlock  = new JJ2Block(s, infoBlockPackedSize, infoBlockUnpackedSize);
                JJ2Block imageBlock = new JJ2Block(s, imageBlockPackedSize, imageBlockUnpackedSize);
                JJ2Block alphaBlock = new JJ2Block(s, alphaBlockPackedSize, alphaBlockUnpackedSize);
                JJ2Block maskBlock  = new JJ2Block(s, maskBlockPackedSize, maskBlockUnpackedSize);

                tileset.LoadMetadata(infoBlock);
                tileset.LoadImageData(imageBlock, alphaBlock);
                tileset.LoadMaskData(maskBlock);

                return(tileset);
            }
        }