Извлёк изображения из архива
imageArcive.arc, в котором хранится большинство изображений (текстур).
Erizo_V изображения получил, для остальных (если такие будут, хех) подготовил программы для извлечения:
https://github.com/infval/AMLUnpacker_TrueRemembrance/.
Так получилось, что есть 2 версии распаковщика на Python и C#. Первая требует установленный Python 3 и медленно работает, вторая - .NET Framework 4+ (хотя можно было скомпилировать под .NET Core со всеми зависимостями).
Python 3 версия1. Установить последний Python 3:
https://www.python.org/.
2. Установить PIL, в командной строке:
pip install -U pillow
3. Скопировать скрипты AMLUnpacker.py и etc1decoder.py.
4. Для распаковки перетащить imageArcive.arc на AMLUnpacker.py или явно в командной строке:
AMLUnpacker.py imageArcive.arc -o images_dir
Где images_dir конечная папка, без -o в imageArcive.arc_unpack.
C# версия1. Если вдруг не установлен, то установите последний .NET Framework.
2. Скомпилированная программа:
https://github.com/infval/AMLUnpacker_TrueRemembrance/releases.
2. Для распаковки перетащить imageArcive.arc на AMLUnpacker.exe или явно в командной строке:
AMLUnpacker.exe imageArcive.arc images_dir
Где images_dir конечная папка, без этого в imageArcive.arc_unpack.
Как распаковать .3ds или .ciaЕсли ромы не зашифрованы, то с помощью ctrtool
.3ds
ctrtool --romfsdir=romfs game.3ds
.cia
ctrtool --contents=contents game.cia
ctrtool --romfsdir=romfs contents.0000.00000000
Подробности:
https://github.com/ihaveamac/3DS-rom-tools/wiki/Extract-a-game-or-application-in-.3ds-or-.cci-format.
Структура imageArcive.arcLitte-Endian
0x00: 128 байт - Заголовок. AML_Arciver и нули.
0x80: 4 байта - Количество файлов. Здесь 671.
0x84: Сжатые файлы
0x00: 64 байта - Путь файла, его имя.
0x40: 4 байта - Размер данных дальше, после следующий файл.
По приколу написал
файл в формате Kaitai Struct (.ksy), описывающий структуру. Но я его не использовал в коде, только проверил в Web IDE.
Сжатие данныхПервый 1 байт - маркер, который будет обозначать сжатие.
Когда встречается маркер, следующие байты: смещение назад и длина. Если смещение больше маркера, то -1 (вычесть единицу). Если два подряд маркера, то вставляется 1 байт равный маркеру, т. е. это экранирование.
Не используются байты только что записанные на выход, поэтому можно скопировать сразу все байты, а не по одному или кусками, как в некоторых алгоритмах.
Формат текстурыПосле декомпрессии получаем ETC1A4 (ETC1 с прозрачностью, стандартный для 3DS), в начале идёт CTPK и заголовок
https://www.3dbrew.org/wiki/CTPK#Texture_Info_Entry, из элементов используются только ширина и высота, остальное однотипно.
ИсследованиеЧтобы изучить сжатие, получил разжатые текстуры из памяти эмулятора Citra с помощью Cheat Engine. Первые байты одинаковые, так как не сжимаются, после сравнивал встречающиеся новые байты, чтобы найти соответствие текстурам. Хотя алгоритм очень простой, догадался не сразу.
Для декодирования ETC1A4 использовал код из
https://github.com/gdkchan/Ohana3DS-Rebirth/, так как не смог найти готовой программы принимающей сырой файл (3dstex у меня не сработал). Изначально использовал Python для всего кроме ETC1A4, поэтому решил переписать декодирование на Python, но это медленно, тогда переписал остальное на C#.
etc1decoder.py можно использовать отдельно: etc1decoder.py файл_etc1 ширина высота [-o выходной_файл] [-a выставить для ETC1A4].
Кстати, ETC1 файл после кодирования в etc1tool.exe из Android SDK не смог правильно декодироваться использованным мной кодом, у 3DS различие в порядке байтов
https://www.3dbrew.org/wiki/CGFX#TXOB:
ETC1 is a compressed texture format which compresses blocks of 4x4 pixels into u64s. These u64 are traditionally stored in big endian; however, nintendo's implementation stores them in little endian. ETC1 textures are stored in 8x8 tiles; decompressed 4x4 therefore have to be organized accordingly.