Beispiel #1
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);
                }
            }
Beispiel #2
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) {