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

StackOverflow https://stackoverflow.com/questions/1727594

Вопрос

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

Требования:

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

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

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

Это было полезно?

Решение

Это распространенный шаблон разработки игр.

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

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

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

Вот макет памяти, который я использую для заголовка большого двоичного объекта:

// 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
//

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

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-битных троек с плавающей запятой для каждого вектора и нормали.
  • Возможно сжатый список ребер или граней.
  • Все данные представлены в собственном формате целевой платформы с порядком байтов.
  • Формат сжатия часто бывает тривиальным (Хаффман), простым (арифметический) или стандартным (gzip).Все это требует очень мало памяти или вычислительной мощности.

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top