4 * Tennix Archive File Format
5 * Copyright (C) 2009 Thomas Perl <thp@thpinfo.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program 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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
32 #include <sys/types.h>
35 #include <arpa/inet.h>
39 #ifdef TENNIXAR_STANDALONE
40 int main(int argc
, char* argv
[])
46 char *bn
= (char*)basename(argv
[0]);
50 if(strcmp(bn
, "archive") == 0) {
52 fprintf(stderr
, "Usage: %s archive.tnx file1 [....]\n", bn
);
54 } else if (argc
== 2) {
55 fprintf(stderr
, "Refusing to create an empty archive.\n");
59 if (stat(argv
[1], &st
) != -1) {
60 fprintf(stderr
, "File %s already exists. Aborting.\n", argv
[1]);
63 tnxar
= tnxar_create();
65 fprintf(stderr
, "Creating %s with %d files\n", argv
[1], argc
-2);
66 for (i
=2; i
<argc
; i
++) {
67 fp
= fopen(argv
[i
], "rb");
68 fseek(fp
, 0, SEEK_END
);
70 fseek(fp
, 0, SEEK_SET
);
71 data
= (char*)malloc(len
);
72 assert(fread(data
, len
, 1, fp
) == 1);
74 tnxar_append(tnxar
, (char*)basename(argv
[i
]), data
, len
);
76 tnxar_build(tnxar
, argv
[1]);
77 } else if(strcmp(bn
, "dump") == 0) {
79 fprintf(stderr
, "Usage: %s archive.tnx\n", bn
);
82 tnxar
= tnxar_open(argv
[1]);
83 assert(tnxar
!= NULL
);
86 } else if(strcmp(bn
, "extract") == 0) {
87 if (argc
< 2 || argc
> 3) {
88 fprintf(stderr
, "Usage: %s archive.tnx [file]\n", bn
);
91 tnxar
= tnxar_open(argv
[1]);
92 assert(tnxar
!= NULL
);
94 while (!tnxar_eof(tnxar
)) {
95 filename
= tnxar_get_current_filename(tnxar
);
96 fprintf(stderr
, "Extracting: %s", filename
);
97 data
= tnxar_read_current(tnxar
);
98 len
= tnxar_size_current(tnxar
);
99 fprintf(stderr
, " (%d bytes)", len
);
100 fp
= fopen(filename
, "wb");
102 assert(fwrite(data
, len
, 1, fp
) == 1);
105 fprintf(stderr
, ".OK\n");
109 } else if (argc
== 3) {
111 if (tnxar_set_current_filename(tnxar
, filename
) != 0) {
112 fprintf(stderr
, "Extracting: %s", filename
);
113 data
= tnxar_read_current(tnxar
);
114 len
= tnxar_size_current(tnxar
);
115 fprintf(stderr
, " (%d bytes)", len
);
116 fp
= fopen(filename
, "wb");
118 assert(fwrite(data
, len
, 1, fp
) == 1);
121 fprintf(stderr
, ".OK\n");
124 fprintf(stderr
, "File not found in %s: %s\n", argv
[1], filename
);
136 TennixArchive
* tnxar_open(const char* filename
)
140 TennixArchive
*tnxar
= (TennixArchive
*)malloc(sizeof(TennixArchive
));
141 assert(tnxar
!= NULL
);
143 tnxar
->fp
= fopen(filename
, "rb");
144 if (tnxar
->fp
== NULL
) {
149 tnxar
->offset
= sizeof(TennixArchiveHeader
)*fread(&(tnxar
->header
), sizeof(TennixArchiveHeader
), 1, tnxar
->fp
);
150 assert(tnxar
->offset
== sizeof(TennixArchiveHeader
));
151 assert(strncmp(tnxar
->header
.header
, TENNIX_ARCHIVE_HEADER
, TENNIX_ARCHIVE_HEADER_LEN
) == 0);
152 assert(tnxar
->header
.versionmajor
== TENNIX_ARCHIVE_VERSIONMAJOR
);
153 assert(tnxar
->header
.versionminor
== TENNIX_ARCHIVE_VERSIONMINOR
);
155 tnxar
->items
= (TennixArchiveItem
*)calloc(tnxar
->header
.items
, sizeof(TennixArchiveItem
));
156 assert(tnxar
->items
!= NULL
);
157 tnxar
->offset
+= sizeof(TennixArchiveItem
)*fread(tnxar
->items
, sizeof(TennixArchiveItem
), tnxar
->header
.items
, tnxar
->fp
);
158 assert(tnxar
->offset
== sizeof(TennixArchiveHeader
) + tnxar
->header
.items
*sizeof(TennixArchiveItem
));
160 tnxar_xormem((char*)(tnxar
->items
), tnxar
->header
.items
*sizeof(TennixArchiveItem
), tnxar
->header
.key
);
162 for (i
=0; i
<tnxar
->header
.items
; i
++) {
163 /* convert offset + length from network byte order */
164 tnxar
->items
[i
].offset
= ntohl(tnxar
->items
[i
].offset
);
165 tnxar
->items
[i
].length
= ntohl(tnxar
->items
[i
].length
);
168 tnxar
->current_item
= 0;
174 #ifdef TENNIXAR_STANDALONE
175 void tnxar_dump(TennixArchive
* tnxar
)
177 fprintf(stderr
, "Tennix Archive\n");
178 fprintf(stderr
, "Header: %s\n", tnxar
->header
.header
);
179 fprintf(stderr
, "Version: %d.%d\n", tnxar
->header
.versionmajor
, tnxar
->header
.versionminor
);
180 fprintf(stderr
, "Master key: %d\n", tnxar
->header
.key
);
181 fprintf(stderr
, "Items: %d\n", tnxar
->header
.items
);
182 for (tnxar
->current_item
= 0; tnxar
->current_item
< tnxar
->header
.items
; tnxar
->current_item
++) {
183 fprintf(stderr
, "===========\n");
184 fprintf(stderr
, "File: %s (#%d)\n", tnxar
->items
[tnxar
->current_item
].filename
, tnxar
->current_item
);
185 fprintf(stderr
, "Size: %d\n", tnxar
->items
[tnxar
->current_item
].length
);
186 fprintf(stderr
, "Offset: %d\n", tnxar
->items
[tnxar
->current_item
].offset
);
187 fprintf(stderr
, "Key: %d\n", tnxar
->items
[tnxar
->current_item
].key
);
192 int tnxar_set_current_filename(TennixArchive
* tnxar
, const char* filename
)
196 for (i
=0; i
<tnxar
->header
.items
; i
++) {
197 if (strncmp(tnxar
->items
[i
].filename
, filename
, TENNIX_ARCHIVE_ITEM_MAXNAME
) == 0) {
198 tnxar
->current_item
= i
;
206 char* tnxar_get_current_filename(TennixArchive
* tnxar
)
208 return tnxar
->items
[tnxar
->current_item
].filename
;
211 char* tnxar_read_current(TennixArchive
* tnxar
)
213 int size
= tnxar_size_current(tnxar
);
214 char* data
= (char*)malloc(size
+1);
215 /* the last char is a null character, so this works for strings, too */
217 fseek(tnxar
->fp
, tnxar
->items
[tnxar
->current_item
].offset
, SEEK_SET
);
218 assert(fread(data
, size
, 1, tnxar
->fp
) == 1);
219 tnxar_xormem(data
, size
, tnxar
->items
[tnxar
->current_item
].key
);
223 size_t tnxar_size_current(TennixArchive
* tnxar
)
225 return tnxar
->items
[tnxar
->current_item
].length
;
228 int tnxar_eof(TennixArchive
* tnxar
)
230 return tnxar
->current_item
>= tnxar
->header
.items
;
233 void tnxar_next(TennixArchive
* tnxar
)
235 tnxar
->current_item
++;
238 void tnxar_close(TennixArchive
*tnxar
)
240 assert(tnxar
!= NULL
);
251 void tnxar_xormem(char* mem
, uint32_t length
, char key
)
253 char *i
= mem
, *end
= mem
+length
;
255 for(; i
!= end
; i
++) {
260 #ifdef TENNIXAR_STANDALONE
261 TennixArchive
* tnxar_create()
263 TennixArchive
*tnxar
= (TennixArchive
*)calloc(sizeof(TennixArchive
), 1);
264 assert(tnxar
!= NULL
);
266 strcpy(tnxar
->header
.header
, TENNIX_ARCHIVE_HEADER
);
267 tnxar
->header
.items
= 0;
272 void tnxar_append(TennixArchive
* tnxar
, char* filename
, char* mem
, uint32_t length
)
275 TennixArchiveItem
*item
;
277 assert(tnxar
!= NULL
);
279 tnxar
->header
.items
++;
280 tnxar
->items
= (TennixArchiveItem
*)realloc(tnxar
->items
, sizeof(TennixArchiveItem
)*tnxar
->header
.items
);
281 tnxar
->blobs
= (char**)realloc(tnxar
->blobs
, sizeof(char*)*tnxar
->header
.items
);
283 item
= &(tnxar
->items
[tnxar
->header
.items
-1]);
284 tnxar
->blobs
[tnxar
->header
.items
-1] = mem
;
285 for (i
=0; i
<TENNIX_ARCHIVE_ITEM_MAXNAME
; i
++) {
286 item
->filename
[i
] = mem
[(i
*2)%length
];
288 strcpy(item
->filename
, filename
);
289 item
->length
= length
;
292 void tnxar_build(TennixArchive
*tnxar
, char* filename
)
296 size_t *memsize
= NULL
;
297 assert(tnxar
!= NULL
);
299 memsize
= (size_t*)calloc(tnxar
->header
.items
, sizeof(size_t));
301 tnxar
->fp
= fopen(filename
, "wb");
303 offset
+= sizeof(TennixArchiveHeader
) + tnxar
->header
.items
*sizeof(TennixArchiveItem
);
305 tnxar
->header
.versionmajor
= TENNIX_ARCHIVE_VERSIONMAJOR
;
306 tnxar
->header
.versionminor
= TENNIX_ARCHIVE_VERSIONMINOR
;
308 tnxar
->header
.key
= (0xaa + 0x77*tnxar
->header
.items
*3) % 0xff;
310 fprintf(stderr
, "Packing: ");
311 for (i
=0; i
<tnxar
->header
.items
; i
++) {
312 fprintf(stderr
, "%s", tnxar
->items
[i
].filename
);
313 tnxar
->items
[i
].offset
= htonl(offset
); /* network byte order */
314 tnxar
->items
[i
].key
= 0xaa ^ ((i
<<2)%0x100);
315 tnxar_xormem(tnxar
->blobs
[i
], tnxar
->items
[i
].length
, tnxar
->items
[i
].key
);
316 memsize
[i
] = tnxar
->items
[i
].length
;
317 offset
+= tnxar
->items
[i
].length
;
318 tnxar
->items
[i
].length
= htonl(tnxar
->items
[i
].length
); /* network byte order */
319 tnxar_xormem((char*)(tnxar
->items
+ i
), sizeof(TennixArchiveItem
), tnxar
->header
.key
);
320 if (i
!= tnxar
->header
.items
-1) {
321 fprintf(stderr
, ", ");
326 fprintf(stderr
, "Writing: %s", filename
);
328 assert(fwrite(&(tnxar
->header
), sizeof(TennixArchiveHeader
), 1, tnxar
->fp
) == 1);
330 assert(fwrite(tnxar
->items
, sizeof(TennixArchiveItem
), tnxar
->header
.items
, tnxar
->fp
) == tnxar
->header
.items
);
332 for (i
=0; i
<tnxar
->header
.items
; i
++) {
333 assert(fwrite(tnxar
->blobs
[i
], memsize
[i
], 1, tnxar
->fp
) == 1);
334 free(tnxar
->blobs
[i
]);
337 fprintf(stderr
, "OK\n");