Assets - Converting Face Files to Bitmaps
Traffic Department 2192 comes with 3 files named NEWFACES plus an extension identifying the episode where they're used. The files have variable length because they contain a different number of images. The file name should be a dead givaway about the information inside. We'll take a close look at these files with the hex editor. We need to get these data in to a common format, such as BMP, that we can easily handle with graphics libraries or a game engine.
Faces File Format
The faces file share a few things in common with the SCR files we analyzed last time. We see section breaks again at at the 0x300 offset in each file. However, the files are much too big to contain just one image. In the game itself, the faces in the briefing scenes appear to occupy a quarter of the screen. If we guess that we're dealing with images of size 160x100, we should check the appropriate offset (0x4180) in the file for changes. There are changes: We see the start of another 0x300 sized section and another long data block. A new palette and a new image to use with it. The Faces file essentially appends a series of smaller SCR files together to store the game faces.
Conversion Process
Scan through each image, reading in the palette and then the 16,000 bytes of color indices. Match each index to an RGB triple in the palette. Write the resulting set colors to the output container to build a horizontal sprite strip. Be careful to calculate the proper position in the output surface since we can't directly stream to a sprite strip. This is done in lines 62-65 of the source code below.
C Source Code
We'll make a small C program that turns NEWFACES.TD, NEWFACES.TD2, and NEWFACES.TD3 in to bitmap files. In order to quickly reuse our SCRtoBMP code, we should rename the 3 files to have different names, such as NEWFACE1,TD1, NEWFACE2.TD2, and NEWFACE3.TD3 to avoid filename collisions after we change all extentions to .BMP. After testing the program one one file, run it against all the SCR files in the folder by redirecting the list of the names to the FCEtoBMP application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
/* Face to BMP file converter designed for use with Traffic Department 2192 NEWFACES files * argv[1] points to Face data file * * Five issues: * 1) This program is a security nightmare - don't compile and store * 2) No trapping, feedback, instructions, or any output whatsoever * 3) SDL_Surface normally needs to be locked due to concurrency * 4) Free the SDL surface - in this case, the OS does it * 5) Void pointer implicit cast (not portable to C++) * 6) User must preprocess input file Name and extension */ #include <stdio.h> /* Opening Files */ #include <string.h> /* Manipulating File Names */ #include <stdint.h> /* Fixed-width data types (C99) */ #include <SDL.H> /* Using SDL data structure */ int main(int argc, char* argv[]) { FILE *fin; /* Input File pointer */ uint32_t FILE_SIZE; /* Get file size */ uint8_t IMAGE_COUNT; /* Number of packed images */ uint16_t SURFACE_WIDTH; /* Width of the output surface */ SDL_Surface *surface; /* SDL Surface struct */ uint8_t *dst_byte; /* Pointer to surface struct Pixels */ uint8_t current_image; /* Named iterator */ uint16_t current_byte; /* Named iterator */ uint8_t palette[768]; /* Palette array: 3 x 8-bit*/ uint8_t src_byte; /* Single byte read from input */ uint8_t red_p, green_p, blue_p; /* Palette lookup bytes by color */ uint8_t image_row, image_col; /* Current image pixel location */ uint32_t surface_offset; /* Byte offset in surface */ uint8_t FILENAME_LEN; /* File Name length*/ char fout[12]; /* New filename container */ /* Open Face File and find file statistics*/ fin = fopen(argv[1],"rb"); fseek(fin, 0, SEEK_END); FILE_SIZE = ftell(fin); rewind(fin); IMAGE_COUNT = FILE_SIZE/16768; /* Make surface strip and pixel data pointer. Each Image is 160x100 */ SURFACE_WIDTH = IMAGE_COUNT * 160; surface = SDL_CreateRGBSurface(0, SURFACE_WIDTH, 100, 32, 0,0,0,0); dst_byte = surface->pixels; /* Process each image in file sequentially */ for(current_image=0; current_image < IMAGE_COUNT; current_image++) { /* Extract image palette - guaranteed 768 bytes*/ for (current_byte=0; current_byte<768; current_byte++) palette[current_byte] = fgetc(fin) << 2; /* Reranging values 63 -> 255 */ /* Extract 16kb of image data, match to palette, write to surface */ for (current_byte=0; current_byte<16000; current_byte++) { src_byte = fgetc(fin); /* Read Byte From File */ red_p = palette[src_byte*3]; /* Lookup Red Channel */ green_p = palette[src_byte*3+1]; /* Lookup Green Channel */ blue_p = palette[src_byte*3+2]; /* Lookup Blue Channel */ image_row = current_byte / 160; /* Int type clips fractions*/ image_col = current_byte % 160; surface_offset = (current_image*160 + image_row*SURFACE_WIDTH +image_col)*4; dst_byte[surface_offset] = blue_p; /* Set Blue channel pixel */ dst_byte[surface_offset+1] = green_p; /* Set Green channel pixel */ dst_byte[surface_offset+2] = red_p; /* Set Red channel pixel */ dst_byte[surface_offset+3] = 0xff; /* Set Alpha channel pixel */ } } fclose(fin); /* New output filename with .BMP extension */ FILENAME_LEN = strlen(argv[1]); /* Length of input file name */ strncpy(fout,argv[1],FILENAME_LEN-3); /* Chop off extension */ fout[FILENAME_LEN-3]='\0'; /* Nullterm it */ strncat(fout,"BMP",3); /* Attach BMP extension */ /* Save BMP*/ SDL_SaveBMP(surface, fout); return 0; } |