Skip to content

Commit 4f9a80a

Browse files
author
Jon Daniel
committed
POSIX GNU tar read support
- supporting tared csm.bin of Chasm: The Rift Remastered at https://store.steampowered.com/app/2061230/Chasm_The_Rift/
1 parent 0cd18b8 commit 4f9a80a

File tree

6 files changed

+512
-0
lines changed

6 files changed

+512
-0
lines changed

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ set(PHYSFS_SRCS
9292
src/physfs_archiver_qpak.c
9393
src/physfs_archiver_wad.c
9494
src/physfs_archiver_csm.c
95+
src/physfs_archiver_tar.c
9596
src/physfs_archiver_zip.c
9697
src/physfs_archiver_slb.c
9798
src/physfs_archiver_iso9660.c
@@ -130,6 +131,11 @@ if(NOT PHYSFS_ARCHIVE_CSM)
130131
add_definitions(-DPHYSFS_SUPPORTS_CSM=0)
131132
endif()
132133

134+
option(PHYSFS_ARCHIVE_TAR "Enable POSIX TAR / Chasm: The Rift [Demo] Remastered csm.bin support" TRUE)
135+
if(NOT PHYSFS_ARCHIVE_TAR)
136+
add_definitions(-DPHYSFS_SUPPORTS_TAR=0)
137+
endif()
138+
133139
option(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE)
134140
if(NOT PHYSFS_ARCHIVE_HOG)
135141
add_definitions(-DPHYSFS_SUPPORTS_HOG=0)
@@ -325,6 +331,7 @@ message_bool_option("7zip support" PHYSFS_ARCHIVE_7Z)
325331
message_bool_option("GRP support" PHYSFS_ARCHIVE_GRP)
326332
message_bool_option("WAD support" PHYSFS_ARCHIVE_WAD)
327333
message_bool_option("CSM support" PHYSFS_ARCHIVE_CSM)
334+
message_bool_option("TAR support" PHYSFS_ARCHIVE_TAR)
328335
message_bool_option("HOG support" PHYSFS_ARCHIVE_HOG)
329336
message_bool_option("MVL support" PHYSFS_ARCHIVE_MVL)
330337
message_bool_option("QPAK support" PHYSFS_ARCHIVE_QPAK)

src/Makefile.os2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SRCS = physfs.c &
2626
physfs_archiver_slb.c &
2727
physfs_archiver_iso9660.c &
2828
physfs_archiver_csm.c &
29+
physfs_archiver_tar.c &
2930
physfs_archiver_vdf.c
3031

3132

src/physfs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,9 @@ static int initStaticArchivers(void)
11911191
#if PHYSFS_SUPPORTS_CSM
11921192
REGISTER_STATIC_ARCHIVER(CSM);
11931193
#endif
1194+
#if PHYSFS_SUPPORTS_TAR
1195+
REGISTER_STATIC_ARCHIVER(TAR);
1196+
#endif
11941197
#if PHYSFS_SUPPORTS_SLB
11951198
REGISTER_STATIC_ARCHIVER(SLB);
11961199
#endif

src/physfs_archiver_tar.c

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* tar support routines for PhysicsFS.
3+
*
4+
* Please see the file LICENSE.txt in the source's root directory.
5+
*
6+
* uses POSIX GNU tar header: https://www.gnu.org/software/tar
7+
*
8+
* based on code by Uli Köhler: https://techoverflow.net/2013/03/29/reading-tar-files-in-c/
9+
*/
10+
11+
#define __PHYSICSFS_INTERNAL__
12+
#include "physfs_internal.h"
13+
14+
#if PHYSFS_SUPPORTS_TAR
15+
#include "physfs_tar.h"
16+
17+
#define ASCII_TO_NUMBER(num) ((num)-48) //Converts an ascii digit to the corresponding number (assuming it is an ASCII digit)
18+
19+
static PHYSFS_uint64 TAR_decodeOctal(char* data, size_t size) {
20+
unsigned char* currentPtr = (unsigned char*) data + size;
21+
PHYSFS_uint64 sum = 0;
22+
PHYSFS_uint64 currentMultiplier = 1;
23+
unsigned char* checkPtr = currentPtr;
24+
25+
for (; checkPtr >= (unsigned char*) data; checkPtr--) {
26+
if ((*checkPtr) == 0 || (*checkPtr) == ' ') {
27+
currentPtr = checkPtr - 1;
28+
}
29+
}
30+
for (; currentPtr >= (unsigned char*) data; currentPtr--) {
31+
sum += ASCII_TO_NUMBER(*currentPtr) * currentMultiplier;
32+
currentMultiplier *= 8;
33+
}
34+
return sum;
35+
}
36+
37+
static bool TAR_magic(union block* block) {
38+
return (memcmp("ustar", block->header.magic, 5) == 0);
39+
}
40+
41+
static size_t TAR_fileSize(union block* block) {
42+
return TAR_decodeOctal(block->header.size, sizeof(block->header.size));
43+
}
44+
45+
static bool TAR_checksum(union block* block) {
46+
PHYSFS_sint64 unsigned_sum = 0;
47+
PHYSFS_sint64 signed_sum = 0;
48+
PHYSFS_uint64 reference_chksum = 0;
49+
char orig_chksum[8];
50+
int i = 0;
51+
52+
memcpy(orig_chksum, block->header.chksum, 8);
53+
memset(block->header.chksum, ' ', 8);
54+
55+
for(; i < BLOCKSIZE; i++) {
56+
unsigned_sum += ((unsigned char*) block->buffer)[i];
57+
signed_sum += ((signed char*) block->buffer)[i];
58+
}
59+
memcpy(block->header.chksum, orig_chksum, 8);
60+
reference_chksum = TAR_decodeOctal(orig_chksum, 12);
61+
return (reference_chksum == unsigned_sum || reference_chksum == signed_sum);
62+
}
63+
64+
static bool TAR_loadEntries(PHYSFS_Io *io, void *arc)
65+
{
66+
union block zero_block = { 0 };
67+
union block current_block = { 0 };
68+
bool long_name = false;
69+
const PHYSFS_Allocator* allocator = PHYSFS_getAllocator();
70+
char name[PATH_MAX] = { 0 };
71+
PHYSFS_uint64 size = 0;
72+
PHYSFS_uint64 pos = 0;
73+
PHYSFS_uint64 pad = 0;
74+
PHYSFS_uint64 count = 0;
75+
char* buf = NULL;
76+
77+
/* read header block until zero-only terminated block */
78+
for(; __PHYSFS_readAll(io, current_block.buffer, BLOCKSIZE); count++)
79+
{
80+
if((memcmp(current_block.buffer, zero_block.buffer, BLOCKSIZE) == 0))
81+
return true;
82+
83+
/* verify magic */
84+
if(!TAR_magic(&current_block))
85+
return false;
86+
87+
/* verify checksum */
88+
if(!TAR_checksum(&current_block))
89+
return false;
90+
91+
memset(name, '\0', PATH_MAX);
92+
/* support prefix */
93+
if(strlen(current_block.header.prefix) > 0)
94+
{
95+
strcpy(name, current_block.header.prefix);
96+
name[strlen(name)] = '/';
97+
}
98+
strcpy(&name[strlen(name)], current_block.header.name);
99+
100+
/* add file type entry */
101+
if (current_block.header.typeflag == REGTYPE || current_block.header.typeflag == 0) {
102+
/* support long file names */
103+
if(long_name) {
104+
strcpy(&name[0], current_block.header.name);
105+
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, current_block.buffer, BLOCKSIZE), 0);
106+
long_name = false;
107+
count++;
108+
}
109+
size = TAR_fileSize(&current_block);
110+
pos = (count + 1) * BLOCKSIZE;
111+
pad = (BLOCKSIZE - (size % BLOCKSIZE)) % BLOCKSIZE;
112+
buf = allocator->Malloc(size + pad);
113+
/* add entry to arc */
114+
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, size + pad), 0);
115+
count += ((size + pad) / BLOCKSIZE );
116+
allocator->Free(buf);
117+
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
118+
}
119+
/* add directory type entry */
120+
else if(current_block.header.typeflag == DIRTYPE)
121+
{
122+
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 1, -1, -1, 0, 0), 0);
123+
}
124+
/* long name mode */
125+
else if(current_block.header.typeflag == GNUTYPE_LONGNAME)
126+
{
127+
long_name = true;
128+
}
129+
else
130+
{
131+
// UNHANDLED
132+
}
133+
}
134+
return false;
135+
}
136+
137+
static void *TAR_openArchive(PHYSFS_Io *io, const char *name,
138+
int forWriting, int *claimed)
139+
{
140+
void *unpkarc = NULL;
141+
142+
assert(io != NULL); /* shouldn't ever happen. */
143+
144+
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
145+
146+
unpkarc = UNPK_openArchive(io, 0, 1);
147+
BAIL_IF_ERRPASS(!unpkarc, NULL);
148+
149+
if (!TAR_loadEntries(io, unpkarc))
150+
{
151+
UNPK_abandonArchive(unpkarc);
152+
return NULL;
153+
} /* if */
154+
155+
*claimed = 1;
156+
157+
return unpkarc;
158+
} /* TAR_openArchive */
159+
160+
161+
const PHYSFS_Archiver __PHYSFS_Archiver_TAR =
162+
{
163+
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
164+
{
165+
"TAR",
166+
"POSIX GNU tar archives / Chasm: the Rift Remastered",
167+
"Jon Daniel <jondaniel879@gmail.com>",
168+
"https://www.gnu.org/software/tar",
169+
0,
170+
},
171+
TAR_openArchive,
172+
UNPK_enumerate,
173+
UNPK_openRead,
174+
UNPK_openWrite,
175+
UNPK_openAppend,
176+
UNPK_remove,
177+
UNPK_mkdir,
178+
UNPK_stat,
179+
UNPK_closeArchive
180+
};
181+
182+
#endif /* defined PHYSFS_SUPPORTS_TAR */
183+
184+
/* end of physfs_archiver_tar.c ... */
185+

src/physfs_internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
8989
extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
9090
extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
9191
extern const PHYSFS_Archiver __PHYSFS_Archiver_CSM;
92+
extern const PHYSFS_Archiver __PHYSFS_Archiver_TAR;
9293
extern const PHYSFS_Archiver __PHYSFS_Archiver_SLB;
9394
extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
9495
extern const PHYSFS_Archiver __PHYSFS_Archiver_VDF;
@@ -204,6 +205,9 @@ void __PHYSFS_smallFree(void *ptr);
204205
#ifndef PHYSFS_SUPPORTS_CSM
205206
#define PHYSFS_SUPPORTS_CSM PHYSFS_SUPPORTS_DEFAULT
206207
#endif
208+
#ifndef PHYSFS_SUPPORTS_TAR
209+
#define PHYSFS_SUPPORTS_TAR PHYSFS_SUPPORTS_DEFAULT
210+
#endif
207211
#ifndef PHYSFS_SUPPORTS_QPAK
208212
#define PHYSFS_SUPPORTS_QPAK PHYSFS_SUPPORTS_DEFAULT
209213
#endif

0 commit comments

Comments
 (0)