├── gbadoom.wad ├── .gitignore ├── wadprocessor.h ├── README.md ├── wadfile.h ├── .github └── workflows │ └── ccpp.yml ├── GbaWadUtil.pro ├── main.cpp ├── wadfile.cpp ├── doomtypes.h └── wadprocessor.cpp /gbadoom.wad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doomhack/GbaWadUtil/HEAD/gbadoom.wad -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.user 3 | *.o 4 | .qmake.stash 5 | .vscode/settings.json 6 | GbaWadUtil 7 | Makefile 8 | moc_*.h 9 | moc_*.cpp 10 | -------------------------------------------------------------------------------- /wadprocessor.h: -------------------------------------------------------------------------------- 1 | #ifndef WADPROCESSOR_H 2 | #define WADPROCESSOR_H 3 | 4 | #include 5 | #include "wadfile.h" 6 | 7 | class WadProcessor : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit WadProcessor(WadFile& wad, QObject *parent = nullptr); 12 | 13 | bool ProcessWad(); 14 | 15 | private: 16 | 17 | bool ProcessD2Levels(); 18 | bool ProcessD1Levels(); 19 | 20 | bool ProcessLevel(quint32 lumpNum); 21 | 22 | bool ProcessVertexes(quint32 lumpNum); 23 | bool ProcessLines(quint32 lumpNum); 24 | bool ProcessSegs(quint32 lumpNum); 25 | bool ProcessSides(quint32 lumpNum); 26 | 27 | bool ProcessPNames(); 28 | bool RemoveUnusedLumps(); 29 | 30 | int GetTextureNumForName(const char* tex_name); 31 | 32 | WadFile& wadFile; 33 | 34 | }; 35 | 36 | #endif // WADPROCESSOR_H 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Utility to process Doom IWAD files for GBA Doom. 2 | 3 | It can write either a new WAD file and/or a C Source file. 4 | 5 | This is required to reduce the memory footprint of some of the data structures in Doom to get it to fit in 256Kb. 6 | 7 | We will pre-calculate more fields so that the lumps stored in the WAD can be used directly from the ROM rather than having to load and convert them in memory. 8 | 9 | For example: 10 | 11 | In the WAD file a vertex is stored as two short values. (X and Y). When it is loaded into memory, it is shifted left by 16 to convert to 16.16 fixed point. By re-writing this lump and storing it in fixed point, we do not need to copy this into memory at runtime. 12 | 13 | Usage: GbaWadUtil -in iwad_file_path -out gba_iwad_path -cfile iwad_cfile_path. 14 | 15 | 16 | I plan to add PWAD support so that an IWAD and 1 or more PWADS can be converted to a GBA IWAD. 17 | 18 | It is a command line util so it can be integrated into a build process. 19 | -------------------------------------------------------------------------------- /wadfile.h: -------------------------------------------------------------------------------- 1 | #ifndef WADFILE_H 2 | #define WADFILE_H 3 | 4 | #include 5 | #include 6 | 7 | class Lump 8 | { 9 | public: 10 | QString name; 11 | quint32 length; 12 | QByteArray data; 13 | }; 14 | 15 | class WadFile : public QObject 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit WadFile(QString filePath, QObject *parent = nullptr); 20 | 21 | bool LoadWadFile(); 22 | 23 | bool SaveWadFile(QString filePath); 24 | bool SaveWadFile(QIODevice* device); 25 | 26 | qint32 GetLumpByName(QString name, Lump& lump); 27 | bool GetLumpByNum(quint32 lumpnum, Lump& lump); 28 | 29 | bool ReplaceLump(quint32 lumpnum, Lump newLump); 30 | bool InsertLump(quint32 lumpnum, Lump newLump); 31 | bool RemoveLump(quint32 lumpnum); 32 | 33 | quint32 LumpCount(); 34 | 35 | bool MergeWadFile(WadFile& wadFile); 36 | 37 | private: 38 | QString wadPath; 39 | 40 | QList lumps; 41 | }; 42 | 43 | #endif // WADFILE_H 44 | -------------------------------------------------------------------------------- /.github/workflows/ccpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build-native: 7 | runs-on: ${{ matrix.os }}-latest 8 | strategy: 9 | fail-fast: true 10 | matrix: 11 | os: [ubuntu, windows, macos] 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v1 15 | - name: "*nix: Install Qt" 16 | uses: jurplel/install-qt-action@v2 17 | if: matrix.os != 'windows' 18 | - name: "Windows: Install Qt" 19 | uses: jurplel/install-qt-action@v2 20 | if: matrix.os == 'windows' 21 | with: 22 | arch: win64_mingw73 23 | - name: Build 24 | run: | 25 | qmake 26 | make 27 | - name: "*nix: Stash" 28 | if: matrix.os != 'windows' 29 | uses: actions/upload-artifact@v1 30 | with: 31 | name: GbaWadUtil-${{ matrix.os }} 32 | path: GbaWadUtil 33 | - name: "Windows: Stash" 34 | if: matrix.os == 'windows' 35 | uses: actions/upload-artifact@v1 36 | with: 37 | name: GbaWadUtil-${{ matrix.os }} 38 | path: release/GbaWadUtil.exe 39 | 40 | -------------------------------------------------------------------------------- /GbaWadUtil.pro: -------------------------------------------------------------------------------- 1 | QT -= gui 2 | 3 | CONFIG += c++11 console 4 | CONFIG -= app_bundle 5 | 6 | # The following define makes your compiler emit warnings if you use 7 | # any Qt feature that has been marked deprecated (the exact warnings 8 | # depend on your compiler). Please consult the documentation of the 9 | # deprecated API in order to know how to port your code away from it. 10 | DEFINES += QT_DEPRECATED_WARNINGS 11 | 12 | # You can also make your code fail to compile if it uses deprecated APIs. 13 | # In order to do so, uncomment the following line. 14 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 16 | 17 | SOURCES += \ 18 | main.cpp \ 19 | wadfile.cpp \ 20 | wadprocessor.cpp 21 | 22 | # Default rules for deployment. 23 | qnx: target.path = /tmp/$${TARGET}/bin 24 | else: unix:!android: target.path = /opt/$${TARGET}/bin 25 | !isEmpty(target.path): INSTALLS += target 26 | 27 | HEADERS += \ 28 | doomtypes.h \ 29 | wadfile.h \ 30 | wadprocessor.h 31 | 32 | DISTFILES += \ 33 | gbadoom.wad 34 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "wadfile.h" 4 | #include "wadprocessor.h" 5 | 6 | void SaveBytesAsCFile(QByteArray& bytes, QString file); 7 | 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QCoreApplication a(argc, argv); 12 | 13 | QStringList args = QCoreApplication::arguments(); 14 | 15 | QString inFile; 16 | QString outFile; 17 | QString cFile; 18 | QStringList pwads; 19 | 20 | for(int i = 0; i < args.count()-1; i++) 21 | { 22 | if(args.at(i) == "-in") 23 | inFile = args.at(++i); 24 | else if(args.at(i) == "-out") 25 | outFile = args.at(++i); 26 | else if(args.at(i) == "-cfile") 27 | cFile = args.at(++i); 28 | else if(args.at(i) == "-pwad") 29 | { 30 | i++; 31 | 32 | while( (i < args.count()) && !args.at(i).startsWith("-")) 33 | { 34 | pwads.append(args.at(i)); 35 | i++; 36 | } 37 | } 38 | } 39 | 40 | //Also insert the GBADoom wad file. (Extra menu options etc) 41 | pwads.append(QCoreApplication::applicationDirPath().append("/gbadoom.wad")); 42 | 43 | WadFile wf(inFile); 44 | wf.LoadWadFile(); 45 | 46 | WadProcessor wp(wf); 47 | 48 | for(int i = 0; i < pwads.length(); i++) 49 | { 50 | WadFile pf(pwads.at(i)); 51 | pf.LoadWadFile(); 52 | 53 | wf.MergeWadFile(pf); 54 | } 55 | 56 | 57 | wp.ProcessWad(); 58 | 59 | if(outFile.length()) 60 | wf.SaveWadFile(outFile); 61 | 62 | if(cFile.length()) 63 | { 64 | QByteArray byteArray; 65 | QBuffer buffer(&byteArray); 66 | buffer.open(QIODevice::WriteOnly); 67 | 68 | wf.SaveWadFile(&buffer); 69 | 70 | SaveBytesAsCFile(byteArray, cFile); 71 | } 72 | } 73 | 74 | void SaveBytesAsCFile(QByteArray& bytes, QString file) 75 | { 76 | QFile f(file); 77 | 78 | if(!f.open(QIODevice::Truncate | QIODevice::ReadWrite)) 79 | return; 80 | 81 | QString decl = QString("const unsigned char doom_iwad[%1UL] = {\n").arg(bytes.size()); 82 | 83 | f.write(decl.toLatin1()); 84 | 85 | for(int i = 0; i < bytes.size(); i++) 86 | { 87 | QString element = QString("0x%1,").arg((quint8)bytes.at(i),2, 16, QChar('0')); 88 | 89 | if(( (i+1) % 40) == 0) 90 | element += "\n"; 91 | 92 | f.write(element.toLatin1()); 93 | } 94 | 95 | QString close = QString("\n};"); 96 | f.write(close.toLatin1()); 97 | 98 | f.close(); 99 | } 100 | -------------------------------------------------------------------------------- /wadfile.cpp: -------------------------------------------------------------------------------- 1 | #include "wadfile.h" 2 | #include "doomtypes.h" 3 | 4 | #define ROUND_UP4(x) ((x+3) & -4) 5 | 6 | WadFile::WadFile(QString filePath, QObject *parent) 7 | : QObject(parent) 8 | { 9 | wadPath = filePath; 10 | } 11 | 12 | bool WadFile::LoadWadFile() 13 | { 14 | QFile f(wadPath); 15 | 16 | if(!f.open(QIODevice::ReadOnly)) 17 | return false; 18 | 19 | QByteArray fd = f.readAll(); 20 | 21 | const char* wadData = fd.constData(); 22 | 23 | 24 | 25 | f.close(); 26 | 27 | const wadinfo_t* header = reinterpret_cast(wadData); 28 | 29 | QString id = QString(QLatin1String(header->identification, 4)); 30 | 31 | if(QString::compare(id, "IWAD") && QString::compare(id, "PWAD")) 32 | return false; 33 | 34 | const filelump_t* fileinfo = reinterpret_cast(&wadData[header->infotableofs]); 35 | 36 | for(int i = 0; i < header->numlumps; i++) 37 | { 38 | Lump l; 39 | l.name = QString(QLatin1String(fileinfo[i].name, 8)); 40 | l.name = QString(QLatin1String(l.name.toLatin1().constData())); 41 | 42 | l.length = fileinfo[i].size; 43 | l.data = QByteArray(&wadData[fileinfo[i].filepos], fileinfo[i].size); 44 | 45 | this->lumps.append(l); 46 | } 47 | 48 | return true; 49 | } 50 | 51 | bool WadFile::SaveWadFile(QString filePath) 52 | { 53 | QFile f(filePath); 54 | 55 | if(!f.open(QIODevice::Truncate | QIODevice::ReadWrite)) 56 | return false; 57 | 58 | bool ret = SaveWadFile(&f); 59 | f.close(); 60 | 61 | return ret; 62 | } 63 | 64 | bool WadFile::SaveWadFile(QIODevice* device) 65 | { 66 | if(!device->isOpen() || !device->isWritable()) 67 | return false; 68 | 69 | wadinfo_t header; 70 | 71 | header.numlumps = lumps.count(); 72 | 73 | header.identification[0] = 'I'; 74 | header.identification[1] = 'W'; 75 | header.identification[2] = 'A'; 76 | header.identification[3] = 'D'; 77 | 78 | header.infotableofs = sizeof(wadinfo_t); 79 | 80 | device->write(reinterpret_cast(&header), sizeof(header)); 81 | 82 | quint32 fileOffset = sizeof(wadinfo_t) + (sizeof(filelump_t)*lumps.count()); 83 | 84 | fileOffset = ROUND_UP4(fileOffset); 85 | 86 | 87 | //Write the file info blocks. 88 | for(int i = 0; i < lumps.count(); i++) 89 | { 90 | Lump l = lumps.at(i); 91 | 92 | filelump_t fl; 93 | 94 | memset(fl.name, 0, 8); 95 | strncpy(fl.name, l.name.toLatin1().toUpper().constData(), 8); 96 | 97 | fl.size = l.length; 98 | 99 | if(l.length > 0) 100 | fl.filepos = fileOffset; 101 | else 102 | fl.filepos = 0; 103 | 104 | device->write(reinterpret_cast(&fl), sizeof(fl)); 105 | 106 | fileOffset += l.length; 107 | fileOffset = ROUND_UP4(fileOffset); 108 | } 109 | 110 | //Write the lump data out. 111 | for(int i = 0; i < lumps.count(); i++) 112 | { 113 | Lump l = lumps.at(i); 114 | 115 | if(l.length == 0) 116 | continue; 117 | 118 | quint32 pos = device->pos(); 119 | 120 | pos = ROUND_UP4(pos); 121 | 122 | device->seek(pos); 123 | 124 | device->write(l.data, l.length); 125 | } 126 | 127 | return true; 128 | } 129 | 130 | qint32 WadFile::GetLumpByName(QString name, Lump& lump) 131 | { 132 | for(int i = lumps.length()-1; i >= 0; i--) 133 | { 134 | if(!lumps.at(i).name.compare(name, Qt::CaseInsensitive)) 135 | { 136 | lump = lumps.at(i); 137 | return i; 138 | } 139 | } 140 | 141 | return -1; 142 | } 143 | 144 | bool WadFile::GetLumpByNum(quint32 lumpnum, Lump& lump) 145 | { 146 | if(lumpnum >= lumps.count()) 147 | return false; 148 | 149 | lump = lumps.at(lumpnum); 150 | 151 | return true; 152 | } 153 | 154 | bool WadFile::ReplaceLump(quint32 lumpnum, Lump newLump) 155 | { 156 | if(lumpnum >= lumps.count()) 157 | return false; 158 | 159 | lumps.replace(lumpnum, newLump); 160 | 161 | return true; 162 | } 163 | 164 | bool WadFile::InsertLump(quint32 lumpnum, Lump newLump) 165 | { 166 | lumps.insert(lumpnum, newLump); 167 | 168 | return true; 169 | } 170 | 171 | bool WadFile::RemoveLump(quint32 lumpnum) 172 | { 173 | if(lumpnum >= lumps.count()) 174 | return false; 175 | 176 | lumps.removeAt(lumpnum); 177 | 178 | return true; 179 | } 180 | 181 | quint32 WadFile::LumpCount() 182 | { 183 | return lumps.count(); 184 | } 185 | 186 | bool WadFile::MergeWadFile(WadFile& wadFile) 187 | { 188 | for(quint32 i = 0; i < wadFile.LumpCount(); i++) 189 | { 190 | Lump l; 191 | 192 | wadFile.GetLumpByNum(i, l); 193 | 194 | InsertLump(0xffff, l); 195 | } 196 | 197 | return true; 198 | } 199 | -------------------------------------------------------------------------------- /doomtypes.h: -------------------------------------------------------------------------------- 1 | #ifndef DOOMTYPES_H 2 | #define DOOMTYPES_H 3 | 4 | #include 5 | 6 | 7 | typedef qint32 fixed_t; 8 | typedef quint32 angle_t; 9 | 10 | 11 | #define FRACBITS 16 12 | #define FRACUNIT (1<> (8*sizeof _t-1); 18 | return (_t^_s)-_s; 19 | } 20 | 21 | inline static fixed_t FixedDiv(fixed_t a, fixed_t b) 22 | { 23 | return ((unsigned)D_abs(a)>>14) >= (unsigned)D_abs(b) ? ((a^b)>>31) ^ INT_MAX : 24 | (fixed_t)(((qint64) a << FRACBITS) / b); 25 | } 26 | 27 | //************************************************************************************* 28 | //WAD file structs from Doom sources. 29 | //************************************************************************************* 30 | 31 | #pragma pack(push,1) 32 | 33 | typedef struct 34 | { 35 | char identification[4]; // Should be "IWAD" or "PWAD". 36 | qint32 numlumps; 37 | qint32 infotableofs; 38 | } wadinfo_t; 39 | 40 | typedef struct 41 | { 42 | qint32 filepos; 43 | qint32 size; 44 | char name[8]; 45 | } filelump_t; 46 | 47 | 48 | 49 | // 50 | // Map level types. 51 | // The following data structures define the persistent format 52 | // used in the lumps of the WAD files. 53 | // 54 | 55 | // Lump order in a map WAD: each map needs a couple of lumps 56 | // to provide a complete scene geometry description. 57 | enum { 58 | ML_LABEL, // A separator, name, ExMx or MAPxx 59 | ML_THINGS, // Monsters, items.. 60 | ML_LINEDEFS, // LineDefs, from editing 61 | ML_SIDEDEFS, // SideDefs, from editing 62 | ML_VERTEXES, // Vertices, edited and BSP splits generated 63 | ML_SEGS, // LineSegs, from LineDefs split by BSP 64 | ML_SSECTORS, // SubSectors, list of LineSegs 65 | ML_NODES, // BSP nodes 66 | ML_SECTORS, // Sectors, from editing 67 | ML_REJECT, // LUT, sector-sector visibility 68 | ML_BLOCKMAP // LUT, motion clipping, walls/grid element 69 | }; 70 | 71 | 72 | 73 | //************************************************************************************* 74 | //r_defs structs from Doom sources. 75 | //************************************************************************************* 76 | 77 | typedef struct 78 | { 79 | fixed_t x, y; 80 | } vertex_t; 81 | 82 | // A single Vertex. 83 | typedef struct 84 | { 85 | short x,y; 86 | } mapvertex_t; 87 | 88 | 89 | #define NO_INDEX ((unsigned short)-1) 90 | 91 | #define ML_TWOSIDED 4 92 | 93 | 94 | typedef enum 95 | { 96 | ST_HORIZONTAL, 97 | ST_VERTICAL, 98 | ST_POSITIVE, 99 | ST_NEGATIVE 100 | } slopetype_t; 101 | 102 | typedef enum 103 | { // cph: 104 | RF_TOP_TILE = 1, // Upper texture needs tiling 105 | RF_MID_TILE = 2, // Mid texture needs tiling 106 | RF_BOT_TILE = 4, // Lower texture needs tiling 107 | RF_IGNORE = 8, // Renderer can skip this line 108 | RF_CLOSED =16, // Line blocks view 109 | } r_flags; 110 | 111 | 112 | /* Bounding box coordinate storage. */ 113 | enum 114 | { 115 | BOXTOP, 116 | BOXBOTTOM, 117 | BOXLEFT, 118 | BOXRIGHT 119 | }; /* bbox coordinates */ 120 | 121 | 122 | typedef struct line_s 123 | { 124 | vertex_t v1; 125 | vertex_t v2; // Vertices, from v1 to v2. 126 | quint32 lineno; //line number. 127 | fixed_t dx, dy; // Precalculated v2 - v1 for side checking. 128 | 129 | quint16 sidenum[2]; // Visual appearance: SideDefs. 130 | 131 | fixed_t bbox[4]; //Line bounding box. 132 | 133 | quint16 flags; // Animation related. 134 | qint16 special; 135 | qint16 tag; 136 | quint16 slopetype; // To aid move clipping. 137 | 138 | } line_t; 139 | 140 | typedef struct { 141 | unsigned short v1; 142 | unsigned short v2; 143 | unsigned short flags; 144 | short special; 145 | short tag; 146 | // proff 07/23/2006 - support more than 32768 sidedefs 147 | // use the unsigned value and special case the -1 148 | // sidenum[1] will be -1 (NO_INDEX) if one sided 149 | unsigned short sidenum[2]; 150 | } maplinedef_t; 151 | 152 | 153 | // LineSeg, generated by splitting LineDefs 154 | // using partition lines selected by BSP builder. 155 | typedef struct { 156 | unsigned short v1; 157 | unsigned short v2; 158 | short angle; 159 | unsigned short linedef; 160 | short side; 161 | short offset; 162 | } mapseg_t; 163 | 164 | 165 | // 166 | // The LineSeg. 167 | // 168 | typedef struct 169 | { 170 | vertex_t v1; 171 | vertex_t v2; // Vertices, from v1 to v2. 172 | 173 | fixed_t offset; 174 | angle_t angle; 175 | 176 | quint16 sidenum; 177 | quint16 linenum; 178 | 179 | quint16 frontsectornum; 180 | quint16 backsectornum; 181 | } seg_t; 182 | 183 | 184 | 185 | // A SideDef, defining the visual appearance of a wall, 186 | // by setting textures and offsets. 187 | typedef struct 188 | { 189 | short textureoffset; 190 | short rowoffset; 191 | char toptexture[8]; 192 | char bottomtexture[8]; 193 | char midtexture[8]; 194 | short sector; // Front sector, towards viewer. 195 | } mapsidedef_t; 196 | 197 | // A SideDef, defining the visual appearance of a wall, 198 | // by setting textures and offsets. 199 | typedef struct 200 | { 201 | short textureoffset; 202 | short rowoffset; 203 | short toptexture; 204 | short bottomtexture; 205 | short midtexture; 206 | short sector; // Front sector, towards viewer. 207 | } sidedef_t; 208 | 209 | typedef struct 210 | { 211 | short originx; 212 | short originy; 213 | short patch; 214 | short stepdir; // unused in Doom but might be used in Phase 2 Boom 215 | short colormap; // unused in Doom but might be used in Phase 2 Boom 216 | } mappatch_t; 217 | 218 | typedef struct 219 | { 220 | char name[8]; 221 | char pad2[4]; // unused 222 | short width; 223 | short height; 224 | char pad[4]; // unused in Doom but might be used in Boom Phase 2 225 | short patchcount; 226 | mappatch_t patches[1]; 227 | } maptexture_t; 228 | 229 | #pragma pack(pop) 230 | 231 | #endif // DOOMTYPES_H 232 | -------------------------------------------------------------------------------- /wadprocessor.cpp: -------------------------------------------------------------------------------- 1 | #include "wadprocessor.h" 2 | #include "doomtypes.h" 3 | 4 | WadProcessor::WadProcessor(WadFile& wad, QObject *parent) 5 | : QObject(parent), wadFile(wad) 6 | { 7 | 8 | } 9 | 10 | bool WadProcessor::ProcessWad() 11 | { 12 | //Figure out if our IWAD is Doom or Doom2. (E1M1 or MAP01) 13 | 14 | Lump mapLump; 15 | 16 | RemoveUnusedLumps(); 17 | 18 | int lumpNum = wadFile.GetLumpByName("MAP01", mapLump); 19 | 20 | if(lumpNum != -1) 21 | { 22 | return ProcessD2Levels(); 23 | } 24 | else 25 | { 26 | int lumpNum = wadFile.GetLumpByName("E1M1", mapLump); 27 | 28 | //Can't find any maps. 29 | if(lumpNum == -1) 30 | return false; 31 | } 32 | 33 | return ProcessD1Levels(); 34 | } 35 | 36 | bool WadProcessor::ProcessD2Levels() 37 | { 38 | for(int m = 1; m <= 32; m++) 39 | { 40 | Lump l; 41 | 42 | QString mapName = QString("MAP%1").arg(m, 2, 10, QChar('0')); 43 | 44 | int lumpNum = wadFile.GetLumpByName(mapName, l); 45 | 46 | if(lumpNum != -1) 47 | { 48 | ProcessLevel(lumpNum); 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | 55 | bool WadProcessor::ProcessD1Levels() 56 | { 57 | for(int e = 1; e <= 4; e++) 58 | { 59 | for(int m = 1; m <= 9; m++) 60 | { 61 | Lump l; 62 | 63 | QString mapName = QString("E%1M%2").arg(e).arg(m); 64 | 65 | int lumpNum = wadFile.GetLumpByName(mapName, l); 66 | 67 | if(lumpNum != -1) 68 | { 69 | ProcessLevel(lumpNum); 70 | } 71 | } 72 | } 73 | 74 | return true; 75 | } 76 | 77 | bool WadProcessor::ProcessLevel(quint32 lumpNum) 78 | { 79 | ProcessVertexes(lumpNum); 80 | ProcessLines(lumpNum); 81 | ProcessSegs(lumpNum); 82 | ProcessSides(lumpNum); 83 | ProcessPNames(); 84 | 85 | return true; 86 | } 87 | 88 | bool WadProcessor::ProcessVertexes(quint32 lumpNum) 89 | { 90 | quint32 vtxLumpNum = lumpNum+ML_VERTEXES; 91 | 92 | Lump vxl; 93 | 94 | if(!wadFile.GetLumpByNum(vtxLumpNum, vxl)) 95 | return false; 96 | 97 | if(vxl.length == 0) 98 | return false; 99 | 100 | quint32 vtxCount = vxl.length / sizeof(mapvertex_t); 101 | 102 | vertex_t* newVtx = new vertex_t[vtxCount]; 103 | const mapvertex_t* oldVtx = reinterpret_cast(vxl.data.constData()); 104 | 105 | for(quint32 i = 0; i < vtxCount; i++) 106 | { 107 | newVtx[i].x = (oldVtx[i].x << 16); 108 | newVtx[i].y = (oldVtx[i].y << 16); 109 | } 110 | 111 | Lump newVxl; 112 | newVxl.name = vxl.name; 113 | newVxl.length = vtxCount * sizeof(vertex_t); 114 | newVxl.data = QByteArray(reinterpret_cast(newVtx), newVxl.length); 115 | 116 | delete[] newVtx; 117 | 118 | wadFile.ReplaceLump(vtxLumpNum, newVxl); 119 | 120 | return true; 121 | } 122 | 123 | bool WadProcessor::ProcessLines(quint32 lumpNum) 124 | { 125 | quint32 lineLumpNum = lumpNum+ML_LINEDEFS; 126 | 127 | Lump lines; 128 | 129 | if(!wadFile.GetLumpByNum(lineLumpNum, lines)) 130 | return false; 131 | 132 | if(lines.length == 0) 133 | return false; 134 | 135 | quint32 lineCount = lines.length / sizeof(maplinedef_t); 136 | 137 | line_t* newLines = new line_t[lineCount]; 138 | 139 | const maplinedef_t* oldLines = reinterpret_cast(lines.data.constData()); 140 | 141 | //We need vertexes for this... 142 | 143 | quint32 vtxLumpNum = lumpNum+ML_VERTEXES; 144 | 145 | Lump vxl; 146 | 147 | if(!wadFile.GetLumpByNum(vtxLumpNum, vxl)) 148 | return false; 149 | 150 | if(vxl.length == 0) 151 | return false; 152 | 153 | const vertex_t* vtx = reinterpret_cast(vxl.data.constData()); 154 | 155 | for(unsigned int i = 0; i < lineCount; i++) 156 | { 157 | newLines[i].v1.x = vtx[oldLines[i].v1].x; 158 | newLines[i].v1.y = vtx[oldLines[i].v1].y; 159 | 160 | newLines[i].v2.x = vtx[oldLines[i].v2].x; 161 | newLines[i].v2.y = vtx[oldLines[i].v2].y; 162 | 163 | newLines[i].special = oldLines[i].special; 164 | newLines[i].flags = oldLines[i].flags; 165 | newLines[i].tag = oldLines[i].tag; 166 | 167 | newLines[i].dx = newLines[i].v2.x - newLines[i].v1.x; 168 | newLines[i].dy = newLines[i].v2.y - newLines[i].v1.y; 169 | 170 | newLines[i].slopetype = 171 | !newLines[i].dx ? ST_VERTICAL : !newLines[i].dy ? ST_HORIZONTAL : 172 | FixedDiv(newLines[i].dy, newLines[i].dx) > 0 ? ST_POSITIVE : ST_NEGATIVE; 173 | 174 | newLines[i].sidenum[0] = oldLines[i].sidenum[0]; 175 | newLines[i].sidenum[1] = oldLines[i].sidenum[1]; 176 | 177 | newLines[i].bbox[BOXLEFT] = (newLines[i].v1.x < newLines[i].v2.x ? newLines[i].v1.x : newLines[i].v2.x); 178 | newLines[i].bbox[BOXRIGHT] = (newLines[i].v1.x < newLines[i].v2.x ? newLines[i].v2.x : newLines[i].v1.x); 179 | 180 | newLines[i].bbox[BOXTOP] = (newLines[i].v1.y < newLines[i].v2.y ? newLines[i].v2.y : newLines[i].v1.y); 181 | newLines[i].bbox[BOXBOTTOM] = (newLines[i].v1.y < newLines[i].v2.y ? newLines[i].v1.y : newLines[i].v2.y); 182 | 183 | newLines[i].lineno = i; 184 | 185 | } 186 | 187 | Lump newLine; 188 | newLine.name = lines.name; 189 | newLine.length = lineCount * sizeof(line_t); 190 | newLine.data = QByteArray(reinterpret_cast(newLines), newLine.length); 191 | 192 | delete[] newLines; 193 | 194 | wadFile.ReplaceLump(lineLumpNum, newLine); 195 | 196 | return true; 197 | } 198 | 199 | bool WadProcessor::ProcessSegs(quint32 lumpNum) 200 | { 201 | quint32 segsLumpNum = lumpNum+ML_SEGS; 202 | 203 | Lump segs; 204 | 205 | if(!wadFile.GetLumpByNum(segsLumpNum, segs)) 206 | return false; 207 | 208 | if(segs.length == 0) 209 | return false; 210 | 211 | quint32 segCount = segs.length / sizeof(mapseg_t); 212 | 213 | seg_t* newSegs = new seg_t[segCount]; 214 | 215 | const mapseg_t* oldSegs = reinterpret_cast(segs.data.constData()); 216 | 217 | //We need vertexes for this... 218 | 219 | quint32 vtxLumpNum = lumpNum+ML_VERTEXES; 220 | 221 | Lump vxl; 222 | 223 | if(!wadFile.GetLumpByNum(vtxLumpNum, vxl)) 224 | return false; 225 | 226 | if(vxl.length == 0) 227 | return false; 228 | 229 | const vertex_t* vtx = reinterpret_cast(vxl.data.constData()); 230 | 231 | //And LineDefs. Must process lines first. 232 | 233 | quint32 linesLumpNum = lumpNum+ML_LINEDEFS; 234 | 235 | Lump lxl; 236 | 237 | if(!wadFile.GetLumpByNum(linesLumpNum, lxl)) 238 | return false; 239 | 240 | if(lxl.length == 0) 241 | return false; 242 | 243 | const line_t* lines = reinterpret_cast(lxl.data.constData()); 244 | 245 | //And sides too... 246 | 247 | quint32 sidesLumpNum = lumpNum+ML_SIDEDEFS; 248 | 249 | Lump sxl; 250 | 251 | if(!wadFile.GetLumpByNum(sidesLumpNum, sxl)) 252 | return false; 253 | 254 | if(sxl.length == 0) 255 | return false; 256 | 257 | const mapsidedef_t* sides = reinterpret_cast(sxl.data.constData()); 258 | 259 | 260 | //**************************** 261 | 262 | for(unsigned int i = 0; i < segCount; i++) 263 | { 264 | newSegs[i].v1.x = vtx[oldSegs[i].v1].x; 265 | newSegs[i].v1.y = vtx[oldSegs[i].v1].y; 266 | 267 | newSegs[i].v2.x = vtx[oldSegs[i].v2].x; 268 | newSegs[i].v2.y = vtx[oldSegs[i].v2].y; 269 | 270 | newSegs[i].angle = oldSegs[i].angle << 16; 271 | newSegs[i].offset = oldSegs[i].offset << 16; 272 | 273 | newSegs[i].linenum = oldSegs[i].linedef; 274 | 275 | const line_t* ldef = &lines[newSegs[i].linenum]; 276 | 277 | int side = oldSegs[i].side; 278 | 279 | newSegs[i].sidenum = ldef->sidenum[side]; 280 | 281 | if(newSegs[i].sidenum != NO_INDEX) 282 | { 283 | newSegs[i].frontsectornum = sides[newSegs[i].sidenum].sector; 284 | } 285 | else 286 | { 287 | newSegs[i].frontsectornum = NO_INDEX; 288 | } 289 | 290 | newSegs[i].backsectornum = NO_INDEX; 291 | 292 | if(ldef->flags & ML_TWOSIDED) 293 | { 294 | if(ldef->sidenum[side^1] != NO_INDEX) 295 | { 296 | newSegs[i].backsectornum = sides[ldef->sidenum[side^1]].sector; 297 | } 298 | } 299 | } 300 | 301 | Lump newSeg; 302 | newSeg.name = segs.name; 303 | newSeg.length = segCount * sizeof(seg_t); 304 | newSeg.data = QByteArray(reinterpret_cast(newSegs), newSeg.length); 305 | 306 | delete[] newSegs; 307 | 308 | wadFile.ReplaceLump(segsLumpNum, newSeg); 309 | 310 | return true; 311 | } 312 | 313 | bool WadProcessor::ProcessSides(quint32 lumpNum) 314 | { 315 | quint32 sidesLumpNum = lumpNum+ML_SIDEDEFS; 316 | 317 | Lump sides; 318 | 319 | if(!wadFile.GetLumpByNum(sidesLumpNum, sides)) 320 | return false; 321 | 322 | if(sides.length == 0) 323 | return false; 324 | 325 | quint32 sideCount = sides.length / sizeof(mapsidedef_t); 326 | 327 | sidedef_t* newSides = new sidedef_t[sideCount]; 328 | 329 | const mapsidedef_t* oldSides = reinterpret_cast(sides.data.constData()); 330 | 331 | for(unsigned int i = 0; i < sideCount; i++) 332 | { 333 | newSides[i].textureoffset = oldSides[i].textureoffset; 334 | newSides[i].rowoffset = oldSides[i].rowoffset; 335 | 336 | newSides[i].toptexture = GetTextureNumForName(oldSides[i].toptexture); 337 | newSides[i].bottomtexture = GetTextureNumForName(oldSides[i].bottomtexture); 338 | newSides[i].midtexture = GetTextureNumForName(oldSides[i].midtexture); 339 | 340 | newSides[i].sector = oldSides[i].sector; 341 | } 342 | 343 | Lump newSide; 344 | newSide.name = sides.name; 345 | newSide.length = sideCount * sizeof(sidedef_t); 346 | newSide.data = QByteArray(reinterpret_cast(newSides), newSide.length); 347 | 348 | delete[] newSides; 349 | 350 | wadFile.ReplaceLump(sidesLumpNum, newSide); 351 | 352 | return true; 353 | } 354 | 355 | int WadProcessor::GetTextureNumForName(const char* tex_name) 356 | { 357 | const int *maptex1, *maptex2; 358 | int numtextures1, numtextures2 = 0; 359 | const int *directory1, *directory2; 360 | 361 | 362 | //Convert name to uppercase for comparison. 363 | char tex_name_upper[9]; 364 | 365 | strncpy(tex_name_upper, tex_name, 8); 366 | tex_name_upper[8] = 0; //Ensure null terminated. 367 | 368 | for (int i = 0; i < 8; i++) 369 | { 370 | tex_name_upper[i] = toupper(tex_name_upper[i]); 371 | } 372 | 373 | Lump tex1lump; 374 | wadFile.GetLumpByName("TEXTURE1", tex1lump); 375 | 376 | maptex1 = (const int*)tex1lump.data.constData(); 377 | numtextures1 = *maptex1; 378 | directory1 = maptex1+1; 379 | 380 | Lump tex2lump; 381 | if (wadFile.GetLumpByName("TEXTURE2", tex2lump) != -1) 382 | { 383 | maptex2 = (const int*)tex2lump.data.constData(); 384 | directory2 = maptex2+1; 385 | numtextures2 = *maptex2; 386 | } 387 | else 388 | { 389 | maptex2 = NULL; 390 | directory2 = NULL; 391 | } 392 | 393 | const int *directory = directory1; 394 | const int *maptex = maptex1; 395 | 396 | int numtextures = (numtextures1 + numtextures2); 397 | 398 | for (int i=0 ; iname, 8)) 412 | { 413 | return i; 414 | } 415 | } 416 | 417 | return 0; 418 | } 419 | 420 | bool WadProcessor::ProcessPNames() 421 | { 422 | Lump pnamesLump; 423 | qint32 lumpNum = wadFile.GetLumpByName("PNAMES", pnamesLump); 424 | 425 | if(lumpNum == -1) 426 | return false; 427 | 428 | const char* pnamesData = (const char*)pnamesLump.data.constData(); 429 | 430 | quint32 count = *((quint32*)pnamesData); 431 | 432 | pnamesData += 4; //Fist 4 bytes are count. 433 | 434 | QStringList pnamesUpper; 435 | 436 | for(quint32 i = 0; i < count; i++) 437 | { 438 | char n[9] = {0}; 439 | strncpy(n, &pnamesData[i*8], 8); 440 | 441 | 442 | QLatin1String nl(n); 443 | QString newName(nl); 444 | 445 | pnamesUpper.push_back(newName.toUpper()); 446 | } 447 | 448 | char* newPnames = new char[(count * 8) + 4]; 449 | memset(newPnames, 0, (count * 8) + 4); 450 | 451 | *((quint32*)newPnames) = count; //Write count of pnames. 452 | 453 | char* newPnames2 = &newPnames[4]; //Start of name list. 454 | 455 | for(quint32 i = 0; i < count; i++) 456 | { 457 | QByteArray pl = pnamesUpper[i].toLatin1(); 458 | 459 | strncpy(&newPnames2[i*8], pl.constData(), 8); 460 | } 461 | 462 | Lump newLump; 463 | newLump.name = "PNAMES"; 464 | newLump.length = (count * 8) + 4; 465 | newLump.data = QByteArray(reinterpret_cast(newPnames), newLump.length); 466 | 467 | delete[] newPnames; 468 | 469 | wadFile.ReplaceLump(lumpNum, newLump); 470 | 471 | return true; 472 | } 473 | 474 | bool WadProcessor::RemoveUnusedLumps() 475 | { 476 | for(quint32 i = 0; i < wadFile.LumpCount(); i++) 477 | { 478 | Lump l; 479 | 480 | wadFile.GetLumpByNum(i, l); 481 | 482 | if( l.name.startsWith("D_") || 483 | l.name.startsWith("DP") || 484 | l.name.startsWith("DS") || 485 | l.name.startsWith("GENMIDI")) 486 | { 487 | wadFile.RemoveLump(i); 488 | i--; 489 | } 490 | } 491 | 492 | return true; 493 | } 494 | --------------------------------------------------------------------------------