
// Black Desert Online file structure definition
// Copyright 2020 TNT

// Released under CC Attribution 4.0 license
// https://creativecommons.org/licenses/by/4.0/


#include "standard-types.hsl"
#pragma maxarray(65536)

#pragma enumsize(2)
#pragma enumsign("unsigned")
typedef enum BDO_FILETYPE
{
    BDO_FT_UNKNOWN = 0,
    BDO_MODEL_FILE_PAC = 259,
    BDO_ANIMATION_FILE_PAA = 514,
    BDO_SKELETON_FILE_PAB = 769,
} BDO_FILETYPE;

typedef struct BDO_ANIMATION_BONE_SCALE_KEYFRAME
{
    halffloat hfScaleKeyframe;
    halffloat hfScaleX;
    halffloat hfScaleY;
    halffloat hfScaleZ;
} BDO_ANIM_BONE_SCALE_KF;

typedef struct BDO_ANIMATION_BONE_ROTATION_KEYFRAME
{
    halffloat hfRotationKeyframe;
    halffloat hfRotationX;
    halffloat hfRotationY;
    halffloat hfRotationZ;
    halffloat hfRotationW;
} BDO_ANIM_BONE_ROTATION_KF;

typedef struct BDO_ANIMATION_BONE_TRANSLATION_KEYFRAME
{
    halffloat hfTranslationKeyframe;
    halffloat hfTranslationX;
    halffloat hfTranslationY;
    halffloat hfTranslationZ;
} BDO_ANIM_BONE_POS_KF;

typedef struct BDO_ANIMATION_BONE
{
    DWORD dwBoneHash;
    USHORT uwKeyframeScaleCount;
    struct BDO_ANIMATION_BONE_SCALE_KEYFRAME sScalingKeyframes[uwKeyframeScaleCount];
    USHORT uwKeyframeRotationCount;
    struct BDO_ANIMATION_BONE_ROTATION_KEYFRAME sRotationKeyframes[uwKeyframeRotationCount];
    USHORT uwKeyframeTranslationCount;
    struct BDO_ANIMATION_BONE_TRANSLATION_KEYFRAME sTranslationKeyframes[uwKeyframeTranslationCount];
} BDO_ANIM_BONE;

typedef struct BDO_SKELE_BONE_QUATERNION
{
    FLOAT x;
    FLOAT y;
    FLOAT z;
    FLOAT w;
} BDO_QUAT;

typedef struct BDO_SKELE_BONE_MATRIX
{
    BDO_QUAT row1;
    BDO_QUAT row2;
    BDO_QUAT row3;
    BDO_QUAT row4;
} BDO_MATRIX;

typedef struct BDO_SKELE_BONE_DEFINITION
{
    DWORD dwBoneHash;
    UBYTE ubBoneNameLen;
    CHAR cBoneName[ubBoneNameLen];
    LONG dwBoneParent;
    BDO_MATRIX mBoneMatrix;
    BDO_MATRIX mBoneMatrixInverse;
    BDO_MATRIX mBoneLocalMatrix;
    BDO_MATRIX mBoneLocalMatrixInverse;
    FLOAT fBoneScaleX;
    FLOAT fBoneScaleY;
    FLOAT fBoneScaleZ;
    FLOAT fBoneRotQuatX;
    FLOAT fBoneRotQuatY;
    FLOAT fBoneRotQuatZ;
    FLOAT fBoneRotQuatW;
    FLOAT fBonePositionW;
    FLOAT fBonePositionY;
    FLOAT fBonePositionZ;
    halffloat hfNoise1;
} BDO_SKELE_BONE;

typedef struct BDO_HEADER {
    DWORD dwMagic;
    BDO_FILETYPE uwVersion;
    
    __verify(dwMagic == 0x20524150);
    
    switch (uwVersion)
    {
        case BDO_MODEL_FILE_PAC:
            BYTE bNoise1[10];
            UBYTE ubBoneCount;
            DWORD dwBoneHash[ubBoneCount];
            //UBYTE ubUsedBoneCount;
            //UBYTE ubUsedBones[ubUsedBoneCount];
            //DWORD dwTotalVertices;
            //DWORD dwTotalFaces;
            //USHORT usMeshCount;

            break;
        
        case BDO_ANIMATION_FILE_PAA:
            BYTE bNoise1[10];
            USHORT ubBoneCount;
            FLOAT fDuration;
            DWORD wMiscCount;
            struct BDO_ANIMATION_BONE sBones[ubBoneCount];
            break;
        
        case BDO_SKELETON_FILE_PAB:
            BYTE bNoise1[10];
            USHORT ubBoneCount;
            struct BDO_SKELE_BONE_DEFINITION sBones[ubBoneCount];
            break;
        
        default:
            break;
    };
} BDO_HEADER;

typedef struct BDO_MODEL_USEDBONES {
    UBYTE ubBoneCount;
    UBYTE ubUsedBones[ubBoneCount];
} BDO_MODEL_USEDBONES;

typedef struct BDO_MODEL_MESHINFO {
    DWORD dwTotalVertices;
    DWORD dwTotalFaces;
    USHORT usMeshCount;
    UBYTE ubMeshNameLen;
} BDO_MODEL_MESHINFO;



function ParseBDO
{
    // Move to beginning
    __setCaretPos(0);
    
    UWord uwVersion = __getUWordAt(4);
    UQWord pos = 0;
    
    pos = __addStructureAt(0, "BDO_HEADER", "");
    
    __setCaretPos(16);
    
    switch (uwVersion)
    {
        case 259:   // pac ? 0x0103
            // Model file
            // For whatever reason, my version of Hex Workshop doesn't align the bytes correctly when parsing the file in the
            // struct definition above, so I wrote spaghetti code here to get around said hiccup.
            UByte ubBoneCount = __getUByteAt(16);
            __setCaretPos(17);
            for (UByte u = 0; u < ubBoneCount; u++)
            {
                __setCaretPos(__getCaretPos() + 4);
            };
            pos = __addStructureAt(__getCaretPos(), "BDO_MODEL_USEDBONES", "");
            UByte ubUsedBoneCount = __getUByteAt(__getCaretPos());
            __setCaretPos(__getCaretPos() + 1);
            for (u = 0; u < ubUsedBoneCount; u++)
            {
                __setCaretPos(__getCaretPos() + 1);
            };
            pos = __addStructureAt(__getCaretPos(), "BDO_MODEL_MESHINFO", "");
            __setCaretPos(__getCaretPos() + 16);
            break;
        
        case 514:	// paa ? 0x0202
            // Animation file, no additional work needed here
            break;
        
        case 769:   // pab ? 0x0301
            // Skeleton file, no additional work needed here
            break;
        
        default:
            break;
    };
};
