[q3map2] Unwind script stack in case of script loading error.
[xonotic/netradiant.git] / plugins / archivewad / archive.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "archive.h"
23
24 #include "idatastream.h"
25 #include "cmdlib.h"
26 #include "bytestreamutils.h"
27 #include <algorithm>
28 #include "stream/filestream.h"
29
30 #include "iarchive.h"
31
32 #include "archivelib.h"
33
34 #include <map>
35 #include "string/string.h"
36
37 #include "wad.h"
38
39 class WadArchive : public Archive {
40     class wad_record_t {
41     public:
42         wad_record_t(unsigned int position, unsigned int stream_size, unsigned int file_size)
43                 : m_position(position), m_stream_size(stream_size), m_file_size(file_size)
44         {}
45
46         unsigned int m_position;
47         unsigned int m_stream_size;
48         unsigned int m_file_size;
49     };
50
51     enum EWadVersion {
52         eNotValid,
53         eWAD2,
54         eWAD3,
55     };
56
57     typedef std::map<CopiedString, wad_record_t, StringLessNoCase> files_t;
58     files_t m_files;
59     CopiedString m_name;
60     FileInputStream m_wadfile;
61
62     EWadVersion wad_version(const char *identification)
63     {
64         if (strncmp(identification, "WAD2", 4) == 0) {
65             return eWAD2;
66         }
67         if (strncmp(identification, "WAD3", 4) == 0) {
68             return eWAD3;
69         }
70         return eNotValid;
71     }
72
73     const char *type_for_version(EWadVersion version)
74     {
75         switch (version) {
76             case eWAD2:
77                 return ".mip";
78             case eWAD3:
79                 return ".hlw";
80             default:
81                 break;
82         }
83         return "";
84     }
85
86     int miptex_type_for_version(EWadVersion version)
87     {
88         switch (version) {
89             case eWAD2:
90                 return TYP_MIPTEX;
91             case eWAD3:
92                 return 67;
93             default:
94                 break;
95         }
96         return -1;
97     }
98
99 public:
100     WadArchive(const char *name)
101             : m_name(name), m_wadfile(name)
102     {
103         if (!m_wadfile.failed()) {
104             wadinfo_t wadinfo;
105             istream_read_wadinfo(m_wadfile, wadinfo);
106
107             EWadVersion version = wad_version(wadinfo.identification);
108             int miptexType = miptex_type_for_version(version);
109
110             if (version != eNotValid) {
111                 m_wadfile.seek(wadinfo.infotableofs);
112
113                 for (int i = 0; i < wadinfo.numlumps; ++i) {
114                     char buffer[32];
115                     lumpinfo_t lumpinfo;
116                     istream_read_lumpinfo(m_wadfile, lumpinfo);
117                     if (lumpinfo.type == miptexType) {
118                         strcpy(buffer, "textures/");
119                         strcat(buffer, lumpinfo.name);
120                         strcat(buffer, type_for_version(version));
121                         m_files.insert(files_t::value_type(buffer, wad_record_t(lumpinfo.filepos, lumpinfo.disksize,
122                                                                                 lumpinfo.size)));
123                     }
124                 }
125             }
126         }
127     }
128
129     void release()
130     {
131         delete this;
132     }
133
134     ArchiveFile *openFile(const char *name)
135     {
136         files_t::iterator i = m_files.find(name);
137         if (i != m_files.end()) {
138             return StoredArchiveFile::create(name, m_name.c_str(), i->second.m_position, i->second.m_stream_size,
139                                              i->second.m_file_size);
140         }
141         return 0;
142     }
143
144     virtual ArchiveTextFile *openTextFile(const char *name)
145     {
146         files_t::iterator i = m_files.find(name);
147         if (i != m_files.end()) {
148             return StoredArchiveTextFile::create(name, m_name.c_str(), i->second.m_position, i->second.m_stream_size);
149         }
150         return 0;
151     }
152
153     bool containsFile(const char *name)
154     {
155         return m_files.find(name) != m_files.end();
156     }
157
158     void forEachFile(VisitorFunc visitor, const char *root)
159     {
160         if (root[0] == '\0') {
161             if (visitor.directory("textures/", 1)) {
162                 return;
163             }
164         } else if (strcmp(root, "textures/") != 0) {
165             return;
166         }
167
168         for (files_t::iterator i = m_files.begin(); i != m_files.end(); ++i) {
169             visitor.file(i->first.c_str());
170         }
171     }
172 };
173
174
175 Archive *OpenArchive(const char *name)
176 {
177     return new WadArchive(name);
178 }
179
180 #if 0
181
182 class TestArchive
183 {
184 class TestVisitor : public Archive::IVisitor
185 {
186 public:
187 void visit( const char* name ){
188     int bleh = 0;
189 }
190 };
191 public:
192 TestArchive(){
193     {
194         Archive* archive = OpenArchive( "" );
195         archive->release();
196     }
197     {
198         Archive* archive = OpenArchive( "NONEXISTANTFILE" );
199         archive->release();
200     }
201     {
202         Archive* archive = OpenArchive( "c:/quake/id1/quake101.wad" );
203         ArchiveFile* file = archive->openFile( "textures/sky1.mip" );
204         if ( file != 0 ) {
205             unsigned char* buffer = new unsigned char[file->size()];
206             file->getInputStream().read( (InputStream::byte_type*)buffer, file->size() );
207             delete[] buffer;
208             file->release();
209         }
210         TestVisitor visitor;
211         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "" );
212         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 0 ), "" );
213         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 0 ), "textures/" );
214         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "textures/" );
215         archive->release();
216     }
217 }
218 };
219
220 TestArchive g_test;
221
222 #endif