#include #include typedef struct atomic { unsigned int frameindex; unsigned int geometryindex; } atomic; typedef struct split { unsigned int indexcount; unsigned int matindex; /* PC */ unsigned int *indices; /* Vice City PS2 */ int splitpartcount; int *spltvc; float ***vertices; float ***uv; unsigned char ***vc; int ***normals; } split; typedef struct texture { unsigned short filterflags; char *tex; char *alpha; } texture; typedef struct material { char color[4]; unsigned int texcount; texture *tex; } material; typedef struct geometry { unsigned short flags; unsigned short nuv; unsigned int facec; unsigned int vertexc; unsigned int framec; float ambient; float diffuse; float specular; /* PC */ unsigned char **vcolors; float **uvcoords; unsigned short **faces; float **vertices; float **normals; unsigned int matcount; material *mat; unsigned int bmfacetype; unsigned int bmsplitcount; unsigned int bmindexcount; split *splt; } geometry; typedef struct frame { float rotmatrix[3][3]; float pos[3]; int parent; char *name; int display; } frame; typedef struct clump { unsigned int objcount; atomic *atm; unsigned int frmcount; frame *frm; unsigned int geocount; geometry *geo; } clump; typedef struct rwheader { unsigned int type; unsigned int size; unsigned int version; } rwheader; typedef struct coordinate { float x; float y; float z; } coordinate; rwheader readheader(FILE *rw) { rwheader rwh; fread(&rwh, 4, 3, rw); return rwh; } int issamevert(float *vert0, float *vert1) { if (vert0[0] == vert1[0] && vert0[1] == vert1[1] && vert0[2] == vert1[2]) { return 1; } else return 0; } void gettotalpos(frame *frm, int i, coordinate *bias) { if (frm[i].parent == -1) { bias->x += frm[i].pos[0]; bias->y += frm[i].pos[1]; bias->z += frm[i].pos[2]; return; } bias->x += frm[i].pos[0]; bias->y += frm[i].pos[1]; bias->z += frm[i].pos[2]; gettotalpos(frm, frm[i].parent, bias); } int main(int argc, char *argv[]) { FILE *dff, *obj, *log; int i, j, k, l, m, n, faceinc; rwheader rwh; clump clp; int rwvers; int nextgeo; int isnotactor; int end; int islast; coordinate bias; int subsecthdr; /* Used for VC */ int blocksize, nextblock; if (argc < 3) { printf("Usage: %s dff obj\n", argv[0]); exit(1); } if ((dff = fopen(argv[1], "rb")) == NULL) { printf("Couldn't open file: %s\n", argv[1]); exit(1); } if ((obj = fopen(argv[2], "wb")) == NULL) { printf("Couldn't open file: %s\n", argv[2]); exit(1); } if ((log = fopen("log.txt", "wb")) == NULL) { printf("Couldn't open file: log.txt\n"); exit(1); } rwh = readheader(dff); /* Clump */ rwvers = rwh.version; readheader(dff); /* Struct */ fread(&clp.objcount, 4, 1, dff); /* objcount = Atomic count */ /* Unknown, not used in GTA: III */ if (rwvers != 0x310) fseek(dff, 8, SEEK_CUR); /**/ /* Frames */ /**/ readheader(dff); /* Frame List */ readheader(dff); /* Struct */ /* Read Rotation Martrix, position and parentframe */ fread(&clp.frmcount, 4, 1, dff); clp.frm = (frame *) malloc(clp.frmcount*sizeof(frame)); for (i = 0; i < clp.frmcount; i++) { /* Read Struct */ fread(&clp.frm[i].rotmatrix, 4, 9, dff); fread(&clp.frm[i].pos, 4, 3, dff); fread(&clp.frm[i].parent, 4, 1, dff); fseek(dff, 4, SEEK_CUR); } /* Read names */ for (i = 0; i < clp.frmcount; i++) { rwh = readheader(dff); /* Extenstion */ j = rwh.size + ftell(dff); /* address of next Extension */ rwh = readheader(dff); /* Frame or HAnim PLG*/ if (rwh.type == 0x11E) { /* If HAnim PGL, skip section and skip Frame header */ fseek(dff, rwh.size, SEEK_CUR); rwh = readheader(dff); } clp.frm[i].name = (char *) malloc(rwh.size+1); fread(clp.frm[i].name, rwh.size, 1, dff); clp.frm[i].name[rwh.size] = '\0'; /* If HAnim PLG comes after Frame, jump to 'j' which we have * calculated before */ fseek(dff, j, 0); /* You can have the program ask, which frames are to be exported. * Uncomment this to enable this feature. * Every frame is to be exported by default. */ /* fprintf(stderr, "Shall object %s be displayed?\n", clp.frm[i].name); scanf("%d", &clp.frm[i].display); getchar(); */ clp.frm[i].display = 1; } /**/ /* Geometry */ /**/ readheader(dff); /* Geometry List */ readheader(dff); /* Struct */ /* Read geometry count and loop through geometries */ fread(&clp.geocount, 4, 1, dff); clp.geo = (geometry *) malloc(clp.geocount*sizeof(geometry)); for (j = 0; j < clp.geocount; j++) { rwh = readheader(dff); /* Geometry */ nextgeo = rwh.size + ftell(dff); /* Struct - nearly empty in ps2 version */ readheader(dff); fread(&clp.geo[j].flags, 2, 1, dff); fread(&clp.geo[j].nuv, 2, 1, dff); fread(&clp.geo[j].facec, 4, 1, dff); fread(&clp.geo[j].vertexc, 4, 1, dff); fread(&clp.geo[j].framec, 4, 1, dff); if ((rwvers == 0x0C02FFF || rwvers == 0x310) == 0) { fread(&clp.geo[j].ambient, 4, 1, dff); fread(&clp.geo[j].diffuse, 4, 1, dff); fread(&clp.geo[j].specular, 4, 1, dff); } fseek(dff, 24, SEEK_CUR); /* skip bounding sphere */ /* Material List */ readheader(dff); readheader(dff); /* Struct */ /* Read material count and loop through materials */ fread(&clp.geo[j].matcount, 4, 1, dff); clp.geo[j].mat = (material *) malloc(clp.geo[j].matcount*sizeof(material)); fseek(dff, 4*clp.geo[j].matcount, SEEK_CUR); /* unknown */ for (k = 0; k < clp.geo[j].matcount; k++) { readheader(dff); /* Material */ readheader(dff); /* Struct */ fseek(dff, 4, SEEK_CUR); /* unknown */ fread(&clp.geo[j].mat[k].color, 1, 4, dff); fseek(dff, 4, SEEK_CUR); /* unknown */ /* Read texture count and loop through textures */ fread(&clp.geo[j].mat[k].texcount, 1, 4, dff); clp.geo[j].mat[k].tex = (texture *) malloc(clp.geo[j].mat[k].texcount*sizeof(texture)); fseek(dff, 12, SEEK_CUR); /* unknown */ for (l = 0; l < clp.geo[j].mat[k].texcount; l++) { rwh = readheader(dff); /* Texture */ readheader(dff); /* Struct */ fread(&clp.geo[j].mat[k].tex[l].filterflags, 2, 1, dff); fseek(dff, 2, SEEK_CUR); /* unknown */ /* Texture name */ rwh = readheader(dff); /* String */ clp.geo[j].mat[k].tex[l].tex = (char *) malloc(rwh.size+1); fread(clp.geo[j].mat[k].tex[l].tex, rwh.size, 1, dff); clp.geo[j].mat[k].tex[l].tex[rwh.size] = '\0'; /* Alpha texture name */ rwh = readheader(dff); /* String */ clp.geo[j].mat[k].tex[l].alpha = (char *) malloc(rwh.size+1); fread(clp.geo[j].mat[k].tex[l].alpha, rwh.size, 1, dff); clp.geo[j].mat[k].tex[l].alpha[rwh.size] ='\0'; /* Extension - usually empty */ rwh = readheader(dff); fseek(dff, rwh.size, SEEK_CUR); } /* Extension - empty, used in 'San Andreas'. Not supported yet. */ rwh = readheader(dff); fseek(dff, rwh.size, SEEK_CUR); } rwh = readheader(dff); /* Extension */ end = rwh.size + ftell(dff); /* Bin Mesh PLG */ readheader(dff); fread(&clp.geo[j].bmfacetype, 4, 1, dff); fread(&clp.geo[j].bmsplitcount, 4, 1, dff); fread(&clp.geo[j].bmindexcount, 4, 1, dff); /* Loop through splits. * PC Version uses index here, PS2 not. * PC Version is not supported yet */ clp.geo[j].splt = (split *) malloc(clp.geo[j].bmsplitcount*sizeof(split)); for (i = 0; i < clp.geo[j].bmsplitcount; i++) { fread(&clp.geo[j].splt[i].indexcount, 4, 1, dff); fread(&clp.geo[j].splt[i].matindex, 4, 1, dff); } /**/ /* Native Data PLG - only PS2 */ /**/ rwh = readheader(dff); /* Struct - incorrect size in header */ readheader(dff); fprintf(log, "Native Data Struct: %lX\n", ftell(dff)); fseek(dff, 4, SEEK_CUR); /* Skip 04 00 00 00 */ fprintf(log, "Splitcount: %d\n", clp.geo[j].bmsplitcount); for (i = 0; i < clp.geo[j].bmsplitcount; i++) { fread(&blocksize, 4, 1, dff); /* splitsize */ nextblock = blocksize + ftell(dff) + 4; fread(&isnotactor, 4, 1, dff); if (isnotactor) fseek(dff, 16, SEEK_CUR); clp.geo[j].splt[i].vertices = (float ***) malloc(4); clp.geo[j].splt[i].uv = (float ***) malloc(4); clp.geo[j].splt[i].vc = (unsigned char ***) malloc(4); clp.geo[j].splt[i].normals = (int ***) malloc(4); clp.geo[j].splt[i].spltvc = (int *) malloc(4); /* Loop through splitparts */ for (l = 0, islast = 0; !islast; l++) { split cursplt; cursplt = clp.geo[j].splt[i]; cursplt.vertices = (float ***) realloc(cursplt.vertices, (l+1)*4); cursplt.uv = (float ***) realloc(cursplt.uv, (l+1)*4); cursplt.vc = (unsigned char ***) realloc(cursplt.vc, (l+1)*4); cursplt.normals = (int ***) realloc(cursplt.normals, (l+1)*4); cursplt.spltvc = (int *) realloc(cursplt.spltvc, (l+1)*4); if (!isnotactor) /* is actor */ fseek(dff, 48, SEEK_CUR); /* */ /* Vertices */ /* */ /* get vertex count in format: 00 80 vertex count 68 */ fprintf(log, "Vertex header at: %lX\n", ftell(dff)); fseek(dff, 12, SEEK_CUR); fread(&subsecthdr, 4, 1, dff); if ((subsecthdr & 0xff00ffff) != 0x68008000) { fprintf(stderr, "Vertex section not found\n"); exit(1); } cursplt.spltvc[l] = (subsecthdr & 0x00ff0000) >> 16; /* read Vertices */ cursplt.vertices[l] = (float **) malloc(cursplt.spltvc[l]*4); for (k = 0; k < cursplt.spltvc[l]; k++) { cursplt.vertices[l][k] = (float *) malloc(3*sizeof(float)); fread(&cursplt.vertices[l][k][0], 4, 1, dff); fread(&cursplt.vertices[l][k][1], 4, 1, dff); fread(&cursplt.vertices[l][k][2], 4, 1, dff); } if ((cursplt.spltvc[l]*12 % 16) != 0) fseek(dff, 16 - (cursplt.spltvc[l]*12 % 16), SEEK_CUR); /* skip padding */ /* */ /* UV Coordinates */ /* */ /* get vertex count in format: 01 80 vertex count 64 */ fprintf(log, "UV header at: %lX\n", ftell(dff)); fseek(dff, 12, SEEK_CUR); fread(&subsecthdr, 4, 1, dff); if ((subsecthdr & 0xff00ffff) != 0x64008001) { fprintf(stderr, "UV section not found\n"); exit(1); } cursplt.spltvc[l] = (subsecthdr & 0x00ff0000) >> 16; /* read UV Coordinates */ cursplt.uv[l] = (float **) malloc(cursplt.spltvc[l]*4); for (k = 0; k < cursplt.spltvc[l]; k++) { cursplt.uv[l][k] = (float *) malloc(2*sizeof(float)); fread(&cursplt.uv[l][k][0], 4, 1, dff); fread(&cursplt.uv[l][k][1], 4, 1, dff); } if ((cursplt.spltvc[l]*8 % 16) != 0) fseek(dff, 16 - (cursplt.spltvc[l]*8 % 16), SEEK_CUR); /* skip padding */ /* */ /* Vertexcolors */ /* */ /* get vertex count in format: 02 C0 vertex count 6E */ fprintf(log, "Vertexcolor header at: %lX\n", ftell(dff)); fseek(dff, 12, SEEK_CUR); fread(&subsecthdr, 4, 1, dff); if ((subsecthdr & 0xff00ffff) != 0x6E00C002) { fprintf(stderr, "Vertexcolor section not found\n"); exit(1); } cursplt.spltvc[l] = (subsecthdr & 0x00ff0000) >> 16; /* read Vertexcolors */ cursplt.vc[l] = (unsigned char **) malloc(cursplt.spltvc[l]*4); for (k = 0; k < cursplt.spltvc[l]; k++) { cursplt.vc[l] [k] = (unsigned char *) malloc(4*sizeof(unsigned char)); fread(&cursplt.vc[l][k][0], 1, 4, dff); } if ((cursplt.spltvc[l]*4 % 16) != 0) fseek(dff, 16 - (cursplt.spltvc[l]*4 % 16), SEEK_CUR); /* skip padding */ /* */ /* Normals */ /* */ /* get vertexcount in format: 03 80 vertexcount 6A */ fprintf(log, "Normal header at:%lX\n", ftell(dff)); fseek(dff, 12, SEEK_CUR); fread(&subsecthdr, 4, 1, dff); if ((subsecthdr & 0xff00ffff) != 0x6A008003) { fprintf(stderr, "Normal section not found\n"); exit(1); } cursplt.spltvc[l] = (subsecthdr & 0x00ff0000) >> 16; /* read Normals */ cursplt.normals[l] = (int **) malloc(cursplt.spltvc[l]*sizeof(int *)); for (k = 0; k < cursplt.spltvc[l]; k++) { cursplt.normals[l][k] = (int *) malloc(3*sizeof(int)); cursplt.normals[l][k][0] = getc(dff); cursplt.normals[l][k][1] = getc(dff); cursplt.normals[l][k][2] = getc(dff); } fprintf(log, "%X\n", cursplt.spltvc[l]); if ((cursplt.spltvc[l]*3 % 16) != 0) fseek(dff, 16 - (cursplt.spltvc[l]*3 % 16), SEEK_CUR); /* skip padding */ /* skip end of splitpart, but read, if this splitpart was the last */ fseek(dff, 8, SEEK_CUR); fread(&islast, 4, 1, dff); if (islast == 0x11000000) islast = 1; else islast = 0; fseek(dff, 4, SEEK_CUR); fprintf(log, "Split end: %d\n\n", islast); clp.geo[j].splt[i] = cursplt; } /* remember splitpartcount */ clp.geo[j].splt[i].splitpartcount = l; /* Jump to next split and skip unknowns */ fseek(dff, nextblock, 0); } fseek(dff, nextgeo, 0); /* Jump to next Geometry */ } clp.atm = (atomic *) malloc(clp.objcount*sizeof(atomic)); /* Loop through Atomics */ for (i = 0; i < clp.objcount; i++) { rwh = readheader(dff); readheader(dff); fread(&clp.atm[i].frameindex, 4, 1, dff); fread(&clp.atm[i].geometryindex, 4, 1, dff); fseek(dff, rwh.size-20, SEEK_CUR); } /* Write the obj File */ /* faceinc is used to increment face indices */ faceinc = 0; bias.x = 0; bias.y = 0; bias.z = 0; /* Loop through Atomics */ for (m = 0; m < clp.objcount; m++) { k = clp.atm[m].geometryindex; n = clp.atm[m].frameindex; if (clp.frm[n].display == 0) continue; gettotalpos(clp.frm, n, &bias); fprintf(obj, "g %s\n", clp.frm[n].name); for (i = 0; i < clp.geo[k].bmsplitcount; i++) { for (l = 0; l < clp.geo[k].splt[i].splitpartcount; l++) { split cursplt; cursplt = clp.geo[k].splt[i]; /* print every vertex */ for (j = 0; j < cursplt.spltvc[l]; j++) { fprintf(obj, "v %f %f %f\n", cursplt.vertices[l][j][0] + bias.x, cursplt.vertices[l][j][1] + bias.y, cursplt.vertices[l][j][2] + bias.z); } /* print UV coordinates */ for (j = 0; j < cursplt.spltvc[l]; j++) { fprintf(obj, "vt %f %f\n", cursplt.uv[l][j][0], 1 - cursplt.uv[l][j][1]); } /* Normals are not supported yet */ for (j = 0; j < cursplt.spltvc[l]; j++) { fprintf(obj, "vn %f %f %f\n", (float) cursplt.normals[l][j][0]/ (float) 127, (float) cursplt.normals[l][j][1]/ (float) 127, (float) cursplt.normals[l][j][2]/ (float) 127); } /* print out every face */ for (j = 2; j < cursplt.spltvc[l]; j++) { /* if two vertices of the same face are the same, begin a new face */ if (issamevert(cursplt.vertices[l][j], cursplt.vertices[l][j-1]) || issamevert(cursplt.vertices[l][j], cursplt.vertices[l][j-2]) || issamevert(cursplt.vertices[l][j-1], cursplt.vertices[l][j-2])) { continue; } if ((j % 2) == 0) fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", j-1+faceinc, j-1+faceinc, j-1+faceinc, j + faceinc, j + faceinc, j + faceinc, j+1+faceinc, j+1+faceinc, j+1+faceinc ); else fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", j+1+faceinc, j+1+faceinc, j+1+faceinc, j + faceinc, j + faceinc, j + faceinc, j-1+faceinc, j-1+faceinc, j-1+faceinc ); } faceinc += clp.geo[k].splt[i].spltvc[l]; } } bias.x = 0; bias.y = 0; bias.z = 0; } return 0; }