使用 include 指令填充数组

Populating an array with include directives

今天早上我得知著名的电子游戏超级马里奥 64 已经完全反编译回 C 源代码。出于好奇,我决定仔细研究一下,我注意到了一些我以前从未见过的东西。

// 0x09000000
ALIGNED8 const u8 machine_09000000[] = {
#include "textures/machine/ttc_textures.00000.rgba16.inc.c"
};

看起来他们在数组中放置了一个 include 指令。这在整个程序中重复进行。
我学习 C 已经有一段时间了,我认为自己对这门语言相当流利,但这对我来说是非常新奇的东西,我有很多关于它的问题。

1) 做这样的事情真的合法,甚至被推荐吗?
2) 你为什么要这样做?
3) 为什么 include 引用 *.c 文件?
4) 为什么类型设置为 u8(这应该是 unsigned char 的标准别名,如果我明白的话)?
5) 那个 ALIGNED8 宏是什么?是标准的东西吗?

我试图自己收集信息,但找不到太多关于这个主题的信息。如果你想自己看源代码,这里是link:https://github.com/n64decomp/sm64

在我开始讨论之前,让我首先推荐使用附加到该 repo 的 Discord link,discord.gg/27JtCWs。我会尽力回答这个问题,但绝对不是最了解一般编码标准和典型行为的人。我的经验只是相关的,因为我与一些参与反编译的人密切合作。

1 & 2。我不认为这通常是明智的做法,但重要的是要记住 repo 的要点 - 允许轻松修改代码库,同时保留其编译 1:1 SM64的ROM。拆分文件可以更轻松地找到事物的相关位置,尽管这个特定的文件还没有很好地命名。

  1. 所以 inc.c 文件是美化的头文件,只是将它们的代码插入该位置。它被命名为 .c 文件,因为它有点像 C,只是它本身不是合法的 C 文件。这就是为什么它是 .inc.c,因为它是 INCluded>

  2. 是标准的unsigned char,没错。这可以在 types.h 中找到。这样做是因为实际上它只是导入了一堆可以单独读取的数据。这个特定的文件不在 repo 中,但如果你提取资产,你可以看到它是插入其中的图像。图像的数据只是分成 u8 并放在那里,允许它既是 extracted/inserted 又更容易编辑。

  3. A​​LIGNED8 用于编译器。存储库使用的 IDO 5.7 编译器经常对齐 ROM 输出中的内容,并且 ALIGN8 指令告诉它填充到 8 个字节。如果 ALIGNED8 不存在,它可能会过早插入该数据,从而移动 ROM。

希望我没有犯任何错误,这对您有所帮助。重要的是要记住整个 GitHub 并不是真正的典型,因为它不太担心内部一致性,而更多地担心外部 (ROM) 一致性,这通常意味着有时解决方案必须是这样的黑客攻击在一起以允许功能。如果您还有其他问题,我会再次建议您在 Discord 中提问,因为那些人​​比我见多识广。

来自一个比我聪明一点的人-

1 & 2:我们包括了一张转换为 C 语言的图像。除 .c 代码中的数组外,还有其他技术可用于此,但这是 sm64 开发人员使用的技术,因此我们模仿它。我们让构建系统自动从图像生成(部分)C,而不是复制粘贴 C 数组,然后将其#included。它工作得很好,在这种情况下我们能做到最好。 (假设我们希望将所有内容都保留在 C 语言中而不是汇编语言中——在汇编语言中我们会使用 .incbin ,它会更干净,但它变得不那么便携了。)

3: .h 是错误的,因为它不是声明函数、类型等的头文件,而是数据(部分 C 文件)

  1. 它是 u8,因为我们包含的文件只是一个二进制数据块,我们不想做出字节顺序假设

  2. ALIGNED8 实际上在 IDO 上被忽略了。它的作用是在 GCC 上对数组进行 8 字节对齐。原因是发送到 RSP 的地址必须是 8 字节对齐的,如果没有这样的指令就不能保证这一点。特别是,GCC 乐于以非 8 字节对齐的方式对变量重新排序。 IDO 不进行那种重新排序(它只是按源顺序发出数组),但变量恰好以 8 字节对齐结束。 它有助于了解#include 的作用,它只是包含另一个文件的文本 从该定义可以得出结论,是的,在这种情况下使用它是绝对合法的;这是否明智是另一个问题。