mirror of
https://github.com/ZDoom/wadext.git
synced 2024-11-10 06:31:41 +00:00
877 lines
20 KiB
C++
877 lines
20 KiB
C++
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright(C) 2016 Christoph Oelckers
|
|
// All rights reserved.
|
|
//
|
|
// 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 3 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, see http://www.gnu.org/licenses/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include "wadext.h"
|
|
#include "resourcefile.h"
|
|
#include "fileformat.h"
|
|
|
|
#pragma warning(disable:4996)
|
|
|
|
bool isLevel(WadItemList * we);
|
|
char profile[256];
|
|
char maindir[128];
|
|
|
|
bool pstartfound=false;
|
|
WadItemList PNames(-1);
|
|
|
|
|
|
char * getdir(const char * lump)
|
|
{
|
|
static char d[2]="";
|
|
|
|
if (*lump>='A' && *lump<='Z') d[0]=*lump;
|
|
else if (*lump>='a' && *lump<='z') d[0]=*lump-32;
|
|
else if (*lump>='0' && *lump<='9') d[0]='0';
|
|
else d[0]='@';
|
|
return d;
|
|
}
|
|
|
|
bool isPatch(const char * n)
|
|
{
|
|
if (!pstartfound || PNames.mLump < 0) return false;
|
|
uint32_t * l = (uint32_t*)PNames.Address();
|
|
char * c = (char*)(l + 1);
|
|
char nn[9];
|
|
|
|
|
|
if (!l) return false;
|
|
|
|
strncpy(nn, n, 8);
|
|
nn[8] = 0;
|
|
strupr(nn);
|
|
|
|
for (unsigned i = 0; i<*l; i++)
|
|
{
|
|
if (!strncmp(c, nn, 8)) return true;
|
|
c += 8;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
const char * MakeFileName(const char * base, const char * ext)
|
|
{
|
|
char basebuffer[20];
|
|
static char buffer[40];
|
|
|
|
strncpy(basebuffer,base,8);
|
|
basebuffer[8]=0;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
// replace special path characters in the lump name
|
|
if (basebuffer[i] == '\\') basebuffer[i] = '^';
|
|
if (basebuffer[i] == '/') basebuffer[i] = '\xA7';
|
|
if (basebuffer[i] == ':') basebuffer[i] = '\xA1';
|
|
if (basebuffer[i] == '*') basebuffer[i] = '\xB2';
|
|
if (basebuffer[i] == '?') basebuffer[i] = '\xBF';
|
|
}
|
|
|
|
for(int i=0;;i++)
|
|
{
|
|
if (i > 0) sprintf(buffer,"%s%s(%d)", basebuffer, ext, i);
|
|
else sprintf(buffer, "%s%s", basebuffer, ext);
|
|
strlwr(buffer);
|
|
FILE *f = fopen(buffer, "rb");
|
|
if (f == NULL) return buffer;
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
void Extract(WadItemList *w,const char * dir,const char * ext, int options, bool isflat) //It is completely impossible to detect flats, so we have to flag it when extracting the F_ namespace
|
|
{
|
|
FileType ft = IdentifyFileType(w->Name(), (uint8_t*)w->Address(), w->Length());
|
|
const char *filename;
|
|
|
|
if (dir == NULL)
|
|
{
|
|
switch (ft.type & FG_MASK)
|
|
{
|
|
case FG_GFX:
|
|
if (isPatch(w->Name())) dir = "patches";
|
|
else dir = "graphics";
|
|
break;
|
|
|
|
case FG_SND:
|
|
dir = "sounds";
|
|
break;
|
|
|
|
case FG_MUS:
|
|
dir = "music";
|
|
break;
|
|
|
|
case FG_VOX:
|
|
dir = "voxels";
|
|
break;
|
|
|
|
}
|
|
}
|
|
// Flats are easy to misidentify so if we are in the flat namespace and the size is exactly 4096 bytes, let it pass as flat
|
|
if (isflat && (ft.type & FG_MASK) != FG_GFX && (ft.type == FT_DOOMSND || ft.type == FT_MP3 || w->Length() == 4096))
|
|
{
|
|
ft.type = FT_BINARY;
|
|
ft.extension = ".LMP";
|
|
}
|
|
|
|
if (dir != NULL)
|
|
{
|
|
mkdir(dir);
|
|
chdir(dir);
|
|
}
|
|
printf("processing %.8s\n", w->Name());
|
|
|
|
if (ft.type == FT_DOOMGFX && !(options & NO_CONVERT_GFX))
|
|
{
|
|
filename = MakeFileName(w->Name(), ".png");
|
|
PatchToPng(options, (const uint8_t*)w->Address(), w->Length(), filename);
|
|
}
|
|
else if (w->Length() > 0 && ft.type == FT_BINARY && !(options & NO_CONVERT_GFX) && (isflat || (w->Length() == 64000 && options&(DO_HEXEN_PAL | DO_HERETIC_PAL))))
|
|
{
|
|
filename = MakeFileName(w->Name(), ".png");
|
|
FlatToPng(options, (const uint8_t*)w->Address(), w->Length(), filename);
|
|
}
|
|
else if (ft.type == FT_DOOMSND && !(options & NO_CONVERT_SND))
|
|
{
|
|
filename = MakeFileName(w->Name(), ".wav");
|
|
DoomSndToWav((const uint8_t*)w->Address(), w->Length(), filename);
|
|
}
|
|
else
|
|
{
|
|
filename = MakeFileName(w->Name(), ft.extension);
|
|
|
|
FILE *f = fopen(filename, "wb");
|
|
if (f == NULL)
|
|
{
|
|
printf("%s: Unable to create file\n", filename);
|
|
chdir(maindir);
|
|
return;
|
|
}
|
|
fwrite(w->Address(), 1, w->Length(), f);
|
|
fclose(f);
|
|
}
|
|
chdir(maindir);
|
|
}
|
|
|
|
int ListExtract(int startmarker,const char * dir,const char * endm,const char * endm2,bool isflat,int options,int pack=0)
|
|
{
|
|
char dirbuf[200];
|
|
|
|
mkdir(dir);
|
|
strcpy(dirbuf,dir);
|
|
WadItemList ww(startmarker + 1);
|
|
WadItemList *w = &ww;
|
|
while (strncmp(w->Name(),endm,8) && strncmp(w->Name(),endm2,8))
|
|
{
|
|
Extract(w, dir, NULL, options, isflat);
|
|
ww = ww.mLump + 1;
|
|
}
|
|
return ww.mLump;
|
|
}
|
|
|
|
bool isLevel(int lump)
|
|
{
|
|
if (lump >= mainwad->NumLumps() - 3) return false; // any level is at least 3 lumps
|
|
WadItemList w(lump + 1);
|
|
if (!stricmp(w.Name(), "THINGS")) return true;
|
|
if (!stricmp(w.Name(), "TEXTMAP")) return true;
|
|
return false;
|
|
}
|
|
|
|
int MapExtract(int startmarker,const char *dir,int options)
|
|
{
|
|
int i;
|
|
char buffer[40];
|
|
TArray<CWADLump> directory;
|
|
|
|
mkdir(dir);
|
|
chdir(dir);
|
|
|
|
WadItemList ww(startmarker);
|
|
sprintf(buffer,"%.8s.wad", ww.Name());
|
|
printf("processing %s\n",buffer);
|
|
FILE *f=fopen(buffer, "wb");
|
|
|
|
fwrite("PWAD\xb\0\0\0\0\0\0\0",1, 12, f);
|
|
|
|
|
|
CWADLump lmp;
|
|
lmp.offset = ftell(f);
|
|
strncpy(lmp.name, ww.Name(), 8);
|
|
lmp.length = ww.Length();
|
|
fwrite(ww.Address(), 1, ww.Length(), f);
|
|
directory.Push(lmp);
|
|
ww = ww.mLump + 1;
|
|
|
|
if (!stricmp(ww.Name(), "TEXTMAP"))
|
|
{
|
|
// UDMF
|
|
while (true)
|
|
{
|
|
if (!(options & DO_STRIP) || (stricmp(ww.Name(), "ZNODES") && stricmp(ww.Name(), "BLOCKMAP") && stricmp(ww.Name(), "REJECT")))
|
|
{
|
|
CWADLump lmp;
|
|
lmp.offset = ftell(f);
|
|
strncpy(lmp.name, ww.Name(), 8);
|
|
lmp.length = ww.Length();
|
|
fwrite(ww.Address(), 1, ww.Length(), f);
|
|
directory.Push(lmp);
|
|
}
|
|
|
|
if (!stricmp(ww.Name(), "ENDMAP"))
|
|
{
|
|
goto finalize;
|
|
}
|
|
int lump = ww.mLump + 1;
|
|
if (lump == mainwad->NumLumps())
|
|
{
|
|
printf("Unexpected end of UDMF map found\n");
|
|
return lump;
|
|
}
|
|
ww = lump;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
static const char *maplumps[] = { "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP", "BEHAVIOR", "SCRIPTS" };
|
|
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
if (ww.Name() != NULL && !stricmp(ww.Name(), maplumps[i]))
|
|
{
|
|
CWADLump lmp;
|
|
lmp.offset = ftell(f);
|
|
strncpy(lmp.name, ww.Name(), 8);
|
|
if ((options & DO_STRIP) && (i == 4 || i == 5 || i == 6 || i == 8 || i == 9))
|
|
{
|
|
lmp.length = 0;
|
|
}
|
|
else
|
|
{
|
|
lmp.length = ww.Length();
|
|
fwrite(ww.Address(), 1, ww.Length(), f);
|
|
}
|
|
directory.Push(lmp);
|
|
ww = ww.mLump + 1;
|
|
}
|
|
}
|
|
}
|
|
finalize:
|
|
uint32_t lumpc = directory.Size();
|
|
uint32_t dpos = ftell(f);
|
|
for (auto &p : directory)
|
|
{
|
|
fwrite(&p, 1, 16, f);
|
|
}
|
|
fseek(f, 4, 0);
|
|
fwrite(&lumpc, 1, 4, f);
|
|
fwrite(&dpos, 1, 4, f);
|
|
fclose(f);
|
|
chdir("..");
|
|
return directory.Size();
|
|
}
|
|
|
|
|
|
typedef char pname[8];
|
|
|
|
bool AddWallTexture(FILE * fo,maptexture_t * wt,pname * pNames, bool first)
|
|
{
|
|
int k;
|
|
char buffer[9];
|
|
|
|
sprintf(buffer,"%.8s",wt->name);
|
|
strupr(buffer);
|
|
bool cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9';
|
|
fprintf(fo,"WallTexture %s%s%s, %d, %d\n{\n",cc? "\"":"", buffer,cc? "\"":"", wt->width,wt->height);
|
|
if (wt->ScaleX) fprintf(fo,"\tXScale %f\n",wt->ScaleX/8.0f);
|
|
if (wt->ScaleY) fprintf(fo,"\tYScale %f\n",wt->ScaleY/8.0f);
|
|
if (wt->Flags&0x8000) fprintf(fo,"\tWorldPanning\n");
|
|
if (first) fprintf(fo,"\tNullTexture\n");
|
|
for(k=0;k<wt->patchcount;k++)
|
|
{
|
|
sprintf(buffer,"%.8s",pNames[wt->patches[k].patch]);
|
|
strupr(buffer);
|
|
cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9';
|
|
fprintf(fo,"\tPatch %s%.8s%s, %d, %d\n",cc? "\"":"", buffer,cc? "\"":"", wt->patches[k].originx,wt->patches[k].originy);
|
|
}
|
|
fprintf(fo,"}\n");
|
|
return true;
|
|
}
|
|
|
|
bool AddWallTexture(FILE * fo,strifemaptexture_t * wt,pname * pNames, bool first)
|
|
{
|
|
int k;
|
|
char buffer[9];
|
|
|
|
sprintf(buffer,"%.8s",wt->name);
|
|
strupr(buffer);
|
|
bool cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9';
|
|
fprintf(fo,"WallTexture %s%s%s, %d, %d\n{\n",cc? "\"":"", buffer,cc? "\"":"", wt->width,wt->height);
|
|
if (wt->ScaleX) fprintf(fo,"\tXScale %f\n",wt->ScaleX/8.0f);
|
|
if (wt->ScaleY) fprintf(fo,"\tYScale %f\n",wt->ScaleY/8.0f);
|
|
if (wt->Flags&0x8000) fprintf(fo,"\tWorldPanning\n");
|
|
for(k=0;k<wt->patchcount;k++)
|
|
{
|
|
sprintf(buffer,"%.8s",pNames[wt->patches[k].patch]);
|
|
strupr(buffer);
|
|
cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9';
|
|
fprintf(fo,"\tPatch %s%.8s%s, %d, %d\n",cc? "\"":"", buffer,cc? "\"":"", wt->patches[k].originx,wt->patches[k].originy);
|
|
}
|
|
fprintf(fo,"}\n");
|
|
return true;
|
|
}
|
|
|
|
void AnimExtract(WadItemList *w)
|
|
{
|
|
int i,numentries;
|
|
char * c;
|
|
char buffer[30];
|
|
|
|
mkdir("decompiled");
|
|
chdir("decompiled");
|
|
|
|
strcpy(buffer,"animdefs.animated");
|
|
printf("processing %s\n",buffer);
|
|
|
|
numentries=w->Length()/23;
|
|
|
|
c=(char*)w->Address();
|
|
|
|
FILE * fo=fopen(buffer,"wt");
|
|
for(i=0;i<numentries;i++)
|
|
{
|
|
fprintf(fo,"%s %s Range %s Tics %d\n", *c? "Texture":"Flat",c+10, c+1,*(int*)(c+19));
|
|
c+=23;
|
|
}
|
|
|
|
fclose(fo);
|
|
chdir(maindir);
|
|
}
|
|
|
|
void SwitchExtract(WadItemList *w)
|
|
{
|
|
int i,numentries;
|
|
char * c;
|
|
char buffer[30];
|
|
|
|
mkdir("decompiled");
|
|
chdir("decompiled");
|
|
|
|
|
|
strcpy(buffer, "animdefs.switches");
|
|
printf("processing %s\n",buffer);
|
|
|
|
numentries=w->Length()/20;
|
|
|
|
c=(char*)w->Address();
|
|
|
|
FILE * fo=fopen(buffer,"wt");
|
|
for(i=0;i<numentries;i++)
|
|
{
|
|
if (*(short*)(c+18)==0) break;
|
|
//fprintf(fo,"%s,%s,%d\n",c,c+9,*(short*)(c+18));
|
|
fprintf(fo,"Switch %s on pic %s tics 0\n", c, c+9);
|
|
c+=20;
|
|
}
|
|
|
|
fclose(fo);
|
|
chdir(maindir);
|
|
//Extract(w, dir);
|
|
}
|
|
|
|
|
|
void GenerateTextureFile(const char *name, const char * pTex, int length, const char * pPNam, int options, bool nullfirst)
|
|
{
|
|
const char * tr=pTex;
|
|
int tc =*(int*)tr;
|
|
int * to =(int*)(tr+4);
|
|
pname * pr=(pname*)((char*)pPNam+4);
|
|
|
|
|
|
FILE * fo=fopen(name,"wt");
|
|
|
|
auto maptex = (const uint32_t*)pTex;
|
|
int numtextures = *maptex;
|
|
int maxoff = length;
|
|
int i;
|
|
const uint32_t* directory;
|
|
|
|
if (maxoff < (numtextures + 1) * 4)
|
|
{
|
|
printf("%s: Texture directory is too short\n", name);
|
|
return;
|
|
}
|
|
bool isStrife = false;
|
|
|
|
// Scan the texture lump to decide if it contains Doom or Strife textures
|
|
for (i = 0, directory = maptex + 1; i < numtextures; ++i)
|
|
{
|
|
int offset = directory[i];
|
|
if (offset > maxoff)
|
|
{
|
|
printf("%s: Bad texture directory\n", name);
|
|
return;
|
|
}
|
|
|
|
maptexture_t* tex = (maptexture_t*)((uint8_t*)maptex + offset);
|
|
|
|
// There is bizzarely a Doom editing tool that writes to the
|
|
// first two elements of columndirectory, so I can't check those.
|
|
if (tex->patchcount < 0 ||
|
|
tex->columndirectory[2] != 0 ||
|
|
tex->columndirectory[3] != 0)
|
|
{
|
|
isStrife = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isStrife)
|
|
{
|
|
for(int i=0;i<tc;i++)
|
|
AddWallTexture(fo, (maptexture_t*)(tr+to[i]),pr, i == 0 && nullfirst);
|
|
|
|
}
|
|
else
|
|
{
|
|
for(int i=0;i<tc;i++)
|
|
AddWallTexture(fo, (strifemaptexture_t*)((char*)tr+to[i]),pr, i == 0 && nullfirst);
|
|
}
|
|
|
|
fclose(fo);
|
|
}
|
|
|
|
void GenerateTextureFile(WadItemList * pTex,WadItemList * pPNam, int options)
|
|
{
|
|
char buffer[40];
|
|
mkdir("decompiled");
|
|
sprintf(buffer,"decompiled/textures.%c", pTex->Name()[7]);
|
|
bool nulltex = !strnicmp(pTex->Name(), "TEXTURE1", 8);
|
|
GenerateTextureFile(buffer, (const char*)pTex->Address(), pTex->Length(), (const char *)pPNam->Address(), options, nulltex);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// IsSeparator (taken from ZDoom)
|
|
//
|
|
// Returns true if the character is a path separator.
|
|
//
|
|
//==========================================================================
|
|
|
|
static inline bool IsSeparator(int c)
|
|
{
|
|
if (c == '/')
|
|
return true;
|
|
#ifdef WIN32
|
|
if (c == '\\' || c == ':')
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// ExtractFileBase (taken from ZDoom)
|
|
//
|
|
// Returns the file part of a pathname, optionally including the extension.
|
|
//
|
|
//==========================================================================
|
|
|
|
std::string ExtractFileBase(const char *path, bool include_extension)
|
|
{
|
|
const char *src, *dot;
|
|
|
|
src = path + strlen(path) - 1;
|
|
|
|
if (src >= path)
|
|
{
|
|
// back up until a / or the start
|
|
while (src != path && !IsSeparator(*(src - 1)))
|
|
src--;
|
|
|
|
// Check for files with drive specification but no path
|
|
#if defined(_WIN32)
|
|
if (src == path && src[0] != 0)
|
|
{
|
|
if (src[1] == ':')
|
|
src += 2;
|
|
}
|
|
#endif
|
|
|
|
if (!include_extension)
|
|
{
|
|
dot = src;
|
|
while (*dot && *dot != '.')
|
|
{
|
|
dot++;
|
|
}
|
|
return std::string(src, dot - src);
|
|
}
|
|
else
|
|
{
|
|
return std::string(src);
|
|
}
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
|
|
void ExtractWad(char * wadfilename,int options)
|
|
{
|
|
WadItemList pTex1(-1), pTex2(-1), pPnam(-1);
|
|
|
|
OpenMainWad(wadfilename);
|
|
auto name = ExtractFileBase(wadfilename, false);
|
|
mkdir(name.c_str());
|
|
chdir(name.c_str());
|
|
|
|
PNames = mainwad->FindLump("PNAMES");
|
|
getcwd(maindir, 128);
|
|
for(int i=0;i<mainwad->NumLumps();i++)
|
|
{
|
|
WadItemList ww(i);
|
|
WadItemList *w = &ww;
|
|
{
|
|
if (!strcmp(w->Name(),"."))
|
|
{
|
|
w->Release();
|
|
}
|
|
else if (!strnicmp(w->Name(),"S_START",8) || !strnicmp(w->Name(),"SS_START",8) )
|
|
{
|
|
i=ListExtract(i,"SPRITES","S_END","SS_END",false,options,false);
|
|
}
|
|
else if (!strnicmp(w->Name(),"F_START",8) || !strnicmp(w->Name(),"FF_START",8) )
|
|
{
|
|
i=ListExtract(i,"FLATS","F_END","FF_END",true,options,false);
|
|
}
|
|
else if (!strnicmp(w->Name(),"C_START",8))
|
|
{
|
|
i=ListExtract(i,"COLORMAPS","C_END","CC_END",false,false,options);
|
|
}
|
|
else if (!strnicmp(w->Name(),"A_START",8))
|
|
{
|
|
i=ListExtract(i,"ACS","A_END","AA_END",false,false,options);
|
|
}
|
|
else if (!strnicmp(w->Name(),"P_START",8) || !strnicmp(w->Name(),"PP_START",8))
|
|
{
|
|
pstartfound=true;
|
|
}
|
|
else if (!strnicmp(w->Name(),"P_END",8) || !strnicmp(w->Name(),"PP_END",8)) // ignore
|
|
{
|
|
}
|
|
else if (!strnicmp(w->Name(),"TX_START",8))
|
|
{
|
|
i=ListExtract(i,"TEXTURES","TX_END","TX_END",false,options);
|
|
}
|
|
else if (!strnicmp(w->Name(),"HI_START",8))
|
|
{
|
|
i=ListExtract(i,"HIRES","HI_END","HI_END",false,options);
|
|
}
|
|
else if (!strnicmp(w->Name(),"VX_START",8))
|
|
{
|
|
i=ListExtract(i,"VOXELS","VX_END","VX_END",false,false,options);
|
|
}
|
|
else if (!strnicmp(w->Name(),"GL_VERT",8)) // ignore
|
|
{
|
|
// skip GL nodes
|
|
}
|
|
else if (!strnicmp(w->Name(),"GL_SEGS",8)) // ignore
|
|
{
|
|
// skip GL nodes
|
|
}
|
|
else if (!strnicmp(w->Name(),"GL_SSECT",8)) // ignore
|
|
{
|
|
// skip GL nodes
|
|
}
|
|
else if (!strnicmp(w->Name(),"GL_NODES",8)) // ignore
|
|
{
|
|
// skip GL nodes
|
|
}
|
|
else if (!strnicmp(w->Name(), "GL_PVS", 8)) // ignore
|
|
{
|
|
// skip GL nodes
|
|
}
|
|
else if (isLevel(i))
|
|
{
|
|
i += MapExtract(i,"MAPS",options) - 1;
|
|
}
|
|
else
|
|
{
|
|
if (!strnicmp(w->Name(), "TEXTURE1", 8)) pTex1 = *w;
|
|
else if (!strnicmp(w->Name(), "TEXTURE2", 8)) pTex2 = *w;
|
|
else if (!strnicmp(w->Name(), "PNAMES", 8)) pPnam = *w;
|
|
else if (!strnicmp(w->Name(), "ANIMATED", 8))
|
|
{
|
|
AnimExtract(w);
|
|
}
|
|
else if (!strnicmp(w->Name(), "SWITCHES", 8))
|
|
{
|
|
SwitchExtract(w);
|
|
}
|
|
|
|
Extract(w, NULL, NULL, options, false);
|
|
}
|
|
|
|
if (pTex1.mLump >= 0 && pPnam.mLump >= 0)
|
|
{
|
|
GenerateTextureFile(&pTex1,&pPnam, options);
|
|
pTex1 = -1;
|
|
}
|
|
if (pTex2.mLump >= 0 && pPnam.mLump >= 0)
|
|
{
|
|
GenerateTextureFile(&pTex2,&pPnam, options);
|
|
pTex2=-1;
|
|
}
|
|
}
|
|
}
|
|
chdir("..");
|
|
}
|
|
|
|
void ConvertTextureX()
|
|
{
|
|
FILE* ft1 = fopen("texture1", "rb");
|
|
if (!ft1) ft1 = fopen("texture1.lmp", "rb");
|
|
|
|
FILE* ft2 = fopen("texture2", "rb");
|
|
if (!ft2) ft2 = fopen("texture2.lmp", "rb");
|
|
|
|
FILE* pnm = fopen("pnames", "rb");
|
|
if (!pnm) pnm = fopen("pnames.lmp", "rb");
|
|
|
|
if (!pnm) exit(1);
|
|
static char bt1[1000000], bpn[1000000];
|
|
fread(bpn, 1, 1000000, pnm), fclose(pnm);
|
|
|
|
if (ft1)
|
|
{
|
|
int l = (int)fread(bt1, 1, 1000000, ft1);
|
|
fclose(ft1);
|
|
GenerateTextureFile("textures.txt", bt1, l, bpn, 0, true);
|
|
}
|
|
if (ft2)
|
|
{
|
|
int l = (int)fread(bt1, 1, 1000000, ft2);
|
|
fclose(ft2);
|
|
GenerateTextureFile("textures.txt2", bt1, l, bpn, 0, false);
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CreatePath
|
|
//
|
|
// Creates a directory including all levels necessary
|
|
//
|
|
//==========================================================================
|
|
#ifdef _WIN32
|
|
void CreatePath(const char* fn)
|
|
{
|
|
char drive[_MAX_DRIVE];
|
|
char dir[_MAX_DIR];
|
|
_splitpath_s(fn, drive, sizeof drive, dir, sizeof dir, nullptr, 0, nullptr, 0);
|
|
|
|
if ('\0' == *dir)
|
|
{
|
|
// Root/current/parent directory always exists
|
|
return;
|
|
}
|
|
|
|
char path[_MAX_PATH];
|
|
_makepath_s(path, sizeof path, drive, dir, nullptr, nullptr);
|
|
|
|
if ('\0' == *path)
|
|
{
|
|
// No need to process empty relative path
|
|
return;
|
|
}
|
|
|
|
// Remove trailing path separator(s)
|
|
for (size_t i = strlen(path); 0 != i; --i)
|
|
{
|
|
char& lastchar = path[i - 1];
|
|
|
|
if ('/' == lastchar || '\\' == lastchar)
|
|
{
|
|
lastchar = '\0';
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create all directories for given path
|
|
if ('\0' != *path)
|
|
{
|
|
CreatePath(path);
|
|
_mkdir(path); // PAKs are ASCII only, so we won't need Unicode extension.
|
|
}
|
|
}
|
|
|
|
#else
|
|
void CreatePath(const char* fn)
|
|
{
|
|
char* copy, * p;
|
|
|
|
if (fn[0] == '/' && fn[1] == '\0')
|
|
{
|
|
return;
|
|
}
|
|
p = copy = strdup(fn);
|
|
do
|
|
{
|
|
p = strchr(p + 1, '/');
|
|
if (p != NULL)
|
|
{
|
|
*p = '\0';
|
|
}
|
|
mkdir(copy, 0755);
|
|
if (p != NULL)
|
|
{
|
|
*p = '/';
|
|
}
|
|
} while (p);
|
|
free(copy);
|
|
}
|
|
#endif
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
struct GrpInfo
|
|
{
|
|
uint32_t Magic[3];
|
|
uint32_t NumLumps;
|
|
};
|
|
|
|
struct GrpLump
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
char Name[12];
|
|
uint32_t Size;
|
|
};
|
|
char NameWithZero[13];
|
|
};
|
|
};
|
|
|
|
|
|
void GrpExtract(const char* filename, FILE* f)
|
|
{
|
|
TArray<GrpLump> fileinfo;
|
|
|
|
GrpInfo header;
|
|
|
|
if (1 != fread(&header, sizeof(header), 1, f)) return;
|
|
|
|
fileinfo.Resize(header.NumLumps);
|
|
if (header.NumLumps != fread(&fileinfo[0], sizeof(GrpLump), header.NumLumps, f)) return;
|
|
if (memcmp(header.Magic, "KenSilverman", 12))
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto name = ExtractFileBase(filename, false);
|
|
mkdir(name.c_str());
|
|
chdir(name.c_str());
|
|
|
|
TArray<char> buffer;
|
|
for (uint32_t i = 0; i < header.NumLumps; i++)
|
|
{
|
|
buffer.Resize(fileinfo[i].Size);
|
|
fileinfo[i].NameWithZero[12] = '\0'; // Be sure filename is null-terminated
|
|
if (buffer.Size() != fread(&buffer[0], 1, buffer.Size(), f)) return;
|
|
FILE* fout = fopen(fileinfo[i].NameWithZero, "wb");
|
|
if (fout)
|
|
{
|
|
fwrite(&buffer[0], 1, fileinfo[i].Size, fout);
|
|
fclose(fout);
|
|
}
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
struct dpackfile_t
|
|
{
|
|
char name[56];
|
|
uint32_t filepos, filelen;
|
|
};
|
|
|
|
struct dpackheader_t
|
|
{
|
|
uint32_t ident; // == IDPAKHEADER
|
|
uint32_t dirofs;
|
|
uint32_t dirlen;
|
|
};
|
|
|
|
void PakExtract(const char* filename, FILE* f)
|
|
{
|
|
TArray<dpackfile_t> fileinfo;
|
|
|
|
dpackheader_t header;
|
|
|
|
if (1 != fread(&header, sizeof(header), 1, f)) return;
|
|
|
|
if (memcmp(&header.ident, "PACK", 4))
|
|
{
|
|
return;
|
|
}
|
|
fseek(f, header.dirofs, SEEK_SET);
|
|
|
|
uint32_t NumLumps = header.dirlen / sizeof(dpackfile_t);
|
|
fileinfo.Resize(NumLumps);
|
|
|
|
if (NumLumps != fread(&fileinfo[0], sizeof(dpackfile_t), NumLumps, f)) return;
|
|
auto name = ExtractFileBase(filename, false);
|
|
mkdir(name.c_str());
|
|
chdir(name.c_str());
|
|
|
|
TArray<char> buffer;
|
|
for (uint32_t i = 0; i < NumLumps; i++)
|
|
{
|
|
buffer.Resize(fileinfo[i].filelen);
|
|
fseek(f, fileinfo[i].filepos, SEEK_SET);
|
|
if (buffer.Size() != fread(&buffer[0], 1, buffer.Size(), f)) return;
|
|
CreatePath(fileinfo[i].name);
|
|
FILE* fout = fopen(fileinfo[i].name, "wb");
|
|
if (fout)
|
|
{
|
|
fwrite(&buffer[0], 1, fileinfo[i].filelen, fout);
|
|
fclose(fout);
|
|
}
|
|
}
|
|
exit(1);
|
|
}
|
|
|