• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
April 18, 2024, 06:55:34 am

News:

Please use .png instead of .bmp when uploading unfinished sprites to the forum!


Another Texttool

Started by looper, October 17, 2014, 07:11:19 pm

looper

Yo! You still remember me? I'm the crazy German guy who made a tool to edit text once (this one). Well, it wasn't nice and most likely full of bugs ...
However, I was sick the last days so I decided to just do this again. The result: A nice Java-Tool that can read and write texts, woohoo. It supports uncompressed texts, single-byte texts and LZSS-compressed texts*. It's still a beta because I'm too busy to test everything. It's a start at least ;).

* Written LZSS-texts are still uncompressed, but marked as LZSS-compressed. That's necessary because texts in scripts have to be compressed. At least it looks like it.

The tool requires Java 8, get it here. The source code is available at Github. Complete without docs because I'm lazy.

You can get the latest version from Github, too: https://github.com/looperhacks/FFTAText/releases

Usage
After starting the tool, you get prompted for a file. Just choose your rom. The next window has two textfields: The big one for text, the small one for the offset. Everything else should be self-explanatory.

Oh, this tool doesn't support all the nice characters the game supports. If you look at the character-table, only characters starting with 0x80 are supported. Kana aren't supported, either, but all the characters from the European version are supported (äßúîò, etc).

So ... that's it. If you find any bugs, please post them here. I'm planning to add some features later, but if you have an idea, you can post them, too :).
Be always yourself.
Except you can be Batman. Then be Batman.

I have the feeling, that my language is... bad. Really.

Darthatron

October 19, 2014, 06:16:24 pm #1 Last Edit: October 19, 2014, 06:25:50 pm by Darthatron
Nice! I wrote a C# function to compress LZSS. It's usually the same or better compression as the original. Maybe you can add it to save space.

public int LZSSDecompress(byte[] source, ref byte[] dest, out int inSize)
        {
            int retlen = source[3] | (source[2] << 0x08) | (source[1] << 0x10) | (source[0] << 0x18);
            int xIn = 4;
            int xOut = 0;
            int tmp = 0;
            int i = 0;
            int j = 0;

            while (xOut < retlen)
            {
                if ((source[xIn] & 0x80) == 0x80)
                {
                    tmp = (xOut - ((source[xIn] & 0x07) << 8)) - source[xIn + 1] - 1;

                    for (i = ((source[xIn] >> 3) & 0x0F) + 3; i > 0; i--)
                    {
                        dest[xOut] = dest[tmp];
                        xOut++;
                        tmp++;
                    }

                    xIn++;
                }
                else if ((source[xIn] & 0x40) == 0x40)
                {
                    for (i = (source[xIn] & 0x3F) + 1; i > 0; i--)
                    {
                        xIn++;
                        dest[xOut] = source[xIn];
                        xOut++;
                        if (xOut >= retlen) break;
                    }
                }
                else if ((source[xIn] & 0x20) == 0x20)
                {
                    for (i = (source[xIn] & 0x1F) + 2; i > 0; i--)
                    {
                        dest[xOut] = 0x00;
                        xOut++;
                        if (xOut >= retlen) break;
                    }
                }
                else if ((source[xIn] & 0x10) == 0x10)
                {
                    j = ((source[xIn + 1] & 0x3F) << 8) | source[xIn + 2];

                    tmp = (xOut - j) - 1;
                    if (tmp < 0) tmp = 0;

                    for (i = (((source[xIn + 1] >> 2) & 0x30) | (source[xIn] & 0x0F)) + 4; i > 0; i--)
                    {
                        dest[xOut] = dest[tmp];
                        tmp++;
                        xOut++;
                        if (xOut >= retlen) break;
                    }

                    xIn += 2;
                }
                else if (source[xIn] == 0x01)
                {
                    for (i = source[xIn + 1] + 3; i > 0; i--)
                    {
                        dest[xOut] = 0xFF;
                        xOut++;
                        if (xOut >= retlen) break;
                    }

                    xIn += 1;
                }
                else if (source[xIn] == 0x02)
                {
                    for (i = source[xIn + 1] + 3; i > 0; i--)
                    {
                        dest[xOut] = 0x00;
                        xOut++;
                        if (xOut >= retlen) break;
                    }

                    xIn += 1;
                }
                else if (source[xIn] == 0x00)
                {
                    j = source[xIn + 3] | (source[xIn + 2] << 0x08);
                    tmp = xOut - j - 1;
                    if (tmp < 0) tmp = 0;

                    for (i = source[xIn + 1] + 5; i > 0; i--)
                    {
                        dest[xOut] = dest[tmp];
                        xOut++;
                        if (xOut >= retlen) break;
                        tmp++;
                    }

                    xIn += 3;
                }

                xIn++;
            }

            inSize = xIn;
            return retlen;
        }


public int LZSSCompress(byte[] source, out byte[] dest)
        {
            List<byte> ret = new List<byte>();
            int xIn = 0;
            int xTemp = 0;
            int temp = 0;
            int size = source.Length;

            int zCount;
            int fCount;
            int notMatched;
            int length;
            int pos;

            //Big-endian Decompressed Size.
            ret.Add((byte)((size / 0x1000000) & 0xFF));
            ret.Add((byte)((size / 0x10000) & 0xFF));
            ret.Add((byte)((size / 0x100) & 0xFF));
            ret.Add((byte)(size & 0xFF));

            while (true)
            {
                if (xIn >= size) break;
                notMatched = 0;
                length = 0;
                pos = -1;
                xTemp = xIn;

                while (pos == -1)
                {
                    if (xTemp >= size) break;
                    pos = FindLongestSubArray(source, xTemp, out length);
                    if (pos != -1)
                    {
                        zCount = CompareBytes(source, xIn, notMatched - 1, 0x00, true);
                        fCount = CompareBytes(source, xIn, notMatched - 1, 0xFF, true);
                        if (notMatched == 1 && source[xIn] == 0x00 && source[xIn + 1] == 0x00) pos = -1;
                        else if (notMatched == 1 && source[xIn] == 0xFF && source[xIn + 1] == 0xFF && source[xIn + 2] == 0xFF) pos = -1;
                        else if (notMatched == 2 && source[xIn] == 0xFF && source[xIn + 1] == 0xFF) pos = -1;
                        else if (zCount > 0 && (temp = CompareBytes(source, xTemp, length, 0x00)) > 0)
                        {
                            if (zCount + temp >= 2)
                                pos = -1;
                        }
                        else if (fCount > 0 && (temp = CompareBytes(source, xTemp, length, 0xFF)) > 0)
                        {
                            if (fCount + temp >= 3) pos = -1;
                        }
                        else if (zCount == 00)
                        {
                            temp = CompareBytes(source, xTemp, length, 0x00);
                            if (temp >= length - 1 && temp >= 2) pos = -1;
                        }
                    }

                    if (pos == -1)
                    {
                        notMatched++;
                        xTemp++;
                    }
                }

                int zPos = -1;
                int fPos = -1;

                while (notMatched > 0)
                {
                    zPos = FindFirstInstanceOfArray(source, xIn, notMatched, 0x00, 2, out zCount);
                    fPos = FindFirstInstanceOfArray(source, xIn, notMatched, 0xFF, 3, out fCount);
                    //If there are FF or 00 arrays within the "uncompressed" data...
                    if (zCount >= 2 || fCount >= 3)
                    {
                        //00's
                        if (zPos < fPos && zPos > -1 || fCount < 3)
                        {
                            if (zPos > 0)
                            {
                                ret.Add((byte)(0x40 + ((zPos - 1) & 0x3F)));
                                if (xIn + zPos > size) zPos = size - xIn;
                                while (zPos > 0)
                                {
                                    ret.Add(source[xIn]);
                                    xIn++;
                                    notMatched--;
                                    zPos--;
                                }
                            }

                            if (zCount >= 2 && zCount <= 33)
                            {
                                zCount = (zCount - 2) & 0x1F;
                                ret.Add((byte)(0x20 | zCount));
                                notMatched -= (zCount + 2);
                                xIn += (zCount + 2);
                            }
                            else if (zCount >= 3)
                            {
                                ret.Add(0x02);
                                zCount = (zCount - 3) & 0xFF;
                                ret.Add((byte)(zCount));
                                notMatched -= (zCount + 3);
                                xIn += (zCount + 3);
                            }
                        }
                        //FF's
                        else
                        {
                            if (fPos > 0)
                            {
                                ret.Add((byte)(0x40 + ((fPos - 1) & 0x3F)));
                                if (xIn + fPos > size) fPos = size - xIn;
                                while (fPos > 0)
                                {
                                    ret.Add(source[xIn]);
                                    xIn++;
                                    notMatched--;
                                    fPos--;
                                }
                            }

                            if (fCount >= 3)
                            {
                                ret.Add(0x01);
                                fCount = (fCount - 3) & 0xFF;
                                ret.Add((byte)(fCount));
                                notMatched -= (fCount + 3);
                                xIn += (fCount + 3);
                            }
                        }
                    }
                    //Otherwise output all remaining data as uncompressed.
                    else
                    {
                        while (notMatched > 0x40)
                        {
                            ret.Add((byte)(0x7F));
                            temp = 0;
                            while (temp < 0x40)
                            {
                                ret.Add(source[xIn]);
                                xIn++;
                                notMatched--;
                                temp++;
                            }
                        }
                        ret.Add((byte)(0x40 + ((notMatched - 1) & 0x3F)));
                        if (xIn + notMatched > size) notMatched = size - xIn;
                        while (notMatched > 0)
                        {
                            ret.Add(source[xIn]);
                            xIn++;
                            notMatched--;
                        }
                    }
                }

                if (pos != -1)
                {
                    int distance = xIn - pos - 1;
                    switch (GetCompressionIndex(length, distance))
                    {
                        case 1: //Copy 3-18 bytes from close proximity
                            ret.Add((byte)(0x80 | (((length - 3) & 0x0F) << 3) | ((distance & 0x700) >> 8)));
                            ret.Add((byte)(distance & 0xFF));
                            break;
                        case 2: //Copy 4-67 bytes from medium proximity
                            ret.Add((byte)(0x10 | ((length - 4) & 0x0F)));
                            ret.Add((byte)((((length - 4) & 0x30) << 2) | ((distance & 0x3F00) >> 8)));
                            ret.Add((byte)(distance & 0xFF));
                            break;
                        case 3: //Copy 5-260 bytes from long proximity
                            ret.Add(0);
                            ret.Add((byte)(length - 5));
                            ret.Add((byte)((distance & 0xFF00) >> 8));
                            ret.Add((byte)(distance & 0xFF));
                            break;
                    }
                    xIn += length;
                }
            }

            dest = ret.ToArray();
            return ret.Count;
        }
  • Modding version: Other/Unknown
  • Discord username: Darthatron