Представление ASCF-строки в Data-файлах

Mizuwokiru

Величайший
Проверенный
Сообщения
945
Розыгрыши
0
Решения
1
Репутация
1 038
Реакции
449
Баллы
1 553
В общем, разбираю Артеевский ItemName по кусочкам и примерно составил такую структуру:
ItemCount + ItemCount * (ItemID + ItemNameLength + ItemName + ItemAdditionalName + ItemDescription + 44 байта, которые разберу позже)
где ItemCount - DWORD, ItemID - DWORD, ItemNameLenght - DWORD, ItemName - wchar_t[ItemNameLength / 2], ItemAdditionalName и ItemDescription - ASCF-строки.
Начал разбирать ASCF, но не совсем пойму закономерность. Например, возьмем Wooden Arrow. Видим такую последовательность байт:
Код:
3C 41 72 72 6F 77 20 6D 61 64 65 20 6F 66 20 77 6F 6F 64 2E 20 49 74 20 69 73 20 61 6E 20 61 72 72 6F 77 20 75 73 65 64 20 66 6F 72 20 61 20 4E 6F 2D 67 72 61 64 65 20 62 6F 77 2E 00
Собственно здесь первый байт - размер строки. Последующие 0x3C байт - сама строка.
Затем возьмем Darrin's Letter. Видим такую последовательность байт:
Код:
71 01 54 68 65 20 6C 6F 76 65 20 6C 65 74 74 65 72 20 74 68 61 74 20 44 61 72 69 6E 20 48 61 73 20 77 72 69 74 74 65 6E 20 74 6F 20 52 6F 78 78 79 2E 20 44 61 72 69 6E 20 48 61 73 20 61 73 6B 65 64 20 79 6F 75 20 74 6F 20 64 65 6C 69 76 65 72 20 74 68 69 73 20 6C 65 74 74 65 72 20 74 6F 20 47 61 74 65 6B 65 65 70 65 72 20 52 6F 78 78 79 2E 00
Здесь же за размер строки уже отвечает 2 байта: 71 01.
Теперь давайте поищем двухбайтовые размеры, в которых второй байт равен 02 и 03 (для анализа).
Код:
5C 02 53 68 69 6C 65 6E 20 4B 6E 69 66 65 20 69 6E 20 61 6E 20 55 6E 69 64 65 6E 74 69 66 69 65 64 20 73 74 61 74 65 2E 20 44 6F 75 62 6C 65 2D 63 6C 69 63 6B 20 74 68 65 20 69 74 65 6D 20 74 6F 20 69 64 65 6E 74 69 66 79 20 69 74 2E 20 54 68 65 20 61 63 71 75 69 72 65 64 20 69 74 65 6D 20 63 61 6E 20 62 65 20 53 74 61 6E 64 61 72 64 20 6F 72 20 42 6F 75 6E 64 2E 20 28 54 79 70 65 3A 20 6F 6E 65 2D 68 61 6E 64 65 64 20 77 65 61 70 6F 6E 20 2F 20 64 61 67 67 65 72 29 00
Тут размер указан как 5C 02.
Код:
7A 03 47 72 65 61 74 65 72 20 53 54 52 20 44 79 65 20 3C 53 74 72 20 2B 20 34 20 44 65 78 20 2D 20 34 3E 2E 20 43 6F 6C 6C 65 63 74 20 61 6E 64 20 74 61 6B 65 20 31 30 20 75 6E 69 74 20 73 20 6F 66 20 74 68 69 73 20 6D 61 67 69 63 61 6C 20 64 79 65 20 74 6F 20 74 68 65 20 73 79 6D 62 6F 6C 20 6D 61 6B 65 72 20 69 6E 20 74 6F 77 6E 20 61 6E 64 20 79 6F 75 20 77 69 6C 6C 20 72 65 63 65 69 76 65 20 61 20 73 79 6D 62 6F 6C 20 74 68 61 74 20 62 6F 6F 73 74 73 20 79 6F 75 72 20 61 62 69 6C 69 74 69 65 73 2E 20 43 61 6E 20 62 65 20 75 73 65 64 20 61 66 74 65 72 20 32 6E 64 20 63 6C 61 73 73 20 74 72 61 6E 73 66 65 72 2C 20 6F 72 20 31 73 74 20 4C 69 62 65 72 61 74 69 6F 6E 20 66 6F 72 20 45 72 74 68 65 69 61 2E 20 41 6C 6C 20 63 6C 61 73 73 65 73 2E 00
Здесь размер - 7A 03.
На сколько я понял, по этим двум байтам размер вычисляется примерно так:
firstByteValue + (secondByteValue - 1) * 0x40.
Теперь по поводу того случая, когда размер составляет всего 1 байт. В чем закономерность? А именно какой потолок значения того байта должно быть, чтобы размер строки составлял всего 1 байт?
 

В общем, разбираю Артеевский ItemName по кусочкам и примерно составил такую структуру:
ItemCount + ItemCount * (ItemID + ItemNameLength + ItemName + ItemAdditionalName + ItemDescription + 44 байта, которые разберу позже)
где ItemCount - DWORD, ItemID - DWORD, ItemNameLenght - DWORD, ItemName - wchar_t[ItemNameLength / 2], ItemAdditionalName и ItemDescription - ASCF-строки.
Начал разбирать ASCF, но не совсем пойму закономерность. Например, возьмем Wooden Arrow. Видим такую последовательность байт:
Здесь размер - 7A 03.
На сколько я понял, по этим двум байтам размер вычисляется примерно так:
firstByteValue + (secondByteValue - 1) * 0x40.
Теперь по поводу того случая, когда размер составляет всего 1 байт. В чем закономерность? А именно какой потолок значения того байта должно быть, чтобы размер строки составлял всего 1 байт?

Код:
    public static int readCompactInt(ByteBuffer input) throws IOException
   {
       int output = 0;
       boolean signed = false;
       for (int i = 0; i < 5; i++) {
           int x = input.get() & 0xFF;
           if (x < 0)
               throw new EOFException();
           if (i == 0) {
           if ((x & 0x80) > 0)
               signed = true;
           output |= x & 0x3F;
           if ((x & 0x40) == 0)
               break;
           } else if (i == 4) {
           output |= (x & 0x1F) << 27;
           } else {
           output |= (x & 0x7F) << 6 + (i - 1) * 7;
           if ((x & 0x80) == 0)
               break;
           }
       }
       if (signed)
           output *= -1;
       return output;
   }
Из godworld`овского fileedit`а.
 
Код:
    public static int readCompactInt(ByteBuffer input) throws IOException
   {
       int output = 0;
       boolean signed = false;
       for (int i = 0; i < 5; i++) {
           int x = input.get() & 0xFF;
           if (x < 0)
               throw new EOFException();
           if (i == 0) {
           if ((x & 0x80) > 0)
               signed = true;
           output |= x & 0x3F;
           if ((x & 0x40) == 0)
               break;
           } else if (i == 4) {
           output |= (x & 0x1F) << 27;
           } else {
           output |= (x & 0x7F) << 6 + (i - 1) * 7;
           if ((x & 0x80) == 0)
               break;
           }
       }
       if (signed)
           output *= -1;
       return output;
   }
Из godworld`овского fileedit`а.
Набросал такое до этого сообщения и кажись не прогадал :D
Код:
                if (descriptionSize < 0x40)
                {
                    descriptionCalcSize = descriptionSize;
                }
                else
                {
                    infile.read((char*)&descriptionSizeMultiplier, sizeof(uint8_t));
                    descriptionCalcSize = descriptionSize + ((uint32_t)(descriptionSizeMultiplier - 1)) * 0x40;
                }
 
А корейцы индюки... Там оно может быть и ASCF-строкой, и строкой Юникода.
Если не ошибаюсь, если первый байт значения размера строки 0x80 и выше, то читать ее как строчку юникода. А суммарное кол-во байт после считается как и с ASCF-строкой, но при этом первый байт умножается на 2.
 
(без сарказма)
Обожаю такое темы, где мне треть непонятно, и где +- отвечают...
А не "ты чё тупой? Напиши сам"
 
(без сарказма)
Обожаю такое темы, где мне треть непонятно, и где +- отвечают...
А не "ты чё тупой? Напиши сам"
Оффтоп:
Мне еще другое интересно. Эти дядьки вообще спят? :D
 
Я себе брал за основу код acmi,
 
В общем, понял как считать ASCF строку. К сожалению, тупой перенос функции readCompactInt из исходников acmi не помог (он считывает явно лишнее, из-за чего идет сдвиг на пару байт в первой же ASCF строчке. Но это натолкнуло меня на мысль: почему бы не поиграться с битовыми операциями. В уме их щелкать на раз-два не умею, поэтому достал калькулятор с ними. Поэкспериментировав, нашел некую закономерность. По сути берем первый байт - тот самый, отвечающий за "базовый" размер строки. Если этот размер превышает 0x7f, то это строка Юникода, и начинаем считать размер с нуля (firstByte &= 0x7f). Затем снова проверяем этот самый байт: если его значение превышает 3f, то считываем еще один байт - модификатор. Ну а дальше по-старинке: firstByte += 0x40 * (secondByte - 1).
Вот мой набросок:
 


Все строки не сильно большие, если взять большую строку, больше 8191, то алгоритм не будет работать
Код:
    public int readC() {
        final byte b0 = buffer.get();
        int result = b0 & 0x3F;
        boolean signed = (b0 & 0x80) != 0;
        if((b0 & 0x40) != 0) {
            final byte b1 = buffer.get();
            result |= (b1 & 0x7F) << 6;
            if((b1 & 0x80) != 0) {
                final byte b2 = buffer.get();
                result |= (b2 & 0x7F) << 13;
                if((b2 & 0x80) != 0) {
                    final byte b3 = buffer.get();
                    result |= (b3 & 0x7F) << 20;
                    if((b3 & 0x80) != 0)
                        result |= (buffer.get() & 0xF) << 27;
                }
            }
        }
        return signed ? -result : result;  
    }
Код:
    public String readS() {
        int length = readC();
        final StringBuilder str = new StringBuilder();
        if(length > 0) {
            for(int i = 0 ; i < length - 1 ; i++)
                str.append((char)buffer.get());
            if(buffer.get() != 0)
                throw new RuntimeException("read string with not null terminal");   
           
        } else if(length < 0) {
            length = -length;
            for(int i = 0 ; i < length - 1 ; i++)
                str.append(buffer.getChar());
            if(buffer.getShort() != 0)
                throw new RuntimeException("read string with not null terminal");
        }
        return str.toString();
    }
 
Последнее редактирование:


Все строки не сильно большие, если взять большую строку, больше 8191, то алгоритм не будет работать
Код:
    public int readC() {
        final byte b0 = buffer.get();
        int result = b0 & 0x3F;
        boolean signed = (b0 & 0x80) != 0;
        if((b0 & 0x40) != 0) {
            final byte b1 = buffer.get();
            result |= (b1 & 0x7F) << 6;
            if((b1 & 0x80) != 0) {
                final byte b2 = buffer.get();
                result |= (b2 & 0x7F) << 13;
                if((b2 & 0x80) != 0) {
                    final byte b3 = buffer.get();
                    result |= (b3 & 0x7F) << 20;
                    if((b3 & 0x80) != 0)
                        result |= (buffer.get() & 0xF) << 27;
                }
            }
        }
        return signed ? -result : result; 
    }
Код:
    public String readS() {
        int length = readC();
        final StringBuilder str = new StringBuilder();
        if(length > 0) {
            for(int i = 0 ; i < length - 1 ; i++)
                str.append((char)buffer.get());
            if(buffer.get() != 0)
                throw new RuntimeException("read string with not null terminal");  
          
        } else if(length < 0) {
            length = -length;
            for(int i = 0 ; i < length - 1 ; i++)
                str.append(buffer.getChar());
            if(buffer.getShort() != 0)
                throw new RuntimeException("read string with not null terminal");
        }
        return str.toString();
    }
Попробовал считать размер этим readC и получил тоже отличный результат, спасибо!
По поводу readS: на сколько я понял, строчка может быть как null-terminated, так и нет.
 
Назад
Сверху Снизу