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