Kart-Public/tools/lumpmod/lump.c

474 lines
14 KiB
C
Raw Permalink Normal View History

2014-03-15 16:59:03 +00:00
/*
LumpMod v0.2.1, a command-line utility for working with lumps in wad
files.
Copyright (C) 2003 Thunder Palace Entertainment.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
lump.c: Provides functions for dealing with lumps
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "lump.h"
/* Read contents of a wad file and store them in memory.
* fpoint is the file to read, opened with "rb" mode.
* A pointer to a new wadfile struct will be returned, or NULL on error.
*/
struct wadfile *read_wadfile(FILE *fpoint) {
struct wadfile *wfptr;
struct lumplist *curlump;
long diroffset, filelen;
unsigned long count;
/* Allocate memory for wadfile struct */
wfptr = malloc(sizeof(struct wadfile));
if(wfptr == NULL) return NULL;
/* Read first four characters (PWAD or IWAD) */
if(fread(wfptr->id, 4, 1, fpoint) < 1) {
free(wfptr);
return NULL;
}
/* Read number of lumps */
if(fread(&(wfptr->numlumps), 4, 1, fpoint) < 1) {
free(wfptr);
return NULL;
}
/* If number of lumps is zero, nothing more needs to be done */
if(wfptr->numlumps == 0) {
wfptr->head = NULL;
return wfptr;
}
/* Read offset of directory */
if(fread(&diroffset, 4, 1, fpoint) < 1) {
free(wfptr);
return NULL;
}
/* Verify that the directory as long as it needs to be */
fseek(fpoint, 0, SEEK_END);
filelen = ftell(fpoint);
if((filelen - diroffset) / DIRENTRYLEN < wfptr->numlumps) {
free(wfptr);
return NULL;
}
/* Allocate memory for head lumplist item and set head pointer */
curlump = malloc(sizeof(struct lumplist));
if(curlump == NULL) {
free(wfptr);
return NULL;
}
wfptr->head = curlump;
curlump->cl = NULL;
/* Read directory entries and lumps */
for(count = 0; count < wfptr->numlumps; count++) {
long lumpdataoffset;
/* Advance to a new list item */
curlump->next = malloc(sizeof(struct lumplist));
if(curlump->next == NULL) {
free_wadfile(wfptr);
return NULL;
}
curlump = curlump->next;
curlump->next = NULL;
/* Allocate memory for the lump info */
curlump->cl = malloc(sizeof(struct lump));
if(curlump->cl == NULL) {
free_wadfile(wfptr);
return NULL;
}
/* Seek to the proper position in the file */
if(fseek(fpoint, diroffset + (count * DIRENTRYLEN), SEEK_SET) != 0) {
free_wadfile(wfptr);
return NULL;
}
/* Read offset of lump data */
if(fread(&lumpdataoffset, 4, 1, fpoint) < 1) {
free_wadfile(wfptr);
return NULL;
}
/* Read size of lump in bytes */
if(fread(&(curlump->cl->len), 4, 1, fpoint) < 1) {
free_wadfile(wfptr);
return NULL;
}
/* Read lump name */
if(fread(curlump->cl->name, 8, 1, fpoint) < 1) {
free_wadfile(wfptr);
return NULL;
}
/* Read actual lump data, unless lump size is 0 */
if(curlump->cl->len > 0) {
if(fseek(fpoint, lumpdataoffset, SEEK_SET) != 0) {
free_wadfile(wfptr);
return NULL;
}
/* Allocate memory for data */
curlump->cl->data = malloc(curlump->cl->len);
if(curlump->cl->data == NULL) {
free_wadfile(wfptr);
return NULL;
}
/* Fill the data buffer */
if(fread(curlump->cl->data, curlump->cl->len, 1, fpoint) < 1) {
free_wadfile(wfptr);
return NULL;
}
} else curlump->cl->data = NULL;
} /* End of directory reading loop */
return wfptr;
}
/* Free a wadfile from memory as well as all related structures.
*/
void free_wadfile(struct wadfile *wfptr) {
struct lumplist *curlump, *nextlump;
if(wfptr == NULL) return;
curlump = wfptr->head;
/* Free items in the lump list */
while(curlump != NULL) {
/* Free the actual lump and its data, if necessary */
if(curlump->cl != NULL) {
if(curlump->cl->data != NULL) free(curlump->cl->data);
free(curlump->cl);
}
/* Advance to next lump and free this one */
nextlump = curlump->next;
free(curlump);
curlump = nextlump;
}
free(wfptr);
}
/* Write complete wadfile to a file stream, opened with "wb" mode.
* fpoint is the stream to write to.
* wfptr is a pointer to the wadfile structure to use.
* Return zero on success, nonzero on failure.
*/
int write_wadfile(FILE *fpoint, struct wadfile *wfptr) {
struct lumplist *curlump;
long lumpdataoffset, diroffset;
if(wfptr == NULL) return 1;
/* Write four-character ID ("PWAD" or "IWAD") */
if(fwrite(wfptr->id, 4, 1, fpoint) < 1) return 2;
/* Write number of lumps */
if(fwrite(&(wfptr->numlumps), 4, 1, fpoint) < 1) return 3;
/* Offset of directory is not known yet. For now, write number of lumps
* again, just to fill the space.
*/
if(fwrite(&(wfptr->numlumps), 4, 1, fpoint) < 1) return 4;
/* Loop through lump list, writing lump data */
for(curlump = wfptr->head; curlump != NULL; curlump = curlump->next) {
/* Don't write anything for the head of the lump list or for lumps of
zero length */
if(curlump->cl == NULL || curlump->cl->data == NULL) continue;
/* Write the data */
if(fwrite(curlump->cl->data, curlump->cl->len, 1, fpoint) < 1)
return 5;
}
/* Current position is where directory will start */
diroffset = ftell(fpoint);
/* Offset for the first lump's data is always 12 */
lumpdataoffset = 12;
/* Loop through lump list again, this time writing directory entries */
for(curlump = wfptr->head; curlump != NULL; curlump = curlump->next) {
/* Don't write anything for the head of the lump list */
if(curlump->cl == NULL) continue;
/* Write offset for lump data */
if(fwrite(&lumpdataoffset, 4, 1, fpoint) < 1) return 6;
/* Write size of lump data */
if(fwrite(&(curlump->cl->len), 4, 1, fpoint) < 1) return 7;
/* Write lump name */
if(fwrite(curlump->cl->name, 8, 1, fpoint) < 1) return 8;
/* Increment lumpdataoffset variable as appropriate */
lumpdataoffset += curlump->cl->len;
}
/* Go back to header and write the proper directory offset */
fseek(fpoint, 8, SEEK_SET);
if(fwrite(&diroffset, 4, 1, fpoint) < 1) return 9;
return 0;
}
/* Get the name of a lump, as a null-terminated string.
* item is a pointer to the lump (not lumplist) whose name will be obtained.
* Return NULL on error.
*/
char *get_lump_name(struct lump *item) {
char convname[9], *retname;
if(item == NULL) return NULL;
memcpy(convname, item->name, 8);
convname[8] = '\0';
retname = malloc(strlen(convname) + 1);
if(retname != NULL) strcpy(retname, convname);
return retname;
}
/* Find the lump after start and before end having a certain name.
* Return a pointer to the list item for that lump, or return NULL if no lump
* by that name is found or lumpname is too long.
* lumpname is a null-terminated string.
* If end parameter is NULL, search to the end of the entire list.
*/
struct lumplist *find_previous_lump(struct lumplist *start, struct lumplist
*end, char *lumpname) {
struct lumplist *curlump, *lastlump;
char *curname;
int found = 0;
/* Verify that parameters are valid */
if(start==NULL || start==end || lumpname==NULL || strlen(lumpname) > 8)
return NULL;
/* Loop through the list from start parameter */
lastlump = start;
for(curlump = start->next; curlump != end && curlump != NULL;
curlump = curlump->next) {
/* Skip header lump */
if(curlump->cl == NULL) continue;
/* Find name of this lump */
curname = get_lump_name(curlump->cl);
if(curname == NULL) continue;
/* Compare names to see if this is the lump we want */
if(strcmp(curname, lumpname) == 0) {
found = 1;
break;
}
/* Free memory allocated to curname */
free(curname);
lastlump = curlump;
}
if(found) return lastlump;
return NULL;
}
/* Remove a lump from the list, free it, and free its data.
* before is the lump immediately preceding the lump to be removed.
* wfptr is a pointer to the wadfile structure to which the removed lump
* belongs, so that numlumps can be decreased.
*/
void remove_next_lump(struct wadfile *wfptr, struct lumplist *before) {
struct lumplist *removed;
/* Verify that parameters are valid */
if(before == NULL || before->next == NULL || wfptr == NULL) return;
/* Update linked list to omit removed lump */
removed = before->next;
before->next = removed->next;
/* Free lump info and data if necessary */
if(removed->cl != NULL) {
if(removed->cl->data != NULL) free(removed->cl->data);
free(removed->cl);
}
free(removed);
/* Decrement numlumps */
wfptr->numlumps--;
}
/* Add a lump.
* The lump will follow prev in the list and be named name, with a data size
* of len.
* A copy will be made of the data.
* Return zero on success or nonzero on failure.
*/
int add_lump(struct wadfile *wfptr, struct lumplist *prev, char *name, long
len, unsigned char *data) {
struct lump *newlump;
struct lumplist *newlumplist;
unsigned char *copydata;
/* Verify that parameters are valid */
if(wfptr == NULL || prev == NULL || name == NULL || strlen(name) > 8)
return 1;
/* Allocate space for newlump and newlumplist */
newlump = malloc(sizeof(struct lump));
newlumplist = malloc(sizeof(struct lumplist));
if(newlump == NULL || newlumplist == NULL) return 2;
/* Copy lump data and set up newlump */
if(len == 0 || data == NULL) {
newlump->len = 0;
newlump->data = NULL;
} else {
newlump->len = len;
copydata = malloc(len);
if(copydata == NULL) return 3;
memcpy(copydata, data, len);
newlump->data = copydata;
}
/* Set name of newlump */
memset(newlump->name, '\0', 8);
if(strlen(name) == 8) memcpy(newlump->name, name, 8);
else strcpy(newlump->name, name);
/* Set up newlumplist and alter prev appropriately */
newlumplist->cl = newlump;
newlumplist->next = prev->next;
prev->next = newlumplist;
/* Increment numlumps */
wfptr->numlumps++;
return 0;
}
/* Rename a lump.
* renamed is a pointer to the lump (not lumplist) that needs renaming.
* newname is a null-terminated string with the new name.
* Return zero on success or nonzero on failure.
*/
int rename_lump(struct lump *renamed, char *newname) {
/* Verify that parameters are valid. */
if(newname == NULL || renamed == NULL || strlen(newname) > 8) return 1;
/* Do the renaming. */
memset(renamed->name, '\0', 8);
if(strlen(newname) == 8) memcpy(renamed->name, newname, 8);
else strcpy(renamed->name, newname);
return 0;
}
/* Find the last lump in a wadfile structure.
* Return this lump or NULL on failure.
*/
struct lumplist *find_last_lump(struct wadfile *wfptr) {
struct lumplist *curlump;
if(wfptr == NULL || wfptr->head == NULL) return NULL;
curlump = wfptr->head;
while(curlump->next != NULL) curlump = curlump->next;
return curlump;
}
/* Find the last lump between start and end.
* Return this lump or NULL on failure.
*/
struct lumplist *find_last_lump_between(struct lumplist *start, struct
lumplist *end) {
struct lumplist *curlump;
if(start == NULL) return NULL;
curlump = start;
while(curlump->next != end) {
curlump = curlump->next;
if(curlump == NULL) break;
}
return curlump;
}
/* Find the next section lump. A section lump is MAPxx (0 <= x <= 9), ExMy
* (0 <= x <= 9, 0 <= y <= 9), or any lump whose name ends in _START or _END.
* Return NULL if there are no section lumps after start.
*/
struct lumplist *find_next_section_lump(struct lumplist *start) {
struct lumplist *curlump, *found = NULL;
char *curname;
/* Verify that parameter is valid */
if(start == NULL || start->next == NULL) return NULL;
/* Loop through the list from start parameter */
for(curlump = start->next; curlump != NULL && found == NULL;
curlump = curlump->next) {
/* Skip header lump */
if(curlump->cl == NULL) continue;
/* Find name of this lump */
curname = get_lump_name(curlump->cl);
if(curname == NULL) continue;
/* Check to see if this is a section lump */
if(strlen(curname) == 5 && strncmp("MAP", curname, 3) == 0 &&
isdigit(curname[3]) && isdigit(curname[4]))
found = curlump;
else if(strlen(curname) == 4 && curname[0] == 'E' && curname[2] ==
'M' && isdigit(curname[1]) && isdigit(curname[3]))
found = curlump;
else if(strlen(curname) == 7 && strcmp("_START", &curname[1]) == 0)
found = curlump;
else if(strlen(curname) == 8 && strcmp("_START", &curname[2]) == 0)
found = curlump;
else if(strlen(curname) == 5 && strcmp("_END", &curname[1]) == 0)
found = curlump;
else if(strlen(curname) == 6 && strcmp("_END", &curname[2]) == 0)
found = curlump;
/* Free memory allocated to curname */
free(curname);
}
return found;
}