]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/archivepak/archive.cpp
Merge branch 'transfilterfix' into 'master'
[xonotic/netradiant.git] / plugins / archivepak / 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
35 #include <map>
36 #include "string/string.h"
37 #include "fs_filesystem.h"
38
39 inline void buffer_findreplace(char *buffer, char find, char replace)
40 {
41     while (*buffer != '\0') {
42         if (*buffer == find) {
43             *buffer = replace;
44         }
45         ++buffer;
46     }
47 }
48
49 #include "pak.h"
50
51 class PakArchive : public Archive {
52     class PakRecord {
53     public:
54         PakRecord(unsigned int position, unsigned int stream_size)
55                 : m_position(position), m_stream_size(stream_size)
56         {
57         }
58
59         unsigned int m_position;
60         unsigned int m_stream_size;
61     };
62
63     typedef GenericFileSystem<PakRecord> PakFileSystem;
64     PakFileSystem m_filesystem;
65     FileInputStream m_pakfile;
66     CopiedString m_name;
67
68 public:
69
70     PakArchive(const char *name)
71             : m_pakfile(name), m_name(name)
72     {
73         if (!m_pakfile.failed()) {
74             pakheader_t header;
75
76             m_pakfile.read(reinterpret_cast<FileInputStream::byte_type *>( header.magic ), 4);
77             header.diroffset = istream_read_uint32_le(m_pakfile);
78             header.dirsize = istream_read_uint32_le(m_pakfile);
79
80             if (strncmp(header.magic, "PACK", 4) == 0) {
81                 m_pakfile.seek(header.diroffset);
82
83                 for (unsigned int i = 0; i < header.dirsize; i += sizeof(pakentry_t)) {
84                     pakentry_t entry;
85
86                     m_pakfile.read(reinterpret_cast<FileInputStream::byte_type *>( entry.filename ), 0x38);
87                     entry.offset = istream_read_uint32_le(m_pakfile);
88                     entry.size = istream_read_uint32_le(m_pakfile);
89
90                     buffer_findreplace(entry.filename, '\\', '/');
91
92                     PakFileSystem::entry_type &file = m_filesystem[entry.filename];
93                     if (!file.is_directory()) {
94                         globalOutputStream() << "Warning: pak archive " << makeQuoted(m_name.c_str())
95                                              << " contains duplicated file: " << makeQuoted(entry.filename) << "\n";
96                     } else {
97                         file = new PakRecord(entry.offset, entry.size);
98                     }
99                 }
100             }
101         }
102     }
103
104     ~PakArchive()
105     {
106         for (PakFileSystem::iterator i = m_filesystem.begin(); i != m_filesystem.end(); ++i) {
107             delete i->second.file();
108         }
109     }
110
111     void release()
112     {
113         delete this;
114     }
115
116     ArchiveFile *openFile(const char *name)
117     {
118         PakFileSystem::iterator i = m_filesystem.find(name);
119         if (i != m_filesystem.end() && !i->second.is_directory()) {
120             PakRecord *file = i->second.file();
121             return StoredArchiveFile::create(name, m_name.c_str(), file->m_position, file->m_stream_size,
122                                              file->m_stream_size);
123         }
124         return 0;
125     }
126
127     virtual ArchiveTextFile *openTextFile(const char *name)
128     {
129         PakFileSystem::iterator i = m_filesystem.find(name);
130         if (i != m_filesystem.end() && !i->second.is_directory()) {
131             PakRecord *file = i->second.file();
132             return StoredArchiveTextFile::create(name, m_name.c_str(), file->m_position, file->m_stream_size);
133         }
134         return 0;
135     }
136
137     bool containsFile(const char *name)
138     {
139         PakFileSystem::iterator i = m_filesystem.find(name);
140         return i != m_filesystem.end() && !i->second.is_directory();
141     }
142
143     void forEachFile(VisitorFunc visitor, const char *root)
144     {
145         m_filesystem.traverse(visitor, root);
146     }
147 };
148
149
150 Archive *OpenArchive(const char *name)
151 {
152     return new PakArchive(name);
153 }
154
155 #if 0
156
157 class TestArchive
158 {
159 public:
160 TestArchive(){
161     Archive* archive = OpenArchive( "c:/quake3/baseq3/pak0.pak" );
162     ArchiveFile* file = archive->openFile( "gfx/palette.lmp" );
163     if ( file != 0 ) {
164         char buffer[1024];
165         file->getInputStream().read( (InputStream::byte_type*)buffer, 1024 );
166         file->release();
167     }
168     archive->release();
169 }
170 };
171
172 TestArchive g_test;
173
174 #endif
175
176 #if 0
177
178 class TestArchive
179 {
180 class TestVisitor : public Archive::IVisitor
181 {
182 public:
183 void visit( const char* name ){
184     int bleh = 0;
185 }
186 };
187 public:
188 TestArchive(){
189     {
190         Archive* archive = OpenArchive( "" );
191         archive->release();
192     }
193     {
194         Archive* archive = OpenArchive( "NONEXISTANTFILE" );
195         archive->release();
196     }
197     {
198         Archive* archive = OpenArchive( "c:/quake/id1/pak0.pak" );
199         ArchiveFile* file = archive->openFile( "gfx/palette.lmp" );
200         if ( file != 0 ) {
201             char buffer[1024];
202             file->getInputStream().read( (InputStream::byte_type*)buffer, 1024 );
203             file->release();
204         }
205         TestVisitor visitor;
206         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 0 ), "" );
207         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFiles, 0 ), "progs/" );
208         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFiles, 0 ), "maps/" );
209         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFiles, 1 ), "sound/ambience/" );
210         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 1 ), "sound/" );
211         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eDirectories, 1 ), "sound/" );
212         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 2 ), "sound/" );
213         archive->forEachFile( Archive::VisitorFunc( &visitor, Archive::eFilesAndDirectories, 2 ), "" );
214         archive->release();
215     }
216 }
217 };
218
219 TestArchive g_test;
220
221 #endif