在游戏机上加载最佳数据文件格式

时间:2009-11-13 06:56:11

标签: c++ python embedded playstation

我需要尽可能高效地在较旧的基于CD的游戏机上加载大型模型和其他结构化二进制数据。最好的方法是什么?数据将从Python应用程序导出。这是一个非常复杂的爱好项目。

Requierements:

  • 不依赖于完全符合标准的STL - 我可能会使用uSTL。
  • 尽量少开销。瞄准一个如此好的解决方案。它可以在原始Playstation上使用,但尽可能现代和优雅。
  • 无需向后/向前兼容。
  • 不要复制大块文件 - 最好是文件在后台加载到RAM中,然后直接从那里访问所有大块文件。
  • 不应该依赖具有相同字节顺序和对齐的目标,即Python中的C插件将其结构转储到光盘上不是一个好主意。
  • 应该允许移动加载的数据,就像单个文件的RAM大小的1/3一样,碎片可能是个问题。没有MMU可以滥用。
  • 健壮性是一个很大的好处,因为我的注意力很短,即我会改变保存部分代码并忘记加载一个或反之亦然,所以至少一个愚蠢的保护措施会很好。
  • 加载数据和运行时生成的数据之间的可交换性,没有运行时开销,也没有严重的内存管理问题,这将是一个很好的奖励。

我有一个半平面的解析Python琐碎,有限的语法C头,它将使用带偏移而不是指针的结构,以及主app中的便捷包装器结构/类,带有getter,可以将偏移转换为正确键入的指针/参考,但我想听听你的建议。

澄清:请求主要是关于数据加载框架和内存管理问题。

4 个答案:

答案 0 :(得分:4)

在Nintendo GameCube和DS等平台上,3D模型通常以非常简单的自定义格式存储:

  • 一个简短的标题,包含标识文件的幻数,顶点数,法线等,以及可选的标题后面的数据校验和(Adler-32,CRC-16等)。
  • 每个向量和法线的32位浮点3元组的可能压缩列表。
  • 可能压缩的边或面列表。
  • 所有数据都采用目标平台的本机端格式。
  • 压缩格式通常是微不足道的(霍夫曼),简单的(算术)或标准的(gzip)。所有这些都需要很少的内存或计算能力。

你可以把这样的格式作为一个提示:它是一个非常紧凑的表示。

我的建议是使用与您的内存数据结构最相似的格式,以尽量减少后处理和复制。如果这意味着你自己创建格式,那就这样吧。您有极端需求,因此需要采取极端措施。

答案 1 :(得分:3)

我注意到你的描述中没有任何地方要求“编程的简易性”。 : - )

因此,这就是我想到的一种创造方式:

  • 数据应该与目标内存中的磁盘格式相同,这样它就可以简单地将blob从磁盘中拉入内存而无需重新格式化。根据您将内容放入内存所需的自由度,“blob”可以是整个文件,也可以是其中的较小位;我不太了解你的数据,不足以建议如何细分它,但可能你可以。因为我们不能在主机上依赖相同的字节顺序和对齐,所以在主机端编写文件时需要有点聪明才能翻译,但至少这样你只需要一方面的聪明才智转移而不是两者。

  • 为了保证目标端和主机端代码匹配,您应该以提供单个数据描述的形式编写它,并使用一些生成代码来生成两者目标端C代码和来自它的主机端Python代码。您甚至可以让您的生成器在进程中生成一个小的随机“版本”号,并让主机端代码将其写入文件头,目标端检查它,如果它们不匹配则给出错误。 (使用随机值的关键是,您关心的唯一信息位是否匹配,并且您不希望手动增加它。)

答案 2 :(得分:3)

这是一种常见的游戏开发模式。

通常的做法是在离线预处理步骤中烹饪数据。生成的blob可以以最小的开销流入。 blob是平台相关的,应该包含正确的对齐和&目标平台的终结。

在运行时,您只需将指针强制转换为内存中的blob文件即可。您也可以处理嵌套结构。如果保留一个目录,其中包含blob中所有指针值的偏移量,则可以修复指针以指向正确的地址。这类似于dll加载的工作方式。

我一直在研究一个红宝石库,bbq,我用来为我的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;
}

我的bbq库还没有为黄金时间做好准备,但是它可以为你提供一些如何在python中自己编写的想法。

祝你好运!

答案 3 :(得分:0)

考虑将数据作为BLOB存储在SQLite DB中。 SQLite非常便携,轻量级,ANSI C,同时具有C ++和Python接口。这将处理大文件,没有碎片,具有快速访问的可变长度记录,等等。其余的只是将结构序列化为这些BLOB。