Skip to content

Instantly share code, notes, and snippets.

@Fothsid
Created May 20, 2023 20:37
Show Gist options
  • Save Fothsid/0d01f5d2f3c6d48de8963f5171a2933b to your computer and use it in GitHub Desktop.
Save Fothsid/0d01f5d2f3c6d48de8963f5171a2933b to your computer and use it in GitHub Desktop.
Resident Evil Outbreak .NBD pattern file for ImHex
//
// Resident Evil Outbreak .NBD pattern file for ImHex
// Made by Fothsid.
//
// WARNING: Doesn't work for the majority of EFxx.NBD files,
// as those have incorrect subchunk/data count values.
// Seems like the game relies solely on the chunk size
// when parsing the models.
// Wasn't tested with room models, but theoretically should work just fine.
//
#include <std/string.pat>
#pragma eval_depth 250
#pragma pattern_limit 200000
bool room_nbd = false;
struct NBDEntry {
char type[3];
char pad;
u32 offset;
u32 size;
u32 texCount;
};
struct TID {
u32 count;
u32 ids[count];
};
struct CompressedTexture {
u32 size;
u8 data[size];
};
enum ChunkType : u32 {
TextureRef = 0x00000000,
Model = 0x00000001,
MeshList = 0x00000002,
Mesh = 0x00000004,
PrimLists = 0x00000005,
MaterialList = 0x00000009,
TextureList = 0x0000000A,
Date = 0x00020000,
TriStripsFW = 0x00030000,
TriStripsPW = 0x00040000,
MaterialRefs = 0x00050000,
TStripMaterials = 0x00060000,
Vertices = 0x00070000,
Normals = 0x00080000,
TexCoords = 0x000A0000,
VertexColors = 0x000B0000,
Weights = 0x000C0000,
RenderAttribs = 0x000F0000,
NodeRefs = 0x00100000,
MaterialConstant= 0x00120000,
MaterialLambert = 0x00130000,
MaterialPhong = 0x00140000,
MaterialVColors = 0x00150000,
};
enum AHIChunkType : u32 {
Hierarchy = 0xC0000000,
RootsType1 = 0x80000000,
RootsType2 = 0x00000000,
Node = 0x40000001,
MeshNode = 0x40000002,
JointNode = 0x40000003
};
struct Date {
u32 value;
} [[sealed, format("format_date")]];
fn format_date(Date date)
{
return std::format("{:02}-{:02}-2{:03}", (date.value >> 8) & 0xFF,
(date.value >> 16) & 0xFF,
(date.value >> 24) & 0xFF);
};
struct TriStrip {
u32 count;
u32 indices[count&0x7FFFFFFF];
};
struct RenderAttribs {
u32 version; // Always 0x10000
u32 material;
u32 specular;
u32 cull;
u32 scissor; // Always seems to be 1
u32 light; // Used; but have to figure out what it does exactly
u32 wrap;
u32 uvScroll;
u32 disableFog;
u32 fadeCol;
u32 specialShader; // Not used.
u32 alphaSrc;
u32 alphaDst;
u32 alphaOp;
u32 zOp; // IGNORE. The game forces everything to ZOP_LESS
u32 zWrite;
u32 filtering;
u32 addressMode;
};
struct vec4 { float x, y, z, w; };
struct vec3 { float x, y, z; };
struct vec2 { float x, y; };
struct Node {
u32 id;
s32 parentId;
s32 childId;
s32 nextId;
vec4 scale;
vec4 rotation;
vec4 translation;
s32 meshId;
s32 groupId;
u32 _dummy0[46][[hidden]];
};
struct Material {
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
u32 textureCount [[hidden]];
u32 _dummy0[50] [[hidden]];
u32 textureRefIds[textureCount];
};
struct WeightNode {
u32 nodeId;
float weight;
};
struct Weight {
u32 count [[hidden]];
WeightNode nodes[count] [[inline]];
};
struct TextureRef {
u32 id;
u32 width;
u32 height;
u32 tiling;
u32 dummy[60] [[hidden]];
};
struct AMOChunk {
ChunkType type;
u32 count [[hidden]];
u32 size [[hidden]];
match (type) {
(ChunkType::Model |
ChunkType::MeshList |
ChunkType::Mesh |
ChunkType::PrimLists |
ChunkType::MaterialList |
ChunkType::TextureList): AMOChunk data[count] [[inline]];
(ChunkType::Date): Date data;
(ChunkType::TriStripsFW |
ChunkType::TriStripsPW): TriStrip data[count];
(ChunkType::MaterialRefs |
ChunkType::TStripMaterials |
ChunkType::NodeRefs): u32 data[count];
(ChunkType::Vertices |
ChunkType::Normals): vec3 data[count];
(ChunkType::TexCoords): vec2 data[count];
(ChunkType::VertexColors): vec4 data[count];
(ChunkType::Weights): Weight data[count];
(ChunkType::RenderAttribs): RenderAttribs data [[inline]];
(ChunkType::MaterialConstant |
ChunkType::MaterialLambert |
ChunkType::MaterialPhong |
ChunkType::MaterialVColors): Material data [[inline]];
(ChunkType::TextureRef): TextureRef data [[inline]];
}
};
struct AHIChunk {
AHIChunkType type;
u32 count [[hidden]];
u32 size [[hidden]];
match (type) {
(AHIChunkType::Hierarchy): AHIChunk data[count] [[inline]];
(AHIChunkType::RootsType1 |
AHIChunkType::RootsType2): u32 data[count];
(AHIChunkType::Node |
AHIChunkType::MeshNode |
AHIChunkType::JointNode): Node data [[inline]];
}
};
struct NBDHeader {
NBDEntry list[3] [[hidden]];
if (list[0].type == "TID") {
TID tid @ list[0].offset;
room_nbd = true;
}
else if (list[0].type == "TEX") {
CompressedTexture textures[list[0].texCount] @ list[0].offset;
}
if (!room_nbd) {
NBDEntry addons[4] [[hidden]];
AMOChunk model @ list[1].offset;
AHIChunk hierarchy @ list[2].offset;
if (addons[1].type == "SDW") {
u8 shadowData[addons[1].size] @ addons[1].offset;
}
if (addons[2].type == "AMO") {
AMOChunk addonModel @ addons[2].offset;
}
if (addons[3].type == "AHI") {
AHIChunk addonHierarchy @ addons[3].offset;
}
}
else {
NBDEntry addons[2] [[hidden]];
AMOChunk stageModel @ list[1].offset;
AHIChunk stageHierarchy @ list[2].offset;
AMOChunk effectModel @ addons[0].offset;
AHIChunk effectHierarchy @ addons[1].offset;
}
};
NBDHeader header @ 0x00;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment