mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2024-12-27 04:41:23 +00:00
412 lines
8.5 KiB
C
412 lines
8.5 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// libwad: Doom WAD format interface library.
|
|
// Copyright (C) 2011 by Callum Dickinson.
|
|
//
|
|
// 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.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file wad.c
|
|
/// \brief WAD interface functionality.
|
|
|
|
#include "wad_static.h"
|
|
|
|
//
|
|
// Offset for the first lump's data is always 12 bytes.
|
|
//
|
|
#define LUMPOFFSET 12
|
|
|
|
//
|
|
// Maximum of wad files used at the same time.
|
|
// (There is a max of simultaneous open files anyway, and this should be plenty.)
|
|
//
|
|
#define MAX_WADFILES 48
|
|
|
|
wad_t *wads[MAX_WADFILES];
|
|
wad_uint32_t wad_numwads;
|
|
|
|
wad_uint32_t wad_oldnumwads;
|
|
wad_uint32_t wad_removednum;
|
|
|
|
static inline void WAD_ChangeWADNum(wad_int32_t i, wad_t *wad)
|
|
{
|
|
wads[i] = wad;
|
|
}
|
|
|
|
static inline void WAD_CacheWAD(wad_t *wad)
|
|
{
|
|
WAD_ChangeWADNum(wad_numwads, wad);
|
|
wad_oldnumwads = wad_numwads;
|
|
wad_numwads++;
|
|
}
|
|
|
|
static void WAD_UncacheWAD(wad_t *wad)
|
|
{
|
|
wad_uint32_t i;
|
|
|
|
for (i = 0; i < wad_numwads; i++)
|
|
if (WAD_WADByNum(i) == wad)
|
|
break;
|
|
|
|
WAD_ChangeWADNum(i, NULL);
|
|
wad_removednum = i;
|
|
|
|
for (; i < wad_numwads; i++)
|
|
{
|
|
if (WAD_WADByNum(i+1))
|
|
{
|
|
WAD_ChangeWADNum(i, WAD_WADByNum(i+1));
|
|
WAD_ChangeWADNum(i+1, NULL);
|
|
}
|
|
}
|
|
|
|
wad_oldnumwads = wad_numwads;
|
|
wad_numwads--;
|
|
}
|
|
|
|
wad_uint32_t WAD_WADLoopAdvance(wad_uint32_t i)
|
|
{
|
|
wad_uint32_t o;
|
|
|
|
o = wad_oldnumwads;
|
|
wad_oldnumwads = wad_numwads;
|
|
|
|
return (o > wad_numwads && i <= wad_removednum) ? i : i + 1;
|
|
}
|
|
|
|
wad_t *WAD_WADByNum(wad_uint16_t num)
|
|
{
|
|
return (wads[num]) ? (wad_t *)wads[num] : NULL;
|
|
}
|
|
|
|
wad_t *WAD_WADByFileName(const char *filename)
|
|
{
|
|
wad_uint32_t i;
|
|
wad_t *w;
|
|
|
|
for (i = 0; i < wad_numwads; i = WAD_WADLoopAdvance(i))
|
|
{
|
|
w = WAD_WADByNum(i);
|
|
if (!strcmp(filename, w->filename))
|
|
return w;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Allocate a wadfile, setup the lumpinfo (directory) and
|
|
// lumpcache, add the wadfile to the current active wadfiles
|
|
//
|
|
// 1: Reached the maximum WAD number.
|
|
// 2: Cannot open the file.
|
|
// 3: Cannot read the WAD file's header.
|
|
// 4: Invalid WAD ID.
|
|
// 5: WAD's lump directory is corrupt.
|
|
// 6: Corrupt compressed WAD.
|
|
//
|
|
wad_t *WAD_OpenWAD(const char *filename)
|
|
{
|
|
size_t i;
|
|
// Pointer to the WAD.
|
|
wad_t *wad = NULL;
|
|
// WAD file structure information.
|
|
// wad->filename is the function's argument.
|
|
FILE *file; // wad->file
|
|
wadheader_t *header; // wad->header
|
|
lump_t *l, *lumps = NULL; // wad->lumps
|
|
// Temporary lump information.
|
|
wad_uint64_t position;
|
|
lumpdir_t *lumpdir;
|
|
lump_t *ln = NULL;
|
|
|
|
//
|
|
// Check if the limit of maximum WAD files has been
|
|
// reached or not.
|
|
//
|
|
if (wad_numwads >= MAX_WADFILES)
|
|
return NULL;
|
|
|
|
//
|
|
// Open the WAD file.
|
|
//
|
|
if (!(file = fopen(filename, "rb")))
|
|
return NULL;
|
|
|
|
//
|
|
// Read the WAD file's header.
|
|
//
|
|
header = malloc(sizeof(*header));
|
|
if (fread(header, 1, sizeof(*header), file) < sizeof(*header))
|
|
return NULL;
|
|
|
|
// Check if the ID is valid.
|
|
if (memcmp(header->id, "IWAD", 4) && memcmp(header->id, "PWAD", 4) && memcmp(header->id, "SDLL", 4))
|
|
{
|
|
WAD_FreeMemory(file, header, NULL, NULL, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
// Check the rest of the header for type.
|
|
header->numlumps = WAD_INT32_Endianness(header->numlumps);
|
|
header->lumpdir = WAD_INT32_Endianness(header->lumpdir);
|
|
|
|
//
|
|
// Read the WAD file's lump directory.
|
|
//
|
|
i = header->numlumps * sizeof(*lumpdir);
|
|
lumpdir = malloc(i);
|
|
|
|
if (fseek(file, header->lumpdir, SEEK_SET) < 0 || fread(lumpdir, i, 1, file) < 1)
|
|
{
|
|
WAD_FreeMemory(file, header, lumpdir, NULL, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Fill lump information structure.
|
|
//
|
|
for (i = 0; i < header->numlumps; i++, lumpdir++)
|
|
{
|
|
if (!(l = malloc(sizeof(*l))))
|
|
{
|
|
WAD_FreeMemory(file, header, lumpdir, lumps, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
position = WAD_INT32_Endianness(lumpdir->position);
|
|
|
|
strncpy(l->name, lumpdir->name, 8);
|
|
l->size = l->disksize = WAD_INT32_Endianness(lumpdir->size);
|
|
l->compressed = false; // TODO: Compressed WAD support.
|
|
|
|
if (l->disksize)
|
|
{
|
|
fseek(file, position, SEEK_SET);
|
|
if (!(l->data = malloc(l->disksize)) || fread(l->data, l->disksize, 1, file) < 1)
|
|
{
|
|
WAD_FreeMemory(file, header, lumpdir, lumps, NULL);
|
|
|
|
if (!l->data)
|
|
return NULL;
|
|
else
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
l->num = i;
|
|
l->next = NULL;
|
|
if (!lumps)
|
|
{
|
|
l->prev = NULL;
|
|
ln = lumps = l;
|
|
}
|
|
else
|
|
{
|
|
l->prev = ln;
|
|
ln = ln->next = l;
|
|
}
|
|
}
|
|
//free(lumpdir);
|
|
|
|
//
|
|
// Allocate and fill WAD entry.
|
|
//
|
|
if (!(wad = malloc(sizeof(*wad))))
|
|
{
|
|
WAD_FreeMemory(file, header, NULL, lumps, NULL);
|
|
return NULL;
|
|
}
|
|
wad->filename = (char *)filename;
|
|
|
|
wad->header = header;
|
|
wad->compressed = false;
|
|
|
|
wad->lumps = lumps;
|
|
|
|
for (l = wad->lumps; l; l = l->next)
|
|
l->wad = wad;
|
|
|
|
fclose(file);
|
|
|
|
//
|
|
// Add the WAD to the wadfiles buffer and wad_numwads number.
|
|
//
|
|
WAD_CacheWAD(wad);
|
|
|
|
//
|
|
// Done, loaded the WAD file.
|
|
//
|
|
return wad;
|
|
}
|
|
|
|
// 1: Invalid wad number.
|
|
// 2: Unable to open file for writing.
|
|
// 3: Unable to write wad ID to the header.
|
|
// 4: Unable to write number of lumps to the header.
|
|
// 5: Unable to write temporary data to the header.
|
|
// 6: Unable to write lump data.
|
|
// 7: Unable to write position of lump data.
|
|
// 8: Unable to Write size of lump data.
|
|
// 9: Unable to write lump name.
|
|
// 9: Unable to write directory position.
|
|
wad_int32_t WAD_SaveWAD(wad_t *wad)
|
|
{
|
|
lump_t *l;
|
|
FILE *file;
|
|
char *newfilename;
|
|
wad_int64_t diroffset, lumpoffset;
|
|
|
|
if (!wad)
|
|
return 1;
|
|
|
|
newfilename = WAD_VA("%s%s", wad->filename, ".bak");
|
|
|
|
remove(newfilename);
|
|
rename(wad->filename, newfilename);
|
|
|
|
if (!(file = fopen(wad->filename, "wb")))
|
|
return 2;
|
|
|
|
//
|
|
// Write four-character WAD ID.
|
|
//
|
|
if(fwrite(wad->header->id, 4, 1, file) < 1)
|
|
return 3;
|
|
|
|
//
|
|
// Write number of lumps.
|
|
//
|
|
if (fwrite(&wad->header->numlumps, 4, 1, file) < 1)
|
|
return 4;
|
|
|
|
//
|
|
// Offset of directory is not known yet. For now, write number of lumps
|
|
// again, just to fill the space.
|
|
//
|
|
if (fwrite(&wad->header->numlumps, 4, 1, file) < 1)
|
|
return 5;
|
|
|
|
//
|
|
// Loop through lump list, writing lump data.
|
|
//
|
|
for (l = wad->lumps; l; l = l->next)
|
|
{
|
|
// Don't write anything for the head of the lump list or for lumps of
|
|
// zero length.
|
|
if (!l->data)
|
|
continue;
|
|
|
|
// Write the data.
|
|
if (fwrite(l->data, l->disksize, 1, file) < 1)
|
|
return 6;
|
|
}
|
|
|
|
//
|
|
// Current position is where directory will start.
|
|
//
|
|
diroffset = ftell(file);
|
|
|
|
//
|
|
// Pointer to the offset for the first lump's data.
|
|
//
|
|
lumpoffset = LUMPOFFSET;
|
|
|
|
//
|
|
// Loop through lump list again, this time writing directory entries.
|
|
//
|
|
for (l = wad->lumps; l; l = l->next)
|
|
{
|
|
// Write position of lump data.
|
|
if (fwrite(&lumpoffset, 4, 1, file) < 1)
|
|
return 7;
|
|
|
|
// Write size of lump data.
|
|
if (fwrite(&(l->disksize), 4, 1, file) < 1)
|
|
return 8;
|
|
|
|
// Write lump name.
|
|
if (fwrite(&(l->name), 8, 1, file) < 1)
|
|
return 9;
|
|
|
|
// Increment lumpoffset variable as appropriate.
|
|
lumpoffset += l->disksize;
|
|
}
|
|
|
|
//
|
|
// Go back to header and write the proper directory position.
|
|
//
|
|
fseek(file, 8, SEEK_SET);
|
|
if (fwrite(&diroffset, 4, 1, file) < 1)
|
|
return 10;
|
|
|
|
//
|
|
// Done, saved the WAD file.
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
inline wad_int32_t WAD_SaveWADByNum(wad_uint16_t num)
|
|
{
|
|
return WAD_SaveWAD(WAD_WADByNum(num));
|
|
}
|
|
|
|
inline wad_int32_t WAD_SaveWADByFileName(const char *filename)
|
|
{
|
|
return WAD_SaveWAD(WAD_WADByFileName(filename));
|
|
}
|
|
|
|
inline wad_int32_t WAD_SaveCloseWAD(wad_t *wad)
|
|
{
|
|
wad_int32_t ret;
|
|
|
|
if (!(ret = WAD_SaveWAD(wad)))
|
|
return ret;
|
|
|
|
WAD_CloseWAD(wad);
|
|
return ret;
|
|
}
|
|
|
|
inline wad_int32_t WAD_SaveCloseWADByNum(wad_uint16_t num)
|
|
{
|
|
return WAD_SaveCloseWAD(WAD_WADByNum(num));
|
|
}
|
|
|
|
inline wad_int32_t WAD_SaveCloseWADByFileName(const char *filename)
|
|
{
|
|
return WAD_SaveCloseWAD(WAD_WADByFileName(filename));
|
|
}
|
|
|
|
void WAD_CloseWAD(wad_t *wad)
|
|
{
|
|
if (!wad)
|
|
return;
|
|
|
|
//
|
|
// Free memory used by the WAD.
|
|
//
|
|
WAD_FreeMemory(NULL, wad->header, NULL, wad->lumps, wad);
|
|
|
|
//
|
|
// Remove references to the WAD.
|
|
//
|
|
WAD_UncacheWAD(wad);
|
|
}
|
|
|
|
inline void WAD_CloseWADByNum(wad_uint16_t num)
|
|
{
|
|
WAD_CloseWAD(WAD_WADByNum(num));
|
|
}
|
|
|
|
inline void WAD_CloseWADByFileName(const char *name)
|
|
{
|
|
WAD_CloseWAD(WAD_WADByFileName(name));
|
|
}
|