Jump to content

Need advice for finding data offsets in .ea3 model files from an old EA Bright Light game


Go to solution Solved by h3x3r,

Recommended Posts

Posted (edited)

I'm trying to reverse engineer an old EA Bright Light game called Create. Inside the .big files, there were .ea3 and .ea3high files, which I know are uncompressed 3D models by the file structure. What I want to do is find the lengths/offsets of the mesh data so I can convert the files to .obj without having to guess where the data chunks start. The .ea3high files are largely irrelevant since all they seem to do is add a couple of vectors to each vertex (probably tangent data and whatnot). I did managed to figure out the stride of the main mesh data in some smaller .ea3 files..

The vertex data always begins with a small header that starts with PH and ends with 01 00 00 00... The offsets/lengths I'm looking for are likely in those header lines somewhere, but I can't seem to find a value that matches up consistently. Once the vertex, normal, and UV data ends, it switches to face data in short TStrip form. There are sometimes multiple meshes in one file as well, with each section prefaced by similar PH headers.

I've attached the small mesh I successfully extracted, along with some screenshots from Model Researcher and a couple of larger meshes. I mainly just want to find a consistent way to calculate where the face data starts.

Screenshot (243).png

Screenshot (242).png

SampleMesh.7z OtherMeshes.7z

Edited by jmancoder
  • jmancoder changed the title to Need advice for finding data offsets in .ea3 model files from an old EA Bright Light game
  • Engineers
Posted (edited)
7 hours ago, jmancoder said:

I mainly just want to find a consistent way to calculate where the face data starts.

Hi, what's the base for your calculation? End of the vertex header, start of vertex block?

Maybe I don't understand the problem - usually you'd search for 000001000200 here to find the start of the face index blocks.

EA-BL-Create.png

train_mesh2.png

Edited by shak-otay
Posted
17 hours ago, shak-otay said:

usually you'd search for 000001000200 here to find the start of the face index blocks

I did manage to make a Python script to extract most of the face and vertex data by just looking for a sequence like that. I feel like there's something off about a few of the .ea3 files though. No matter what I try, the faces are heavily distorted on meshes like sp_the_ferriswheel_v0.ea3, even when I try to find the offsets manually in Model Researcher and hex2obj. I think they have a slightly different file structure. I was thinking if I could interpret the PH header that appears before each submesh, it would be easier to see what I'm reading wrong. I've attached a couple more meshes that didn't convert well.

MoreMeshes.7z

  • Engineers
  • Solution
Posted

End of guess work guys...😉

Hope you are familiar with 010 HEX Editor Templates... You can easily rewrite it to the Noesis form. Or maybe I can...

Found only 32 / 36 stride type in samples you provide. There may be more I guess. Template parse only 32 / 36 stride.

//------------------------------------------------
//--- 010 Editor v14.0 Binary Template
//
//      File: 
//   Authors: 
//   Version: 
//   Purpose: 
//  Category: 
// File Mask: 
//  ID Bytes: 
//   History: 
//------------------------------------------------
LittleEndian();OutputPaneClear();

local uint32 i,j,k,l,m,n;

char Sign[4];
uint32 TotalFileSize;
uint32 MatrixOffset;
uint32 MeshDataBaseOffset;
FSkip(16);
float Matrix4x4[16];
float BBox[8];
uint32 Unknown_0,
       UnknownOffset,
       MaterialIndex,
       MaterialIndexOffset,
       TextureCount,
       MaterialInfoOffset,
       Unknown_1,
       Unknown_2,
       BoneMatrixOffset,
       Unknown_3,
       Unknown_4,
       Unknown_5,
       Unknown_6,
       Unknown_7,
       RootOffset,
       Unknown_8;
       
FSeek(MaterialIndexOffset);
struct
{
    struct
    {
        uint32 MaterialPropOffset;
        ubyte TextureMapCount,Unknown_1,Unknown_2,Unknown_3;
        ubyte Unknown_4,Unknown_5;
        uint16 Unknown_6;
        uint32 Unknown_7;
    }MaterialInfo[MaterialIndex]<optimize=false>;
        
    struct
    {
        for (i=0; i < MaterialIndex; i++)
        {
            FSeek(MaterialInfo[i].MaterialPropOffset);
            struct
            {
                ubyte TextureIndex;
                ubyte Unknown_0,Unknown_1,Unknown_2;
                float Unknown_3;
            }MaterialProp;
        }
    }MaterialProperties;
    
    FSeek(MaterialInfoOffset);
    uint32 TextureNameOffset;
    
    FSeek(TextureNameOffset);
    struct
    {
        string TextureName;
    }Texture[TextureCount]<optimize=false>;
}MaterialTable;

FSeek(UnknownOffset);
struct
{
    float Unknown_0[8];
    uint32 Unknown_1[4];
    float Unknown_2[8];
}UnknownData;

struct
{
    uint32 MeshIndex;
    uint32 Unknown_16,MeshInfoOffset;
    
    FSeek(MeshInfoOffset);
    struct
    {
        float BBox[8];
        uint32 ShapeInfoOffset,
               Unknown_0,
               UnknownOffset;
        ubyte MaterialIndex,
              Unknown_1,
              Unknown_2,
              Unknown_3;
    }MeshInfo[MeshIndex]<optimize=false>;
}MeshTable;

if (BoneMatrixOffset != 0)
{
    FSeek(BoneMatrixOffset);
    struct
    {
        uint32 BoneCount;
        uint32 BoneNameInfoOffset,
               BoneParentOffset,
               Unknown_2,
               Unknown_3,
               BoneIndexOffset,
               BoneIndex,
               Unknown_6;
        struct
        {
            float Mat00,Mat01,Mat02,Mat03,
                  Mat10,Mat11,Mat12,Mat13,
                  Mat20,Mat21,Mat22,Mat23,
                  Mat30,Mat31,Mat32,Mat33;
        }BonePosition[BoneCount]<optimize=false>;
        
        FSeek(BoneNameInfoOffset);
        struct
        {
            uint32 BoneNameOffset;
            local uint32 cPos=FTell();
            FSeek(BoneNameOffset);
            string BoneName;
            FSeek(cPos);
        }BoneNames[BoneCount]<optimize=false>;
        
        FSeek(BoneParentOffset);
        ubyte BoneParent[BoneCount];
        
        FSeek(RootOffset);
        char RootName[8];
        ubyte Count;
        ubyte RootIndex[Count];
    }BoneTable;
}

for (i=0; i < MeshTable.MeshIndex; i++)
{
    FSeek(MeshTable.MeshInfo[i].ShapeInfoOffset);
    struct
    {
        char Sign[2];
        ubyte Unknown_0;
        ubyte Unknown_1;
        uint16 Unknown_2;
        uint16 Unknown_3;
        uint32 Unknown_4;
        uint32 MeshDataSize;
        uint32 VertexOffset;
        uint32 IndexOffset;
        uint32 Unknown_5;
        uint16 Unknown_6;
        uint16 Unknown_7;
        uint16 Unknown_8;
        uint16 IndexCount;
        ubyte Stride;
        ubyte Unknown_9;
        uint16 VertexCount;
        FSkip(24);
        
        struct
        {
            float VPosX,VPosY,VPosZ;
            float VNPosX,VNPosY,VNPosZ;
            float UVPosX,UVPosY;
            if (Stride == 36)
                float Unknown;
        }VertexBuffer[VertexCount]<optimize=false>;
        
        FSeek(MeshDataBaseOffset + IndexOffset);
        struct
        {
            uint16 Index;
        }Indices[IndexCount];
    }Mesh;
}

 

  • Like 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...