Оптимальная загрузка формата файла данных на игровой консоли
Мне нужно максимально эффективно загружать большие модели и другие структурированные двоичные данные на более старую игровую консоль на 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-объектов.