Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Archive.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: Archive.cpp
7  AUTHOR: John DiCamillo
8 
9 */
10 
11 #include "MemDebug.h"
12 #include "Types.h"
13 #include "Archive.h"
14 
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <io.h>
19 #include <stdio.h>
20 #include <time.h>
21 
22 #include "zlib.h"
23 
24 // +--------------------------------------------------------------------+
25 
26 void Print(const char* fmt, ...);
27 
28 int verbose = 1;
29 int err;
30 
31 #define CHECK_ERR(err, msg) { \
32  if (err != Z_OK) { \
33  fprintf(stderr, "%s error: %d\n", msg, err); \
34  exit(1); \
35  } \
36  }
37 
38 // +--------------------------------------------------------------------+
39 
40 DataArchive::DataArchive(const char* name)
41 {
42  ZeroMemory(this, sizeof(DataArchive));
43 
44  if (name)
45  LoadDatafile(name);
46 }
47 
49 {
50  delete [] block_map;
51  delete [] directory;
52 }
53 
54 // +--------------------------------------------------------------------+
55 
56 void DataArchive::WriteEntry(int index, BYTE* buf)
57 {
58  int f = _open(datafile, _O_RDWR|_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE);
59 
60  if (f != -1) {
61  header.dir_size_comp = DirBlocks() * BLOCK_SIZE;
62  dirbuf = new(__FILE__,__LINE__) BYTE[header.dir_size_comp];
63 
64  if (!dirbuf) {
65  err = Z_MEM_ERROR;
66  }
67 
68  else {
69  err = compress(dirbuf, &header.dir_size_comp,
70  (BYTE*) directory, header.nfiles * sizeof(DataEntry));
71  CHECK_ERR(err, "compress");
72 
73  header.dir_blocks = Blocks(header.dir_size_comp) * BLOCK_SIZE;
74 
75  _lseek(f, 0, SEEK_SET);
76  _write(f, &header, sizeof(DataHeader));
77  _lseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
78  _write(f, dirbuf, header.dir_blocks);
79 
80  delete [] dirbuf;
81  dirbuf = 0;
82  }
83 
84  if (buf && directory && directory[index].size_comp) {
85  _lseek(f, sizeof(DataHeader) + directory[index].offset, SEEK_SET);
86  _write(f, buf, directory[index].size_comp);
87  }
88  _close(f);
89  }
90  else
91  perror("WriteEntry");
92 }
93 
94 // +--------------------------------------------------------------------+
95 
96 DWORD DataArchive::Blocks(DWORD raw_size)
97 {
98  int full_blocks = raw_size / BLOCK_SIZE;
99  int part_blocks = (raw_size % BLOCK_SIZE) > 0;
100 
101  return full_blocks + part_blocks;
102 }
103 
105 {
106  DWORD result = Blocks(header.nfiles * sizeof(DataEntry));
107  if (result == 0) result = 1;
108  return result;
109 }
110 
111 DWORD DataArchive::FileBlocks(int index)
112 {
113  if (index >= 0 && index < (int) header.nfiles && directory)
114  return Blocks(directory[index].size_comp);
115 
116  return 0;
117 }
118 
119 // +--------------------------------------------------------------------+
120 
122 {
123  delete [] block_map;
124  block_map = 0;
125 
126  if (header.nfiles == 0) return;
127 
128  DWORD i,j;
129  DWORD dir_usage = header.dir_offset + DirBlocks() * BLOCK_SIZE;
130  DWORD max_usage = dir_usage;
131 
132  for (i = 0; i < header.nfiles; i++) {
133  DWORD last_block = directory[i].offset + FileBlocks(i) * BLOCK_SIZE;
134  if (last_block > max_usage)
135  max_usage = last_block;
136  }
137 
138  nblocks = max_usage/BLOCK_SIZE;
139  block_map = new(__FILE__,__LINE__) DWORD[nblocks];
140 
141  if (!block_map) {
142  nblocks = 0;
143  }
144 
145  else {
146  ZeroMemory(block_map, nblocks*sizeof(DWORD));
147 
148  DWORD first_block = header.dir_offset/BLOCK_SIZE +
149  (header.dir_offset%BLOCK_SIZE > 0);
150 
151  for (j = 0; j < DirBlocks(); j++)
152  block_map[first_block+j] = 1;
153 
154  for (i = 0; i < header.nfiles; i++) {
155  DWORD first_block = directory[i].offset/BLOCK_SIZE +
156  (directory[i].offset%BLOCK_SIZE > 0);
157 
158  for (j = 0; j < FileBlocks(i); j++)
159  block_map[first_block+j] = i+2;
160  }
161  }
162 }
163 
164 // +--------------------------------------------------------------------+
165 
167 {
168  if ((int) (nblocks)-need > 0) {
169  DWORD start;
170  int i;
171 
172  for (start = 0; start < nblocks-need; start++) {
173  for (i = 0; block_map[start+i] == 0 && i < need; i++);
174 
175  if (i == need) return start*BLOCK_SIZE;
176 
177  start += i;
178  }
179  }
180 
181  return nblocks*BLOCK_SIZE;
182 }
183 
184 // +--------------------------------------------------------------------+
185 
186 void DataArchive::LoadDatafile(const char* name)
187 {
188  if (!name) return;
189 
190  delete [] directory;
191  delete [] block_map;
192 
193  ZeroMemory(this, sizeof(DataArchive));
194  strncpy_s(datafile, name, NAMELEN-1);
195 
196  FILE* f;
197  fopen_s(&f, datafile, "rb");
198  if (f) {
199  fread(&header, sizeof(DataHeader), 1, f);
200 
201  if (header.version != VERSION) {
202  Print("ERROR: datafile '%s' invalid version '%d'\n",
203  datafile, header.version);
204  fclose(f);
205  ZeroMemory(&header, sizeof(header));
206  return;
207  }
208 
209  DWORD len = DirBlocks() * BLOCK_SIZE;
210  DWORD dirsize = header.nfiles + 64;
211 
212  dirbuf = new(__FILE__,__LINE__) BYTE[len];
213  directory = new(__FILE__,__LINE__) DataEntry[dirsize];
214 
215  if (!dirbuf || !directory) {
216  err = Z_MEM_ERROR;
217  }
218 
219  else {
220  ZeroMemory(directory, sizeof(DataEntry) * dirsize);
221 
222  fseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
223  fread(dirbuf, header.dir_size_comp, 1, f);
224 
225  int err = uncompress((BYTE*) directory, &len,
226 #pragma warning(suppress: 6029)
227  dirbuf, header.dir_size_comp);
228  if (err != Z_OK)
229  ZeroMemory(directory, sizeof(DataEntry) * dirsize);
230 
231  delete [] dirbuf;
232  dirbuf = 0;
233 
234  CreateBlockMap();
235  }
236  }
237  else {
238  Print("Creating Archive '%s'...\n", datafile);
239 
240  header.version = VERSION;
241  header.nfiles = 0;
242  header.dir_blocks = 0;
243  header.dir_size_comp = 0;
244  header.dir_offset = 0;
245 
246  nblocks = DirBlocks();
247 
248  delete [] block_map;
249  block_map = 0;
250  }
251 }
252 
253 // +--------------------------------------------------------------------+
254 
255 int DataArchive::FindEntry(const char* req_name)
256 {
257  int entry = -1;
258 
259  if (req_name && *req_name && directory) {
260  char path[256];
261  int len = strlen(req_name);
262 
263  ZeroMemory(path, sizeof(path));
264 
265  for (int c = 0; c < len; c++) {
266  if (req_name[c] == '\\')
267  path[c] = '/';
268  else
269  path[c] = req_name[c];
270  }
271 
272  for (DWORD i = 0; i < header.nfiles; i++) {
273  if (!_stricmp(directory[i].name, path))
274  return i;
275  }
276  }
277 
278  return entry;
279 }
280 
281 // +--------------------------------------------------------------------+
282 
284 {
285  if (directory && i >= 0 && i < (int) header.nfiles) {
286  char* name = directory[i].name;
287 
288  FILE* f;
289  fopen_s(&f, name, "rb");
290 
291  if (f) {
292  fseek(f, 0, SEEK_END);
293  DWORD len = ftell(f);
294  fseek(f, 0, SEEK_SET);
295 
296  BYTE* buf = new(__FILE__,__LINE__) BYTE[len];
297 
298  if (!buf) {
299  err = Z_MEM_ERROR;
300  }
301 
302  else {
303  fread(buf, len, 1, f);
304  fclose(f);
305 
306  directory[i].size_orig = len;
307 
308  directory[i].size_comp = (int) (len * 1.1);
309  BYTE* cbuf = new(__FILE__,__LINE__) BYTE[directory[i].size_comp];
310 
311  if (!cbuf) {
312  err = Z_MEM_ERROR;
313  }
314  else {
315  err = compress(cbuf, &directory[i].size_comp, buf, len);
316  CHECK_ERR(err, "compress");
317  }
318 
319  delete [] buf;
320  return cbuf;
321  }
322  }
323  }
324 
325  return 0;
326 }
327 
328 // +--------------------------------------------------------------------+
329 
330 int DataArchive::ExpandEntry(int i, BYTE*& buf, bool null_terminate)
331 {
332  DWORD len = 0;
333 
334  if (directory && i >= 0 && i < (int) header.nfiles) {
335  FILE* f;
336  fopen_s(&f, datafile, "rb");
337 
338  if (f) {
339  DWORD clen = directory[i].size_comp;
340  BYTE* cbuf = new(__FILE__,__LINE__) BYTE[clen];
341 
342  if (!cbuf) {
343  err = Z_MEM_ERROR;
344  }
345 
346  else {
347  fseek(f, sizeof(DataHeader) + directory[i].offset, SEEK_SET);
348  fread(cbuf, clen, 1, f);
349 
350  len = directory[i].size_orig;
351 
352  if (null_terminate) {
353  buf = new(__FILE__,__LINE__) BYTE[len+1];
354  if (buf) buf[len] = 0;
355  }
356 
357  else {
358  buf = new(__FILE__,__LINE__) BYTE[len];
359  }
360 
361  if (!buf) {
362  err = Z_MEM_ERROR;
363  }
364 
365  else {
366  err = uncompress(buf, &len, cbuf, clen);
367  if (err != Z_OK) {
368  delete [] buf;
369  buf = 0;
370  }
371  }
372 
373  delete [] cbuf;
374  fclose(f);
375  }
376  }
377  }
378 
379  return len;
380 }
381 
382 // +--------------------------------------------------------------------+
383 
384 int DataArchive::InsertEntry(const char* name)
385 {
386  if (name && *name) {
387  char path[256];
388  DWORD len = strlen(name);
389 
390  ZeroMemory(path, sizeof(path));
391 
392  for (DWORD c = 0; c < len; c++) {
393  if (name[c] == '\\')
394  path[c] = '/';
395  else
396  path[c] = name[c];
397  }
398 
399  int dirsize = header.nfiles + 64;
400 
401  if (directory && dirsize) {
402  for (int i = 0; i < dirsize; i++) {
403  if (directory[i].size_orig == 0) {
404  ZeroMemory(directory[i].name, NAMELEN);
405  strncpy_s(directory[i].name, path, NAMELEN);
406  directory[i].name[NAMELEN-1] = '\0';
407  directory[i].size_orig = 1;
408 
409  return i;
410  }
411  }
412  }
413 
414  DataEntry* dir = new(__FILE__,__LINE__) DataEntry[dirsize + 64];
415 
416  if (directory && dirsize) {
417  ZeroMemory(dir, (dirsize + 64) * sizeof(DataEntry));
418  CopyMemory(dir, directory, dirsize * sizeof(DataEntry));
419  }
420 
421  delete [] directory;
422 
423  header.nfiles = dirsize + 64;
424  directory = dir;
425 
426  ZeroMemory(directory[dirsize].name, NAMELEN);
427  strncpy_s(directory[dirsize].name, path, NAMELEN);
428  directory[dirsize].name[NAMELEN-1] = '\0';
429  directory[dirsize].size_orig = 1;
430 
431  return dirsize;
432  }
433 
434  return -1;
435 }
436 
437 // +--------------------------------------------------------------------+
438 
440 {
441  if (directory && index >= 0 && index < (int) header.nfiles)
442  ZeroMemory(&directory[index], sizeof(DataEntry));
443 }
444 
445 // +--------------------------------------------------------------------+
446 
447 void DataArchive::Insert(const char* name)
448 {
449  DWORD old_blocks = 0, old_offset = 0, new_blocks = 0;
450  DWORD old_dir_blocks = 0, old_dir_offset = 0, new_dir_blocks = 0;
451  int added = 0;
452 
453  int index = FindEntry(name);
454 
455  if (index < 0) {
456  old_dir_blocks = DirBlocks();
457  old_dir_offset = header.dir_offset;
458 
459  index = InsertEntry(name);
460 
461  if (index >= (int) header.nfiles) {
462  header.nfiles = index+1;
463  added = 1;
464  }
465 
466  new_dir_blocks = DirBlocks();
467 
468  if (new_dir_blocks > old_dir_blocks) {
469  header.dir_offset = FindDataBlocks(new_dir_blocks);
470  CreateBlockMap();
471  }
472  }
473  else {
474  old_blocks = FileBlocks(index);
475  old_offset = directory[index].offset;
476  }
477 
478  if (index >= 0) {
479  DataEntry& e = directory[index];
480 
481  if (verbose) Print(" Inserting: %-16s ", e.name);
482 
483  BYTE* buf = CompressEntry(index);
484 
485  if (!buf) {
486  // this is (almost) unrecoverable,
487  // so we quit before screwing things up:
488  Print("ERROR: Could not compress %d:%s\n", index, directory[index].name);
489  exit(1);
490  }
491 
492  new_blocks = FileBlocks(index);
493 
494  // the file is new, or got bigger,
495  // need to find room for the data:
496  if (new_blocks > old_blocks) {
497  directory[index].offset = FindDataBlocks(new_blocks);
498  CreateBlockMap();
499  }
500 
501  WriteEntry(index, buf);
502  delete [] buf;
503 
504  if (verbose) {
505  int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
506  Print("%9d => %9d (%2d%%)\n", e.size_orig, e.size_comp, ratio);
507  }
508  }
509  else if (added)
510  header.nfiles--;
511 }
512 
513 // +--------------------------------------------------------------------+
514 
515 void DataArchive::Extract(const char* name)
516 {
517  int index = FindEntry(name);
518 
519  if (!directory || index < 0 || index >= (int) header.nfiles) {
520  Print("Could not extract '%s', not found\n", name);
521  return;
522  }
523 
524  BYTE* buf;
525  ExpandEntry(index, buf);
526 
527  FILE* f;
528  fopen_s(&f, directory[index].name, "wb");
529  if (f) {
530  fwrite(buf, directory[index].size_orig, 1, f);
531  fclose(f);
532  }
533  else
534  Print("Could not extract '%s', could not open file for writing\n", name);
535 
536  delete [] buf;
537 
538  if (verbose) Print(" Extracted: %s\n", name);
539 }
540 
541 // +--------------------------------------------------------------------+
542 
543 void DataArchive::Remove(const char* name)
544 {
545  int index = FindEntry(name);
546 
547  if (!directory || index < 0 || index >= (int) header.nfiles) {
548  Print("Could not remove '%s', not found\n", name);
549  return;
550  }
551 
552  RemoveEntry(index);
553  WriteEntry(index, 0);
554 
555  if (verbose) Print(" Removed: %s\n", name);
556 }
557 
558 // +--------------------------------------------------------------------+
559 
561 {
562  int total_orig = 0;
563  int total_comp = 0;
564 
565  printf("DATAFILE: %s\n", datafile);
566  printf("Files: %d\n", header.nfiles);
567  printf("\n");
568 
569  if (directory && header.nfiles) {
570  printf("Index Orig Size Comp Size Ratio Name\n");
571  printf("----- --------- --------- ----- ----------------\n");
572 
573  for (DWORD i = 0; i < header.nfiles; i++) {
574  DataEntry& e = directory[i];
575  int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
576 
577  printf("%5d %9d %9d %2d%% %s\n", i+1, e.size_orig, e.size_comp, ratio, e.name);
578 
579  total_orig += e.size_orig;
580  total_comp += e.size_comp;
581  }
582 
583  int total_ratio = (int) (100.0 * (double) total_comp / (double) total_orig);
584 
585  printf("----- --------- --------- -----\n");
586  printf("TOTAL %9d %9d %2d%%\n\n", total_orig, total_comp, total_ratio);
587  }
588 }
589 
590 
591 // +--------------------------------------------------------------------+
592