Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions Source/functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "functions.h"

char* readFile(char *inputFile){
// This function reads a file into memory, and returns a pointer
printf("Reading binary file %s...\n", inputFile);
Expand All @@ -22,6 +24,86 @@ char* readFile(char *inputFile){
fclose(levelFile); // Closes file
return bytelist;
}

// This function requires a 24-bit bitmap with no compression
// The caller of the function is responsible for freeing the returned BYTE* memory
// Returns the pixel array in 3-byte RGB pixels, with rows bottom-to-top
BYTE* readBitmapFile(char* inputFilename)
{
char* image = readFile(inputFilename);

// Check bitmap integrity
BYTE bmpIntegrityA;
BYTE bmpIntegrityB;
memcpy(&bmpIntegrityA, image + 0, 1);
memcpy(&bmpIntegrityB, image + 1, 1);
//printf("Bitmap integrity: %c%c\n", bmpIntegrityA, bmpIntegrityB);
if (!(bmpIntegrityA == 'B' && bmpIntegrityB == 'M'))
{
printf("ERROR: File must be BM for Windows 3.1x, 95, NT, etc.\n");
free(image);
return NULL;
}

// Verify that the bitmap is 24 bits per pixel
unsigned short bitsPerPixel;
memcpy(&bitsPerPixel, image + 28, 2);
//printf("Bitmap bpp: %d", bitsPerPixel);
if (!bitsPerPixel == 24)
{
printf("ERROR: Bitmap must be 24 bits per pixel.\n");
free(image);
return NULL;
}

// Verify image is uncompressed
signed int compression;
memcpy(&compression, image + 30, 4);
//printf("Compression method: %d", compression);
if (compression != 0)
{
printf("ERROR: Bitmap must be uncompressed (found compression method %d, must be 0 a.k.a. BI_RGB).\n", compression);
free(image);
return NULL;
}

// Read width and height
signed int width;
signed int height;
memcpy(&width, image + 18, 4);
memcpy(&height, image + 22, 4);
//printf("Width x height: %d x %d\n", width, height);

// Read image size
signed int imageSize;
memcpy(&imageSize, image + 34, 4);
if (imageSize == 0)
{
// For BI_RGB, a dummy 0 may indicate bpp x w x h
imageSize = bitsPerPixel * width * height;
}
printf("Image size: %d\n", imageSize);

// Allocate memory for result
BYTE* result = malloc(imageSize * sizeof(BYTE));
if (result == 0)
{
printf("ERROR: Could not allocate memory to read bmp! (error code: %d)\n", errno);
return NULL;
}

// Determine the offset of the pixel array
signed int offset;
memcpy(&offset, image + 10, 4);
//printf("Bitmap pixel array offset: %d", offset);

memcpy(result, image + offset, imageSize);

free(image);

return result;
}

void writeFile(char *outputFile, char *bytelist, unsigned fsize){
// This function writes bytelist to the level file.
FILE *File = fopen(outputFile,"wb");
Expand Down Expand Up @@ -84,6 +166,9 @@ unsigned removerange(char *bytelist, unsigned offset, unsigned length, unsigned
return (fsize-length);
}

// This function writes a 24-bit bitmap (R8 G8 B8)
// In TR1, full black (0, 0, 0) represents transparent
// In TR2, full magenta (255, 0, 255) represents transparent
void writeBitmap(char* img, char* filename, const unsigned w, const unsigned h)
{
FILE* f;
Expand Down Expand Up @@ -118,3 +203,89 @@ void writeBitmap(char* img, char* filename, const unsigned w, const unsigned h)

fclose(f);
}

void writeBitmap32(char* img, char* filename, const unsigned w, const unsigned h)
{
FILE* f;
int bmpFileSize = 54 + 4 * w * h;

unsigned char bmpfileheader[14] = { 'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0 };
unsigned char bmpinfoheader[40] = { 40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 32,0 };

bmpfileheader[2] = (unsigned char)(bmpFileSize);
bmpfileheader[3] = (unsigned char)(bmpFileSize >> 8);
bmpfileheader[4] = (unsigned char)(bmpFileSize >> 16);
bmpfileheader[5] = (unsigned char)(bmpFileSize >> 24);

bmpinfoheader[4] = (unsigned char)(w);
bmpinfoheader[5] = (unsigned char)(w >> 8);
bmpinfoheader[6] = (unsigned char)(w >> 16);
bmpinfoheader[7] = (unsigned char)(w >> 24);
bmpinfoheader[8] = (unsigned char)(h);
bmpinfoheader[9] = (unsigned char)(h >> 8);
bmpinfoheader[10] = (unsigned char)(h >> 16);
bmpinfoheader[11] = (unsigned char)(h >> 24);

f = fopen(filename, "wb");
fwrite(bmpfileheader, 1, 14, f);
fwrite(bmpinfoheader, 1, 40, f);
for (int i = 0; i < h; i++)
{
fwrite(img + (w * (h - i - 1) * 4), 4, w, f);
}

fclose(f);
}

BYTE findPixelKey(BYTE* palette, BYTE r, BYTE g, BYTE b)
{
BYTE pr, pg, pb;
BYTE result;
short closestMatch = 255 * 3;
short diff;
BYTE isExactMatch = 0;
BYTE rpr, rpg, rpb;
for (int i = 0; i < 256; i++)
{
short curpos = i * 3;
memcpy(&pr, palette + curpos + 2, 1);
memcpy(&pg, palette + curpos + 1, 1);
memcpy(&pb, palette + curpos + 0, 1);

if (pr == r &&
pg == g &&
pb == b)
{
result = i;
isExactMatch = 1;
break;
}

diff = abs(pr - r) +
abs(pg - g) +
abs(pb - b);

if (diff < closestMatch)
{
closestMatch = diff;
result = i;
rpr = pr;
rpg = pg;
rpb = pb;
isExactMatch = 0;
}
}

/*
if (isExactMatch == 1)
{
printf("Exact match: %d, %d, %d matches palette %d\n", r, g, b, result);
}
else
{
printf("No match: %d, %d, %d is closest to palette %d (%d, %d, %d)\n", r, g, b, result, rpr, rpg, rpb);
}
*/

return result;
}
4 changes: 4 additions & 0 deletions Source/functions.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED

typedef unsigned char BYTE;

char* readFile(char *inputFile);
BYTE* readBitmapFile(char* inputFilename);
void writeFile(char *outputFile, char *bytelist, unsigned fsize);
unsigned filesize(char *inputFile);
char* substring(char *string, unsigned offset, unsigned length);
Expand All @@ -10,5 +13,6 @@ unsigned countchar(char *instring, char countChar);
char* insertrange(char *bytelist, unsigned offset, unsigned length, char *insertme, unsigned fsize);
unsigned removerange(char *bytelist, unsigned offset, unsigned length, unsigned fsize);
void writeBitmap(char* img, char* filename, const unsigned w, const unsigned h);
BYTE findPixelKey(BYTE* palette, BYTE r, BYTE g, BYTE b);

#endif
73 changes: 69 additions & 4 deletions Source/tr1pc.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ void tr1pc_main(int argc, char *args[], char *bytelist, unsigned fsize){
if (!_strnicmp(args[2],"get",3)&&!_strnicmp(args[3],"offset",6)) tr1pc_get_offset(bytelist, args, argc, p_NumVertices, p_NumSprites, p_NumLights, p_NumZSector, p_NumStaticMeshes, p_AlternateRoom, p_NumFloorData, p_NumItems, p_NumSpriteSequences, p_NumCameras, p_NumDemoData, p_NumBoxes, p_NumOverlaps, p_NumSoundDetails, p_NumSamples, p_NumSampleIndices, p_NumSoundSources, fsize);
if (!_strnicmp(args[2],"extract",7)&&!strnicmp(args[3],"textile",7)) tr1pc_extract_textile(bytelist, args, numTexTiles, p_TexTiles, palette, !strnicmp(args[4],"all",3) ? -1 : atoi(args[4]));

if (!_strnicmp(args[2], "replace", 7))
{
if (!strnicmp(args[3], "textile", 7))
{
tr1pc_replace_textile(bytelist, args, numTexTiles, p_TexTiles, palette, fsize);
}
}

free(p_TexTiles);
free(roomX);
free(roomY);
Expand Down Expand Up @@ -3152,8 +3160,9 @@ void tr1pc_get_offset(char *bytelist, char *args[], unsigned argc, unsigned p_Nu
if (!_strnicmp(args[4],"numsampleindices",16)) printf("%X\n",p_NumSampleIndices);
}

// This function extracts a texture tile from the level into a bitmap file. The syntax is "trmod [FILE] EXTRACT TEXTILE [#/All]".
// int selection // -1 for all, 0 - (numTexTiles-1) for an individual texture tile
void tr1pc_extract_textile(char* bytelist, char* args[], unsigned numTexTiles, unsigned* p_TexTiles, unsigned char* palette, int selection)
void tr1pc_extract_textile(char* bytelist, char* args[], unsigned numTexTiles, unsigned* p_TexTiles, BYTE* palette, int selection)
{
if (selection < -1 || selection >= (int)numTexTiles)
{
Expand All @@ -3162,17 +3171,16 @@ void tr1pc_extract_textile(char* bytelist, char* args[], unsigned numTexTiles, u
}

printf("Extracting textile %d...\n", selection);
// This function extracts a texture tile from the level into a bitmap file. The syntax is "trmod [FILE] EXTRACT TEXTILE [#/All]".

printf("Writing \"palette.bmp\"...\n");
writeBitmap(palette, "palette.bmp", 16, 16);

const unsigned w = 256;
const unsigned h = 256;

for (int t = (selection == -1 ? 0 : selection); t <= (selection == -1 ? numTexTiles-1 : selection); ++t)
for (int t = (selection == -1 ? 0 : selection); t <= (selection == -1 ? numTexTiles - 1 : selection); ++t)
{
unsigned char* img = (char*)malloc(3 * w * h);
BYTE* img = malloc(3 * w * h);
memset(img, 0, 3 * w * h); // Fill the whole image with 0s

for (int j = 0; j < h; j++)
Expand Down Expand Up @@ -3215,4 +3223,61 @@ void tr1pc_extract_textile(char* bytelist, char* args[], unsigned numTexTiles, u
free(img);
free(filename);
}
}

// This function replaces a texture tile in the level from a bitmap file. The syntax is "trmod [FILE] REPLACE TEXTILE [#] [FILENAME.BMP]".
void tr1pc_replace_textile(char* bytelist, char* args[], unsigned numTexTiles, unsigned* p_TexTiles, BYTE * palette, unsigned fsize)
{
unsigned int selection = atoi(args[4]);
char* filename = args[5];

if (selection < 0 || selection >= numTexTiles)
{
printf("ERROR: Textile selection must be between \"0\" and \"%d\", argument was \"%d\"", numTexTiles - 1, selection);
return 0;
}

printf("Replacing textile %d...\n", selection);

const short w = 256;
const short h = 256;

BYTE* bitmapImage = readBitmapFile(filename);
if (bitmapImage == NULL)
{
printf("ERROR: Could not read bitmap.\n");
return 0;
}

for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
unsigned int curpos = j * w + i;
BYTE r;
BYTE g;
BYTE b;
BYTE pixelKey;

memcpy(&r, bitmapImage + curpos * 3 + 2, 1);
memcpy(&g, bitmapImage + curpos * 3 + 1, 1);
memcpy(&b, bitmapImage + curpos * 3 + 0, 1);

if (r == 0 && g == 0 && b == 0)
{
pixelKey = 0; // Reserved for pure-black aka transparent
}
else
{
pixelKey = findPixelKey(palette, r, g, b);
}

unsigned int curposFlip = ((h - 1 - j) * w) + i; // Bitmap rows are stored bottom-up
memcpy(bytelist + p_TexTiles[selection] + curposFlip, &pixelKey, 1);
}
}

writeFile(args[1], bytelist, fsize);

free(bitmapImage);
}
5 changes: 4 additions & 1 deletion Source/tr1pc.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef TR1PC_H_INCLUDED
#define TR1PC_H_INCLUDED

typedef unsigned char BYTE;

void tr1pc_main(int argc, char *args[], char *bytelist, unsigned fsize);
void tr1pc_list(char *bytelist, char *args[], unsigned roomX[], int roomY[], unsigned roomZ[], unsigned p_NumVertices[], unsigned p_NumSprites[], unsigned p_NumLights[], unsigned p_NumZSector[], unsigned p_NumStaticMeshes[], unsigned p_AlternateRoom[], unsigned p_NumFloorData, unsigned p_NumSpriteSequences, unsigned p_NumCameras, unsigned p_NumBoxes, unsigned p_NumSoundSources, unsigned p_NumItems, unsigned short numRooms, unsigned fsize);
void tr1pc_add_item(char *bytelist, char *args[], unsigned roomX[], int roomY[], unsigned roomZ[], unsigned p_NumItems, unsigned fsize);
Expand Down Expand Up @@ -59,6 +61,7 @@ void tr1pc_replace_zone(char *bytelist, char *args[], unsigned p_NumBoxes, unsig
void tr1pc_add_overlap(char *bytelist, char *args[], unsigned argc, unsigned p_NumOverlaps, unsigned fsize);
void tr1pc_overwrite_overlap(char *bytelist, char *args[], unsigned argc, unsigned p_NumOverlaps, unsigned fsize);
void tr1pc_get_offset(char *bytelist, char *args[], unsigned argc, unsigned p_NumVertices[], unsigned p_NumSprites[], unsigned p_NumLights[], unsigned p_NumZSector[], unsigned p_NumStaticMeshes[], unsigned p_AlternateRoom[], unsigned p_NumFloorData, unsigned p_NumItems, unsigned p_NumSpriteSequences, unsigned p_NumCameras, unsigned p_NumDemoData, unsigned p_NumBoxes, unsigned p_NumOverlaps, unsigned p_NumSoundDetails, unsigned p_NumSamples, unsigned p_NumSampleIndices, unsigned p_NumSoundSources, unsigned fsize);
void tr1pc_extract_textile(char* bytelist, char* args[], unsigned numTexTiles, unsigned* p_TexTiles, unsigned char* palette, int selection);
void tr1pc_extract_textile(char *bytelist, char* args[], unsigned numTexTiles, unsigned* p_TexTiles, BYTE* palette, int selection);
void tr1pc_replace_textile(char* bytelist, char* args[], unsigned numTexTiles, unsigned* p_TexTiles, BYTE* palette, unsigned fsize);

#endif
8 changes: 8 additions & 0 deletions Source/tr1psx.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ void tr1psx_main(int argc, char *args[], char *bytelist, unsigned fsize){
if (!_strnicmp(args[2],"get",3)&&!_strnicmp(args[3],"offset",6)) tr1psx_get_offset(bytelist, args, argc, p_NumVertices, p_NumSprites, p_NumLights, p_NumZSector, p_NumStaticMeshes, p_AlternateRoom, p_NumFloorData, p_NumItems, p_NumSpriteSequences, p_NumCameras, p_NumBoxes, p_NumOverlaps, p_NumSoundSources, fsize);
if (!_strnicmp(args[2], "extract", 7) && !strnicmp(args[3], "textile", 7)) printf("EXTRACT TEXTILE is not supported for PSX file format.");

if (!_strnicmp(args[2], "replace", 7))
{
if (!strnicmp(args[3], "textile", 7))
{
printf("REPLACE TEXTILE is not supported for PSX file format.");
}
}

free(roomX);
free(roomY);
free(roomZ);
Expand Down
Loading