1 /*
2     Unmass - unpacker for game archives.
3     Copyright (C) 2002-2007  Mirex
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include <stdlib.h>
21 
22 #include "ma.h"
23 #include "utools.h"
24 
CMassArchive()25 CMassArchive::CMassArchive()
26 {
27 	massf = NULL; pFileList = NULL; FileListSize = 0;
28 	files_count = 0; FileRec.set = 0;
29 	next_set = false; next_rec_num = 0;
30 }
31 
32 //default extract for plain data
33 //return: bytes read
Extract(unsigned long offset,unsigned long size,unsigned char * output)34 unsigned long CMassArchive::Extract( unsigned long offset, unsigned long size, unsigned char *output )
35 {
36 	if ( massf == NULL )
37 		return 0;
38 
39 	if ( offset + size > FileRec.size ) {
40 		SetErrorStr( "Out of file" );
41 		return 0;
42 	}
43 
44 	//no compression
45 	if ( fseek( massf, FileRec.offset + offset, SEEK_SET ) != 0 ) {
46 		SetErrorStr( "Error seeking" );
47 		return 0;
48 	}
49 
50 	return fread( output, 1, size, massf );
51 }
52 
ExtractWholeFile(FILE * fout)53 int	CMassArchive::ExtractWholeFile( FILE *fout )
54 {
55 /*
56 	pos = 0; size = FF8bufferSize;
57 	while ( pos < FileRec.size ) {
58 
59 		if ( pos + size > FileRec.size )
60 			size = FileRec.size - pos;
61 
62 		if ( fread( buf, 1, size, fs ) != size )
63 			return 0;
64 
65 		if ( fwrite( buf, 1, size, fout ) != size )
66 			return 0;
67 
68 		pos += size;
69 	}*/
70 	return 0;
71 }
72 
OpenFile(const char * filename)73 int CMassArchive::OpenFile( const char *filename )
74 {
75 	if ( massf != NULL )
76 		fclose( massf );
77 
78 	massf = fopen( filename, "rb" );
79 
80 	files_count = 0; FileRec.set = 0;
81 	FileListItems = 0;
82 	next_set = false; next_rec_num = 0;
83 
84 	strncpy( MassFileNameFull, filename, FileNameWithPathMaxLen - 1 );
85 	// MassFileNameFull[ FileNameWithPathMaxLen-1 ] = 0;
86 
87 	int		i, j;
88 
89 	//find last slash
90 	i = strlen( MassFileNameFull );
91 	while (( i > 0 ) && ( MassFileNameFull[ i ] != '\\' ) &&
92 			( MassFileNameFull[ i ] != '/' ))
93 		i--;
94 	if ( i != 0 )
95 		i++;
96 
97 	// get path from result
98 	strncpy( MassFilePath, MassFileNameFull, FileNameWithPathMaxLen-1 );
99 	MassFilePath[ i ] = 0;
100 
101 	// find out filename and extension from the rest
102 	strncpy( MassFileName, &MassFileNameFull[ i ], FileNameWithPathMaxLen-1 );
103 
104 	i = strlen( MassFileName );
105 	while (( i > 0 ) && ( MassFileName[ i ] != '.' ))
106 		i--;
107 
108 	if (( i == 0 ) && ( MassFileName[ i ] != '.' ))
109 		MassFileExt[ 0 ] = 0;
110 	else
111 		strncpy( MassFileExt, &MassFileName[ i + 1 ], FileNameWithPathMaxLen-1 );
112 	MassFileName[ i ] = 0;
113 
114 	UnmassTools::_strlwr( MassFileName );
115 	UnmassTools::_strlwr( MassFileExt );
116 
117 	file_open_for_write = false;
118 
119 	return ( massf != NULL ) ? 1 : 0;
120 }
121 
CloseFile()122 void CMassArchive::CloseFile()
123 {
124 	if ( massf != NULL )
125 		fclose( massf );
126 	massf = NULL;
127 
128 	if ( pFileList != NULL )
129 		free( pFileList );
130 	pFileList = NULL;
131 
132 	FileListSize = 0;
133 	files_count = 0;
134 }
135 
ReadRecords(void)136 int CMassArchive::ReadRecords( void )
137 {
138 	if ( massf == NULL ) {
139 		SetErrorStr( "File not opened" );
140 		return 0;
141 	}
142 
143 	if ( FileListItems >= files_count ) {
144 		SetErrorStr( "Already filled" );
145 		return 0;
146 	}
147 
148 	int		res;
149 
150 	if ( FileListItems == 0 ) {
151 
152 		if ( GrowFileList() == 0 )
153 			return 0;
154 
155 		res = ReadRec( 0 );
156 	}
157 	else
158 		res = ReadRec( -1 );
159 
160 	if ( res == 1 ) {
161 		FileRec.index = FileListItems;
162 		pFileList[ FileListItems ] = FileRec;
163 	} else
164 		pFileList[ FileListItems ].Reset();
165 
166 	FileListItems++;
167 
168 	return res;
169 
170 }
171 
GetRec(long num)172 int CMassArchive::GetRec( long num )
173 {
174 	if (( num < 0 ) || ( num >= files_count )) {
175 		SetErrorStr( "Bad index" );
176 		return 0;
177 	}
178 
179 	FileRec = pFileList[ num ];
180 	return 1;
181 }
182 
AddFileData(unsigned long offset,unsigned long size,char * data)183 int	CMassArchive::AddFileData( unsigned long offset, unsigned long size, char* data )
184 {
185 	SetErrorStr( "AddFileData:" );
186 
187 	if ( ! file_open_for_write ) {
188 		SetErrorStr( "Not open for writing" );
189 		return 0;
190 	}
191 
192 	if (( FileRec.index < 0 ) || ( FileRec.index >= files_count )) {
193 		SetErrorStr( "Bad index" );
194 		return 0;
195 	}
196 
197 	s_FileRec	*pFR;
198 
199 	pFR = &pFileList[ FileRec.index ];
200 
201 	if ( offset + size > pFR->size ) {
202 		SetErrorStr( "Data out of file" );
203 		return 0;
204 	}
205 
206 	fseek( massf, pFR->offset + offset, SEEK_SET );
207 	fwrite( data, size, 1, massf );
208 
209 	return 1;
210 }
211 
GrowFileList(void)212 int	CMassArchive::GrowFileList( void )
213 {
214 	s_FileRec		*pNewList;
215 	unsigned long	ul, oldlistsize;
216 
217 	if ( files_count <= FileListSize )
218 		return 1;
219 
220 	oldlistsize = FileListSize;
221 	if ( FileListSize == 0 )
222 		FileListSize = 4;
223 
224 	while ( FileListSize < files_count )
225 		FileListSize *= 2;
226 
227 	ul = sizeof( FileRec ) * FileListSize;
228 	pNewList = (s_FileRec*) realloc( pFileList, ul );
229 	if ( pNewList == NULL ) {
230 		SetErrorStr( "Not enough memory for new list" );
231 		FileListSize = oldlistsize;
232 		return 0;
233 	}
234 	pFileList = pNewList;
235 
236 	return 1;
237 }
238 
ReopenForWriting(void)239 int CMassArchive::ReopenForWriting( void )
240 {
241 	SetErrorStr( "ReopenForWriting:" );
242 
243 	if ( massf == NULL ) {
244 		SetErrorStr( "File not opened yet" );
245 		return 0;
246 	}
247 
248 	if ( file_open_for_write )
249 		return 1;
250 
251 	fclose( massf );
252 
253 	massf = fopen( MassFileNameFull, "r+b" );
254 
255 	if ( massf == NULL ) {
256 		SetErrorStr( "File could not be opened. It might be read-only." );
257 
258 		massf = fopen( MassFileNameFull, "rb" );
259 		if ( massf == NULL )
260 			SetErrorStr( "Could not open file, neither for reading" );
261 		return 0;
262 	}
263 
264 	file_open_for_write = true;
265 
266 	return 1;
267 }
268 
269