/* 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; }