Оптимальная загрузка формата файла данных на игровой консоли

Мне нужно максимально эффективно загружать большие модели и другие структурированные двоичные данные на более старую игровую консоль на CD. Какой лучший способ сделать это? Данные будут экспортированы из приложения Python. Это довольно сложный хобби-проект.

Requierements:

  • не полагаясь на полностью стандартную совместимую STL - я мог бы использовать uSTL все же.
  • как можно меньше накладных расходов. Стремитесь к решению так хорошо. что он может быть использован на оригинальной Playstation, и при этом максимально современный и элегантный.
  • нет необходимости в обратной / прямой совместимости.
  • не нужно копировать большие куски - желательно, чтобы файлы загружались в ОЗУ в фоновом режиме, а все большие куски открывались напрямую оттуда позже.
  • не следует полагаться на цель, имеющую такой же порядок байтов и выравнивание, то есть плагин C в Python, который выгружает свои структуры на диск, не будет хорошей идеей.
  • должно позволять перемещать загруженные данные вокруг, так как с отдельными файлами 1/3 размера ОЗУ, фрагментация может быть проблемой. Нет MMU, чтобы злоупотреблять.
  • Надежность - это большой бонус, так как мой объем внимания очень короткий, то есть я бы изменил сохраняющую часть кода и забыл загрузочную или наоборот, так что по крайней мере глупая защита была бы хорошей.
  • Взаимодействие между загруженными данными и сгенерированными во время выполнения данными без дополнительных затрат времени выполнения и без серьезных проблем с управлением памятью было бы хорошим бонусом.

У меня вроде есть полуплоскость разбора в тривиальных заголовках Python с ограниченным синтаксисом, которые будут использовать структуры со смещениями вместо указателей, и удобные структуры / классы обертки в основном приложении с геттерами, которые будут преобразовывать смещения в правильно типизированные указатели / ссылки, но я хотел бы услышать ваши предложения.

Пояснение: запрос в основном касается структуры загрузки данных и проблем управления памятью.

4 ответа

Решение

Это общий шаблон разработки игр.

Обычный подход состоит в том, чтобы готовить данные в автономном режиме предварительной обработки. Полученные капли могут быть добавлены с минимальными накладными расходами. Капли зависят от платформы и должны содержать правильное выравнивание и порядковый номер целевой платформы.

Во время выполнения вы можете просто привести указатель к файлу BLOB-объекта в памяти. Вы также можете иметь дело с вложенными структурами. Если вы сохраняете оглавление со смещением для всех значений указателя в BLOB-объекте, вы можете исправить указатели, чтобы они указывали на правильный адрес. Это похоже на то, как работает загрузка dll.

Я работал над библиотекой ruby, которую я использую для приготовления данных для своей игры на iphone.

Вот расположение памяти, которое я использую для заголовка BLOB-объекта:

// Memory layout
//
// p begining of file in memory.
// p + 0 : num_pointers
// p + 4 : offset 0
// p + 8 : offset 1
// ...
// p + ((num_pointers - 1) * 4) : offset n-1
// p + (num_pointers * 4) : num_pointers   // again so we can figure out 
//                                            what memory to free.
// p + ((num_pointers + 1) * 4) : start of cooked data
//

Вот как я загружаю двоичный файл BLOB-объектов и исправляю указатели:

void* bbq_load(const char* filename)
{
    unsigned char* p;
    int size = LoadFileToMemory(filename, &p);
    if(size <= 0)
        return 0;

    // get the start of the pointer table
    unsigned int* ptr_table = (unsigned int*)p;
    unsigned int num_ptrs = *ptr_table;
    ptr_table++;

    // get the start of the actual data
    // the 2 is to skip past both num_pointer values
    unsigned char* base = p + ((num_ptrs + 2) * sizeof(unsigned int));

    // fix up the pointers
    while ((ptr_table + 1) < (unsigned int*)base)
    {
        unsigned int* ptr = (unsigned int*)(base + *ptr_table);
        *ptr = (unsigned int)((unsigned char*)ptr + *ptr);
        ptr_table++;
    }

    return base;
}

Моя библиотека для барбекю не совсем готова в прайм-тайм, но она может дать вам некоторые идеи о том, как написать ее самостоятельно на python.

Удачи!

На платформах, таких как Nintendo GameCube и DS, 3D-модели обычно хранятся в очень простом пользовательском формате:

  • Краткий заголовок, содержащий магическое число, идентифицирующее файл, количество вершин, нормалей и т. Д., И, необязательно, контрольную сумму данных, следующих за заголовком (Adler-32, CRC-16 и т. Д.).
  • Возможно сжатый список 32-битных 3-кортежей с плавающей точкой для каждого вектора и нормали.
  • Возможно сжатый список ребер или граней.
  • Все данные находятся в собственном формате прямого порядка целевой платформы.
  • Формат сжатия часто бывает тривиальным (Хаффман), простым (арифметическим) или стандартным (gzip). Все это требует очень мало памяти или вычислительной мощности.

Вы можете взять такие форматы как реплику: это довольно компактное представление.

Я предлагаю использовать формат, наиболее похожий на ваши структуры данных в памяти, чтобы минимизировать постобработку и копирование. Если это означает, что вы сами создаете формат, пусть будет так. У вас есть крайние потребности, поэтому необходимы крайние меры.

Отмечу, что нигде в вашем описании вы не просите "простоты программирования".:-)

Итак, вот что приходит мне в голову как способ создания этого:

  • Данные должны быть в том же формате на диске, что и в целевой памяти, чтобы они могли просто вытягивать большие двоичные объекты с диска в память без переформатирования. В зависимости от того, сколько свободы вы хотите поместить в память, "BLOB-объекты" могут быть целым файлом или меньшими битами в нем; Я недостаточно хорошо понимаю ваши данные, чтобы предложить, как их подразделить, но, вероятно, вы можете. Поскольку мы не можем полагаться на один и тот же порядок байтов и выравнивание на хосте, вам нужно быть немного умным в переводе вещей при записи файлов на стороне хоста, но, по крайней мере, в этом случае вам нужен ум только на одной стороне передачи, а не на обоих.

  • Чтобы обеспечить некоторую уверенность в том, что код на стороне назначения и на стороне хоста совпадает, вы должны записать это в форме, где вы предоставляете одно описание данных и имеете некоторый код генерации, который будет генерировать как код C на целевой стороне, так и код Python на стороне хоста из него. Вы могли бы даже заставить свой генератор генерировать небольшое случайное число "версии" в процессе, и чтобы код на стороне хоста записал это в заголовок файла, а целевой объект проверил его и выдал ошибку, если они не совпадают., (Смысл использования случайного значения заключается в том, что единственный информационный бит, который вас волнует, - совпадают ли они, и вам не нужно увеличивать его вручную.)

Подумайте о том, чтобы хранить ваши данные как BLOB в БД SQLite. SQLite чрезвычайно портативен и легок, ANSI C, имеет интерфейсы C++ и Python. Это позаботится о больших файлах, никакой фрагментации, записях переменной длины с быстрым доступом и так далее. Все остальное - просто сериализация структур для этих BLOB-объектов.

Другие вопросы по тегам