/*! * \file test/TombRaider.cpp * \brief Loads maps, meshes, textures... * * \author Mongoose */ #include #include #include #include #include #include #ifdef __TEST_TR5_DUMP_TGA #include #endif #ifdef DEBUG_MEMORY #include #endif void dump_textures(TombRaider *tr, char *mapname) { #ifdef __TEST_TR5_DUMP_TGA int i; unsigned char *image; unsigned char *bumpmap; char buffer[128]; FILE *f; if (!tr || !mapname) return; // Dump textures printf("\n\t[Texture dumping for '%s']\n", mapname); for (i = 0; i < tr->NumTextures(); i++) { tr->Texture(i, &image, &bumpmap); if (image) { snprintf(buffer, 128, "%s-%03i-texture.tga", mapname, i); f = fopen(buffer, "wb"); if (f) { if (!tga_save(f, image, 256, 256, 4)) printf("\tWrote texture %s\n", buffer); fclose(f); } snprintf(buffer, 128, "%s.lst", mapname); f = fopen(buffer, "a"); if (f) { fprintf(f, "%s-%03i-texture.tga;\n", mapname, i); fclose(f); } delete [] image; } if (bumpmap) { snprintf(buffer, 64, "%s-%03i-bumpmap.tga", mapname, i); f = fopen(buffer, "wb"); if (f) { if (!tga_save(f, bumpmap, 256, 256, 4)) printf("\tWrote texture %s\n", buffer); fclose(f); } delete [] bumpmap; } } for (i = 0; i < tr->NumSpecialTextures(); i++) { image = tr->SpecialTexTile(i); snprintf(buffer, 128, "%s-%03i-special.tga", mapname, i); f = fopen(buffer, "wb"); if (f) { if (!tga_save(f, image, 256, 256, 4)) printf("\tWrote texture %s\n", buffer); fclose(f); } else { printf("\tFailed to write texture %s\n", buffer); } delete [] image; } #else printf("Texture dumping not in this build\n"); #endif } void dump_mesh(TombRaider *tr, char *mapname, int index) { tr2_object_texture_t *object_texture = NULL; tr2_mesh_t *meshes = NULL; unsigned int v, check; int i, triangles, rectangles, t_index; char buffer[128]; float rgba[4]; float s, t; char id[8]; FILE *f; if (!mapname || !tr) { return; } snprintf(buffer, 128, "%s-%03i.mesh", mapname, index); object_texture = tr->ObjectTextures(); meshes = tr->Mesh(); f = fopen(buffer, "wb"); if (!f) { perror("Failed to write mesh :"); return; } // Setup header id and check points strncpy(id, "TRMESH", 7); id[7] = 0; check = 0xcdcdcdcd; fwrite(id, 8, 1, f); fwrite(&meshes[index].num_vertices, 2, 1, f); fwrite(&meshes[index].num_textured_triangles, 2, 1, f); fwrite(&meshes[index].num_textured_rectangles, 2, 1, f); fwrite(&meshes[index].num_coloured_triangles, 2, 1, f); fwrite(&meshes[index].num_coloured_rectangles, 2, 1, f); fwrite(&meshes[index].collision_size, 4, 1, f); // Textured triangles //////////////////////// fwrite(&check, 4, 1, f); triangles = meshes[index].num_textured_triangles; for (i = 0; triangles > 0 && i < triangles; i++) { t_index = meshes[index].textured_triangles[i].texture; // Store texture info fwrite(&object_texture[t_index].tile, 2, 1, f); fwrite(&object_texture[t_index].transparency_flags, 2, 1, f); // Store vertices v = meshes[index].textured_triangles[i].vertices[0]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].textured_triangles[i].vertices[1]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].textured_triangles[i].vertices[2]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); // Store texels s = tr->adjustTexel(object_texture[t_index].vertices[0].xpixel, object_texture[t_index].vertices[0].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[0].ypixel, object_texture[t_index].vertices[0].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = tr->adjustTexel(object_texture[t_index].vertices[1].xpixel, object_texture[t_index].vertices[1].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[1].ypixel, object_texture[t_index].vertices[1].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = tr->adjustTexel(object_texture[t_index].vertices[2].xpixel, object_texture[t_index].vertices[2].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[2].ypixel, object_texture[t_index].vertices[2].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); } fwrite(&check, 4, 1, f); // Textured rectangles //////////////////////// fwrite(&check, 4, 1, f); rectangles = meshes[index].num_textured_rectangles; for (i = 0; rectangles > 0 && i < rectangles; i++) { t_index = meshes[index].textured_rectangles[i].texture; // Store texture info fwrite(&object_texture[t_index].tile, 2, 1, f); fwrite(&object_texture[t_index].transparency_flags, 2, 1, f); // Store vertices v = meshes[index].textured_rectangles[i].vertices[0]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].textured_rectangles[i].vertices[1]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].textured_rectangles[i].vertices[2]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].textured_rectangles[i].vertices[3]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); // Store texels s = tr->adjustTexel(object_texture[t_index].vertices[0].xpixel, object_texture[t_index].vertices[0].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[0].ypixel, object_texture[t_index].vertices[0].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = tr->adjustTexel(object_texture[t_index].vertices[1].xpixel, object_texture[t_index].vertices[1].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[1].ypixel, object_texture[t_index].vertices[1].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = tr->adjustTexel(object_texture[t_index].vertices[2].xpixel, object_texture[t_index].vertices[2].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[2].ypixel, object_texture[t_index].vertices[2].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = tr->adjustTexel(object_texture[t_index].vertices[3].xpixel, object_texture[t_index].vertices[3].xcoordinate); t = tr->adjustTexel(object_texture[t_index].vertices[3].ypixel, object_texture[t_index].vertices[3].ycoordinate); fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); } fwrite(&check, 4, 1, f); // Coloured triangles //////////////////////// fwrite(&check, 4, 1, f); triangles = meshes[index].num_coloured_triangles; for (i = 0; triangles > 0 && i < triangles; i++) { // Store vertices v = meshes[index].coloured_triangles[i].vertices[0]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].coloured_triangles[i].vertices[1]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].coloured_triangles[i].vertices[2]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); // Store color switch (tr->Engine()) { case TR_VERSION_1: tr->getColor(meshes[index].coloured_triangles[i].texture & 0xff, rgba); break; case TR_VERSION_UNKNOWN: case TR_VERSION_2: case TR_VERSION_3: case TR_VERSION_4: case TR_VERSION_5: tr->getColor((meshes[index].coloured_triangles[i].texture>>8) & 0xff, rgba); break; } s = rgba[0]; t = rgba[1]; fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = rgba[2]; t = rgba[3]; fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); } fwrite(&check, 4, 1, f); // Coloured rectangles //////////////////////// fwrite(&check, 4, 1, f); rectangles = meshes[index].num_coloured_rectangles; for (i = 0; rectangles > 0 && i < rectangles; i++) { // Store vertices v = meshes[index].coloured_rectangles[i].vertices[0]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].coloured_rectangles[i].vertices[1]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].coloured_rectangles[i].vertices[2]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); v = meshes[index].coloured_rectangles[i].vertices[3]; fwrite(&meshes[index].vertices[v].x, 2, 1, f); fwrite(&meshes[index].vertices[v].y, 2, 1, f); fwrite(&meshes[index].vertices[v].z, 2, 1, f); // Store color switch (tr->Engine()) { case TR_VERSION_1: tr->getColor(meshes[index].coloured_rectangles[i].texture & 0xff, rgba); break; case TR_VERSION_UNKNOWN: case TR_VERSION_2: case TR_VERSION_3: case TR_VERSION_4: case TR_VERSION_5: tr->getColor((meshes[index].coloured_rectangles[i].texture>>8) & 0xff, rgba); break; } s = rgba[0]; t = rgba[1]; fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); s = rgba[2]; t = rgba[3]; fwrite(&s, 4, 1, f); fwrite(&t, 4, 1, f); } fwrite(&check, 4, 1, f); fclose(f); printf("."); fflush(stdout); } void dump_lara_stuff(char *mapname, TombRaider &tr) { unsigned int i, j, k, n; tr2_moveable_t *moveable = tr.Moveable(); unsigned int numMoveables = tr.NumMoveables(); char filename[64]; unsigned char *riff; unsigned int riffSz, total = 0; FILE *f; snprintf(filename, 63, "%s-lara.nfo", mapname); f = fopen(filename, "w"); if (!f) { perror("Failed to write lara.nfo: "); return; } for (i = 0; i < numMoveables; ++i) { j = n = 0; if (moveable[i].object_id == 0) { j = moveable[i].starting_mesh; n = moveable[i].num_meshes + j; fprintf(f, "Lara (%u)\n", i); } else if (moveable[i].object_id == 30) { j = moveable[i].starting_mesh; n = moveable[i].num_meshes + j; fprintf(f, "Lara ponytail\n"); } else if (tr.Engine() == TR_VERSION_4) { switch (moveable[i].object_id) { case 8: // Joints, ( interconnecting skin/ploys ) case 9: fprintf(f, "TR4 lara joints (%u)\n", i); j = moveable[i].starting_mesh; n = moveable[i].num_meshes + j; } } for (k = j; j < n; ++j) { fprintf(f, "\tMesh[%i] = %u\n", (j - k), j); } } fclose(f); printf("\nDumping %u audio samples: ", tr.getSoundSamplesCount()); for (i = 0, j = 0; i < tr.getSoundSamplesCount(); ++i) { tr.getSoundSample(i, &riffSz, &riff); total += riffSz; snprintf(filename, 63, "%s-%03i.wav", mapname, j++); f = fopen(filename, "wb"); if (!f) { perror("Failed to write riff.wav: "); continue; } fwrite(riff, 1, riffSz, f); fclose(f); delete [] riff; printf("."); fflush(stdout); } printf("\n"); if (total) { printf("Dumped %ubytes (%.2f MB) of audio samples\n", total, (float)total/1024000.0); } } void percent_callback(int p) { printf("Level %i%% loaded\n", p); } int test_main(int argc, char *argv[]) { TombRaider tr; char mapname[128]; int len, i, j; printf("[TombRaider class test]\n"); tr.setDebug(true); if (argc > 2) { // Strip for mapname ////////////////////////////// len = strlen(argv[2]); for (i = len, j = 0; i > 0; i--, j++) if (argv[2][i] == '/' || argv[2][i] == '\\') break; j--; memset(mapname, 0, 128); for (i = 0; i < len - j && i < 30; i++) mapname[i] = argv[2][i + len - j]; //////////////////////////////////////////////////// if (strncmp(argv[1], "load", 4) == 0) { if (!tr.Load(argv[2], percent_callback)) { printf("\nmain: Load reports success.\n"); } } else if (strncmp(argv[1], "dump", 4) == 0) { if (!tr.Load(argv[2], percent_callback)) { printf("\nmain: Load reports success.\n"); dump_textures(&tr, mapname); printf("Mesh dumping: "); for (i = 0; i < tr.getMeshCount(); i++) { dump_mesh(&tr, mapname, i); } if (argc > 3) { printf("\nLoading external sound SFX.\n"); tr.loadSFX(argv[3]); } dump_lara_stuff(mapname, tr); printf("\n"); } else { printf("\nmain: Load failed.\n"); } } else printf("\n\n%s [ load | dump ] filename [sound.sfx]\n", argv[0]); } else { printf("\n\n%s [ load | dump ] filename [sound.sfx]\n", argv[0]); } return 0; } int main(int argc, char *argv[]) { test_main(argc, argv); #ifdef DEBUG_MEMORY dump_memory_report(); #endif return 0; }