Пример #1
0
        public static byte[] Compress(ByteArrayStream target)
        {
            if (target.Address != 0)
            {
                throw new ArgumentException($"{nameof(target)} address must be 0");
            }
            if (target.Size % 2 != 0)
            {
                throw new ArgumentException($"size is wrong ({target.Size.ToString("x2")}), {nameof(target)} needs to be in 2 byte chunks (words)");
            }

            var output = new List <byte>(target.Size / 2);

            while (!target.AtEnd)
            {
                var low  = target.Byte();
                var high = target.Byte();

                if ((low & 0x07) != low)
                {
                    throw new Exception($"{nameof(low)} byte should be lower three bits only. value: ${low.ToString("x2")}  address: ${(target.Address - 2).ToString("x2")}");
                }
                if ((high & 0x07) != low)
                {
                    throw new Exception($"{nameof(high)} byte should be lower three bits only. value: ${high.ToString("x2")}  address: ${(target.Address - 1).ToString("x2")}");
                }

                var data = (byte)(low + (high << 4));
                output.Add(data);
            }

            return(output.ToArray());
        }
        public static byte[] Compress(ByteArrayStream target)
        {
            if (target.Address != 0)
            {
                throw new ArgumentException($"{nameof(target)} address must be 0");
            }
            if (target.Size % 0x20 != 0)
            {
                throw new ArgumentException($"size is wrong ({target.Size.ToString("x2")}), {nameof(target)} needs to be in full $20 byte chunks");
            }

            var output = new List <byte>(target.Size * 3 / 4);

            while (!target.AtEnd)
            {
                output.Add(target.Byte());

                if ((target.Address % 0x20) >= 0x10)
                {
                    var zero = target.Byte();

                    if (zero != 0)
                    {
                        throw new Exception($"byte should be zero. value: ${zero.ToString("x2")}  address: ${(target.Address - 1).ToString("x4")}");
                    }
                }
            }

            return(output.ToArray());
        }
Пример #3
0
        public static byte[] Decompress(ByteArrayStream source, int outputSize)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            var          output   = new ByteArrayStream(outputSize);
            var          work     = new ByteRingBuffer(RingSize, StartWriteAddress);
            Queue <bool> commands = source.Byte().ToBooleanQueue();

            while (output.HasSpace)
            {
                if (commands.Count == 0)
                {
                    commands = source.Byte().ToBooleanQueue();
                }

                if (commands.Dequeue())
                {
                    var b = source.Byte();

                    work.Byte(b);
                    output.Byte(b);
                }
                else
                {
                    var d1 = source.Byte();
                    var d2 = source.Byte();

                    var address    = d1 + ((d2 << 2) & 0x0300);
                    var counter    = (d2 & 0x3f) + 3;
                    var copySource = work.Branch(address);

                    while (output.HasSpace && (counter != 0))
                    {
                        var copy = copySource.Byte();
                        counter--;

                        work.Byte(copy);
                        output.Byte(copy);
                    }
                }
            }

            return(output.Buffer);
        }
Пример #4
0
        public static byte[] Decompress(ByteArrayStream source, int compressedSize)
        {
            var output = new List <byte>(compressedSize * 2);

            for (int i = 0; i < compressedSize; i++)
            {
                var data = source.Byte();
                output.Add((byte)(data & 0x07));
                output.Add((byte)((data >> 4) & 0x07));
            }

            return(output.ToArray());
        }
        public static (byte[] comp, byte[] decomp) DecompressFull(ByteArrayStream source, int outputSize)
        {
            var startAddress     = source.Address;
            var dataSourceOffset = source.Word();
            var dataSource       = source.Branch(source.Address + dataSourceOffset);

            var output = new ByteArrayStream(outputSize);

            byte command;

            while ((command = source.Byte()) != 0x00)
            {
                if ((command & 0x0f) != 0)
                {
                    var length = command & 0x0f;
                    dataSource.CopyTo(output, length);
                }

                if ((command & 0xf0) != 0)
                {
                    var length = ((command & 0xf0) >> 4) + 2;

                    var address = output.Address - source.Byte() - 1;
                    if (address < 0)
                    {
                        throw new IndexOutOfRangeException($"{nameof(address)} cannot be less than 0");
                    }

                    output.Branch(address).CopyTo(output, length);
                }
            }

            var comp   = source.GetBytes(dataSource.Address - startAddress, startAddress);
            var decomp = output.GetBytes(output.Address, 0);

            return(comp, decomp);
        }
Пример #6
0
        public static string AttemptTranslateLine(ByteArrayStream stream)
        {
            var line = new List <string>();

            while (stream.ByteAt(0) != BasicTable.EndOfString)
            {
                line.Add(AttemptTranslate(stream));
            }

            var text = string.Join("", line);

            stream.Byte();

            return(text);
        }
Пример #7
0
        public static string AttemptTranslate(ByteArrayStream stream, int depthLeft = 2)
        {
            var code = stream.Byte();

            if (code >= 0x80)
            {
                return(BasicTable.Lookup(code));
            }

            if (code < 0x30)
            {
                if (code == 0x01)
                {
                    return($"{{windowbreak}}");
                }
                if (code == 0x05)
                {
                    return($"{{05:{stream.Byte().ToString("x2")}}}");
                }
                if (code == 0x1b)
                {
                    return($"{{swapspeaker:{stream.Byte().ToString("x2")}}}");
                }
                if (code == 0x1d)
                {
                    return($"{{character:{CharacterNames.GetString(stream.Byte())}}}");
                }
                if (code == 0x1e)
                {
                    return($"{{item:{ItemNames.GetString(stream.Byte())}}}");
                }
                if (code == 0x1f)
                {
                    return($"{{location:{LocationNames.GetString(stream.Byte())}}}");
                }
                //if (code == 0x2f) {
                //	return $"{{if:{stream.Byte().ToString("x2")} {stream.Byte().ToString("x2")} {stream.Byte().ToString("x2")}}}";
                //}


                return($"{{{code.ToString("x2")}}}");
            }

            if (depthLeft == 0)
            {
                return($"{{{code.ToString("x2")}}}");
            }

            //var text = string.Join("", LookupBytes(code).Select(x => AttemptTranslate(x, depthLeft - 1)));
            var text = AttemptTranslateLine(LookupBytes(code), depthLeft - 1);

            return(text);
        }
Пример #8
0
        public static string AttemptTranslateLine(byte[] input, int depthLeft = 3)
        {
            var line   = new List <string>();
            var stream = new ByteArrayStream(input);

            while (!stream.AtEnd)
            {
                line.Add(AttemptTranslate(stream, depthLeft));
            }

            var text = string.Join("", line);

            if (!stream.AtEnd)
            {
                stream.Byte();
            }

            return(text);
        }
        public static byte[] Decompress(ByteArrayStream source, int compressedSize)
        {
            if (compressedSize % 0x18 != 0)
            {
                throw new ArgumentException($"{nameof(compressedSize)} is wrong ({compressedSize.ToString("x2")}), {nameof(source)} needs to be in full $18 byte chunks");
            }

            var output = new List <byte>(compressedSize * 4 / 3);

            for (int i = 0; i < compressedSize; i++)
            {
                output.Add(source.Byte());

                // skip a byte in destination
                if ((i % 0x18) >= 0x10)
                {
                    output.Add(0);
                }
            }

            return(output.ToArray());
        }
        public static byte[] Compress(ByteArrayStream target)
        {
            var commands = new List <byte>();
            var data     = new List <byte>();
            var copyData = 0;

            while (!target.AtEnd)
            {
                var term       = target.GetBytes(0x11);
                var copyOutput = 0;
                var copyOffset = -1;

                while (term.Length >= 3)
                {
                    var(found, address) = target.FindLastInWindow(term, target.Address - 256, target.Address + term.Length - 1);

                    if (found)
                    {
                        copyOutput = term.Length - 2;
                        copyOffset = target.Address - address - 1;
                        break;
                    }

                    term = target.GetBytes(term.Length - 1);
                }

                if (copyOutput == 0)
                {
                    if (copyData == 0xf)
                    {
                        commands.Add((byte)copyData);
                        copyData = 1;
                    }
                    else
                    {
                        copyData++;
                    }
                    data.Add(target.Byte());
                }
                else
                {
                    commands.Add((byte)((copyOutput << 4) + copyData));
                    commands.Add((byte)copyOffset);
                    copyData        = 0;
                    target.Address += term.Length;
                }
            }

            // Add last copy data command
            if (copyData != 0)
            {
                commands.Add((byte)copyData);
            }

            // Add terminating command
            commands.Add(0);

            var output = new ByteArrayStream(commands.Count + data.Count + 2);

            var dataOffset = commands.Count;

            if (dataOffset > 0xffff)
            {
                throw new Exception($"{nameof(dataOffset)} cannot be larger than 0xffff. {nameof(commands)} is too large");
            }

            output.Word((ushort)dataOffset);
            output.Write(commands);
            output.Write(data);

            return(output.Buffer);
        }
Пример #11
0
        public static void Go()
        {
            var rom = FFMQ.Game.Rom;
            //0B81A5 $AD $91 $0E     LDA $0E91 [010E91] = $0D        A:400D X:182E Y:0080 S:1FC8 D:0000 DB:01 P:nvMxdizc V:30  H:166
            var start = 0x0d;             // byte

            start *= 2;

            Console.WriteLine($"{nameof(start)} == {start.ToString("x2")}");
            //0B81AF $BF $3B $AF $07 LDA $07AF3B,X[07AF55] = $0234  A: 001A X:001A Y:0080 S: 1FC8 D:0000 DB: 01 P: nvmxdizc V:30  H: 192
            var lookup_07af3b = rom.GetStream(0x07af3b);
            var offset        = lookup_07af3b.WordAt(start);

            Console.WriteLine($"{nameof(offset)} == {offset.ToString("x4")}");

            //0B81BC $BF $13 $B0 $07 LDA $07B013,X[07B247] = $07    A: 0234 X: 0234 Y: 0000 S: 1FC8 D:0000 DB: 01 P: nvMxdiZc V:30  H: 229
            //0B81C0 $99 $10 $19     STA $1910,Y[011910] = $00      A: 0207 X: 0234 Y: 0000 S: 1FC8 D:0000 DB: 01 P: nvMxdizc V:30  H: 239
            //0B81C3 $E8 INX                             A: 0207 X: 0234 Y: 0000 S: 1FC8 D:0000 DB: 01 P: nvMxdizc V:30  H: 249
            //0B81C4 $C8 INY                             A: 0207 X: 0235 Y: 0000 S: 1FC8 D:0000 DB: 01 P: nvMxdizc V:30  H: 252
            //0B81C5 $C0 $07 $00     CPY #$0007                      A:0207 X:0235 Y:0001 S:1FC8 D:0000 DB:01 P:nvMxdizc V:30  H:256
            var optionsLookup = rom.GetStream(0x07b013);

            // $1910 - $1916
            // $1910 is index into tilemap data
            // $1911 is index into bg tile graphics data
            // $1912 is index into color palettes
            var options = optionsLookup.GetBytesAt(7, offset);

            Console.WriteLine($"{nameof(options)} == {options.ToHexString()}");



            //; multiply(lower six bits of $1910) * 3
            //0b850e sep #$20
            //0b8510 lda $1910
            //0b8513 and #$3f
            //0b8515 sta $4202
            //0b8518 lda #$03
            //0b851a sta $4203

            //; get multiply result
            //0b8526 ldx $4216

            var mapDataOffsetIndex = (options[0] & 0x3f) * 3;

            Console.WriteLine($"{nameof(mapDataOffsetIndex)} == {mapDataOffsetIndex.ToString("x4")}");

            var mapDataOffsetLookup = rom.GetStream(0x0b8735);


            //; store source data lookup address, long, at $0900 => value at $0b8735,x
            //0b8529 rep #$20
            //0b852b lda $0b8735,x
            //0b852f sta $0900
            //0b8532 sep #$20
            //0b8534 lda $0b8737,x
            //0b8538 sta $0902

            var mapDataOffset = mapDataOffsetLookup.LongAt(mapDataOffsetIndex);

            Console.WriteLine($"{nameof(mapDataOffset)} == {mapDataOffset.ToString("x4")}");

            // $1000 seems to be the largest map, but using $2000 for now
            var(tilemapcomp, tilemapdecomp) = SimpleTailWindowCompression.DecompressFull(rom.GetStream(mapDataOffset), 0x2000);


            Utilities.WriteBytesToFile(tilemapcomp, @"c:\working\ffmq\~go! -- comp.txt");
            Utilities.WriteBytesToFile(tilemapdecomp, @"c:\working\ffmq\~go! -- decomp.txt");



            // $19b7
            var bgGraphicsOffsetsIndex = options[1] * 0x0a;
            var lookup_0b8cd9          = rom.GetStream(0x0b8cd9);

            // $1918 - $1921
            // $1918 is ???
            // $1919 is color data index to
            // $191a to $1921 are bg tile graphics offsets
            var x1918 = lookup_0b8cd9.GetBytesAt(0x0a, bgGraphicsOffsetsIndex);



            // TODO: clean up data access, this is awkward
            // bgTileIndexes is $191a to $1921
            var bgTileIndexes          = new ByteArrayStream(x1918, 2);
            var bgTileData             = rom.GetStream(0x058c80);
            var bgTilePaletteIndexData = rom.GetStream(0x05f280);

            var bgTileSets = new List <BgTileSetEntry>();

            while (!bgTileIndexes.AtEnd)
            {
                var bgTileIndex = bgTileIndexes.Byte();

                // $300 is $20 tiles of $18 bytes
                var tileDataAddressOffset = 0x300 * bgTileIndex;
                var tileData = ExpandSecondHalfWithZeros.Decompress(bgTileData.GetBytes(0x300, tileDataAddressOffset));

                // $10 is $20 tiles, each a nibble
                var paletteAddress = 0x10 * bgTileIndex;
                var palette        = ExpandNibblesMasked.Decompress(bgTilePaletteIndexData.GetBytes(0x10, paletteAddress));

                bgTileSets.Add(new BgTileSetEntry {
                    TileDataAddress     = bgTileData.Address + tileDataAddressOffset,
                    PaletteIndexAddress = bgTilePaletteIndexData.Address + paletteAddress,
                    TileData            = tileData,
                    PaletteIndex        = palette
                });
            }



            Console.ReadKey();
        }