1/*
2 * QPAK support routines for PhysicsFS.
3 *
4 * This archiver handles the archive format utilized by Quake 1 and 2.
5 * Quake3-based games use the PkZip/Info-Zip format (which our
6 * physfs_archiver_zip.c handles).
7 *
8 * ========================================================================
9 *
10 * This format info (in more detail) comes from:
11 * https://web.archive.org/web/20040209101748/http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt
12 *
13 * Quake PAK Format
14 *
15 * Header
16 * (4 bytes) signature = 'PACK'
17 * (4 bytes) directory offset
18 * (4 bytes) directory length
19 *
20 * Directory
21 * (56 bytes) file name
22 * (4 bytes) file position
23 * (4 bytes) file length
24 *
25 * ========================================================================
26 *
27 * Please see the file LICENSE.txt in the source's root directory.
28 *
29 * This file written by Ryan C. Gordon.
30 */
31
32#define __PHYSICSFS_INTERNAL__
33#include "physfs_internal.h"
34
35#if PHYSFS_SUPPORTS_QPAK
36
37#define QPAK_SIG 0x4B434150 /* "PACK" in ASCII. */
38
39static int qpakLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
40{
41 PHYSFS_uint32 i;
42 for (i = 0; i < count; i++)
43 {
44 PHYSFS_uint32 size;
45 PHYSFS_uint32 pos;
46 char name[56];
47 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 56), 0);
48 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
49 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
50 size = PHYSFS_swapULE32(size);
51 pos = PHYSFS_swapULE32(pos);
52 BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
53 } /* for */
54
55 return 1;
56} /* qpakLoadEntries */
57
58
59static void *QPAK_openArchive(PHYSFS_Io *io, const char *name,
60 int forWriting, int *claimed)
61{
62 PHYSFS_uint32 val = 0;
63 PHYSFS_uint32 pos = 0;
64 PHYSFS_uint32 count = 0;
65 void *unpkarc;
66
67 assert(io != NULL); /* shouldn't ever happen. */
68
69 BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
70
71 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
72 if (PHYSFS_swapULE32(val) != QPAK_SIG)
73 BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
74
75 *claimed = 1;
76
77 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
78 pos = PHYSFS_swapULE32(val); /* directory table offset. */
79
80 BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
81 count = PHYSFS_swapULE32(val);
82
83 /* corrupted archive? */
84 BAIL_IF((count % 64) != 0, PHYSFS_ERR_CORRUPT, NULL);
85 count /= 64;
86
87 BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
88
89 /* !!! FIXME: check case_sensitive and only_usascii params for this archive. */
90 unpkarc = UNPK_openArchive(io, 1, 0);
91 BAIL_IF_ERRPASS(!unpkarc, NULL);
92
93 if (!qpakLoadEntries(io, count, unpkarc))
94 {
95 UNPK_abandonArchive(unpkarc);
96 return NULL;
97 } /* if */
98
99 return unpkarc;
100} /* QPAK_openArchive */
101
102
103const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
104{
105 CURRENT_PHYSFS_ARCHIVER_API_VERSION,
106 {
107 "PAK",
108 "Quake I/II format",
109 "Ryan C. Gordon <icculus@icculus.org>",
110 "https://icculus.org/physfs/",
111 0, /* supportsSymlinks */
112 },
113 QPAK_openArchive,
114 UNPK_enumerate,
115 UNPK_openRead,
116 UNPK_openWrite,
117 UNPK_openAppend,
118 UNPK_remove,
119 UNPK_mkdir,
120 UNPK_stat,
121 UNPK_closeArchive
122};
123
124#endif /* defined PHYSFS_SUPPORTS_QPAK */
125
126/* end of physfs_archiver_qpak.c ... */
127
128