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,
31 #include <sys/types.h>
34 #include <arpa/inet.h>
38 #ifdef TENNIXAR_STANDALONE
39 int main(int argc
, char* argv
[])
45 char *bn
= (char*)basename(argv
[0]);
49 if(strcmp(bn
, "archive") == 0) {
51 fprintf(stderr
, "Usage: %s archive.tnx file1 [....]\n", bn
);
53 } else if (argc
== 2) {
54 fprintf(stderr
, "Refusing to create an empty archive.\n");
58 if (stat(argv
[1], &st
) != -1) {
59 fprintf(stderr
, "File %s already exists. Aborting.\n", argv
[1]);
62 tnxar
= tnxar_create();
64 fprintf(stderr
, "Creating %s with %d files\n", argv
[1], argc
-2);
65 for (i
=2; i
<argc
; i
++) {
66 fp
= fopen(argv
[i
], "rb");
67 fseek(fp
, 0, SEEK_END
);
69 fseek(fp
, 0, SEEK_SET
);
70 data
= (char*)malloc(len
);
71 fread(data
, len
, 1, fp
);
73 tnxar_append(tnxar
, (char*)basename(argv
[i
]), data
, len
);
75 tnxar_build(tnxar
, argv
[1]);
76 } else if(strcmp(bn
, "dump") == 0) {
78 fprintf(stderr
, "Usage: %s archive.tnx\n", bn
);
81 tnxar
= tnxar_open(argv
[1]);
82 assert(tnxar
!= NULL
);
85 } else if(strcmp(bn
, "extract") == 0) {
86 if (argc
< 2 || argc
> 3) {
87 fprintf(stderr
, "Usage: %s archive.tnx [file]\n", bn
);
90 tnxar
= tnxar_open(argv
[1]);
91 assert(tnxar
!= NULL
);
93 while (!tnxar_eof(tnxar
)) {
94 filename
= tnxar_get_current_filename(tnxar
);
95 fprintf(stderr
, "Extracting: %s", filename
);
96 data
= tnxar_read_current(tnxar
);
97 len
= tnxar_size_current(tnxar
);
98 fprintf(stderr
, " (%d bytes)", len
);
99 fp
= fopen(filename
, "wb");
101 fwrite(data
, len
, 1, fp
);
104 fprintf(stderr
, ".OK\n");
108 } else if (argc
== 3) {
110 if (tnxar_set_current_filename(tnxar
, filename
) != 0) {
111 fprintf(stderr
, "Extracting: %s", filename
);
112 data
= tnxar_read_current(tnxar
);
113 len
= tnxar_size_current(tnxar
);
114 fprintf(stderr
, " (%d bytes)", len
);
115 fp
= fopen(filename
, "wb");
117 fwrite(data
, len
, 1, fp
);
120 fprintf(stderr
, ".OK\n");
123 fprintf(stderr
, "File not found in %s: %s\n", argv
[1], filename
);
135 TennixArchive
* tnxar_open(char* filename
)
139 TennixArchive
*tnxar
= (TennixArchive
*)malloc(sizeof(TennixArchive
));
140 assert(tnxar
!= NULL
);
142 tnxar
->fp
= fopen(filename
, "rb");
143 if (tnxar
->fp
== NULL
) {
148 tnxar
->offset
= sizeof(TennixArchiveHeader
)*fread(&(tnxar
->header
), sizeof(TennixArchiveHeader
), 1, tnxar
->fp
);
149 assert(tnxar
->offset
== sizeof(TennixArchiveHeader
));
150 assert(strcmp(tnxar
->header
.header
, TENNIX_ARCHIVE_HEADER
) == 0);
151 assert(tnxar
->header
.versionmajor
== TENNIX_ARCHIVE_VERSIONMAJOR
);
152 assert(tnxar
->header
.versionminor
== TENNIX_ARCHIVE_VERSIONMINOR
);
154 tnxar
->items
= (TennixArchiveItem
*)calloc(tnxar
->header
.items
, sizeof(TennixArchiveItem
));
155 assert(tnxar
->items
!= NULL
);
156 tnxar
->offset
+= sizeof(TennixArchiveItem
)*fread(tnxar
->items
, sizeof(TennixArchiveItem
), tnxar
->header
.items
, tnxar
->fp
);
157 assert(tnxar
->offset
== sizeof(TennixArchiveHeader
) + tnxar
->header
.items
*sizeof(TennixArchiveItem
));
159 tnxar_xormem((char*)(tnxar
->items
), tnxar
->header
.items
*sizeof(TennixArchiveItem
), tnxar
->header
.key
);
161 for (i
=0; i
<tnxar
->header
.items
; i
++) {
162 /* convert offset + length from network byte order */
163 tnxar
->items
[i
].offset
= ntohl(tnxar
->items
[i
].offset
);
164 tnxar
->items
[i
].length
= ntohl(tnxar
->items
[i
].length
);
167 tnxar
->current_item
= 0;
173 #ifdef TENNIXAR_STANDALONE
174 void tnxar_dump(TennixArchive
* tnxar
)
176 fprintf(stderr
, "Tennix Archive\n");
177 fprintf(stderr
, "Header: %s\n", tnxar
->header
.header
);
178 fprintf(stderr
, "Version: %d.%d\n", tnxar
->header
.versionmajor
, tnxar
->header
.versionminor
);
179 fprintf(stderr
, "Master key: %d\n", tnxar
->header
.key
);
180 fprintf(stderr
, "Items: %d\n", tnxar
->header
.items
);
181 for (tnxar
->current_item
= 0; tnxar
->current_item
< tnxar
->header
.items
; tnxar
->current_item
++) {
182 fprintf(stderr
, "===========\n");
183 fprintf(stderr
, "File: %s (#%d)\n", tnxar
->items
[tnxar
->current_item
].filename
, tnxar
->current_item
);
184 fprintf(stderr
, "Size: %d\n", tnxar
->items
[tnxar
->current_item
].length
);
185 fprintf(stderr
, "Offset: %d\n", tnxar
->items
[tnxar
->current_item
].offset
);
186 fprintf(stderr
, "Key: %d\n", tnxar
->items
[tnxar
->current_item
].key
);
191 int tnxar_set_current_filename(TennixArchive
* tnxar
, char* filename
)
195 for (i
=0; i
<tnxar
->header
.items
; i
++) {
196 if (strcmp(tnxar
->items
[i
].filename
, filename
) == 0) {
197 tnxar
->current_item
= i
;
205 char* tnxar_get_current_filename(TennixArchive
* tnxar
)
207 return tnxar
->items
[tnxar
->current_item
].filename
;
210 char* tnxar_read_current(TennixArchive
* tnxar
)
212 int size
= tnxar_size_current(tnxar
);
213 char* data
= (char*)malloc(size
);
214 fseek(tnxar
->fp
, tnxar
->items
[tnxar
->current_item
].offset
, SEEK_SET
);
215 assert(fread(data
, size
, 1, tnxar
->fp
) == 1);
216 tnxar_xormem(data
, size
, tnxar
->items
[tnxar
->current_item
].key
);
220 size_t tnxar_size_current(TennixArchive
* tnxar
)
222 return tnxar
->items
[tnxar
->current_item
].length
;
225 int tnxar_eof(TennixArchive
* tnxar
)
227 return tnxar
->current_item
>= tnxar
->header
.items
;
230 void tnxar_next(TennixArchive
* tnxar
)
232 tnxar
->current_item
++;
235 void tnxar_close(TennixArchive
*tnxar
)
237 assert(tnxar
!= NULL
);
248 void tnxar_xormem(char* mem
, uint32_t length
, char key
)
250 char *i
= mem
, *end
= mem
+length
;
252 for(; i
!= end
; i
++) {
257 #ifdef TENNIXAR_STANDALONE
258 TennixArchive
* tnxar_create()
260 TennixArchive
*tnxar
= (TennixArchive
*)calloc(sizeof(TennixArchive
), 1);
261 assert(tnxar
!= NULL
);
263 strcpy(tnxar
->header
.header
, TENNIX_ARCHIVE_HEADER
);
264 tnxar
->header
.items
= 0;
267 void tnxar_append(TennixArchive
* tnxar
, char* filename
, char* mem
, uint32_t length
)
270 TennixArchiveItem
*item
;
272 assert(tnxar
!= NULL
);
274 tnxar
->header
.items
++;
275 tnxar
->items
= (TennixArchiveItem
*)realloc(tnxar
->items
, sizeof(TennixArchiveItem
)*tnxar
->header
.items
);
276 tnxar
->blobs
= (char**)realloc(tnxar
->blobs
, sizeof(char*)*tnxar
->header
.items
);
278 item
= &(tnxar
->items
[tnxar
->header
.items
-1]);
279 tnxar
->blobs
[tnxar
->header
.items
-1] = mem
;
280 for (i
=0; i
<TENNIX_ARCHIVE_ITEM_MAXNAME
; i
++) {
281 item
->filename
[i
] = mem
[(i
*2)%length
];
283 strcpy(item
->filename
, filename
);
284 item
->length
= length
;
287 void tnxar_build(TennixArchive
*tnxar
, char* filename
)
291 size_t *memsize
= NULL
;
292 assert(tnxar
!= NULL
);
294 memsize
= (size_t*)calloc(tnxar
->header
.items
, sizeof(size_t));
296 tnxar
->fp
= fopen(filename
, "wb");
298 offset
+= sizeof(TennixArchiveHeader
) + tnxar
->header
.items
*sizeof(TennixArchiveItem
);
300 tnxar
->header
.versionmajor
= TENNIX_ARCHIVE_VERSIONMAJOR
;
301 tnxar
->header
.versionminor
= TENNIX_ARCHIVE_VERSIONMINOR
;
303 tnxar
->header
.key
= (0xaa + 0x77*tnxar
->header
.items
*3) % 0xff;
305 fprintf(stderr
, "Packing: ");
306 for (i
=0; i
<tnxar
->header
.items
; i
++) {
307 fprintf(stderr
, "%s", tnxar
->items
[i
].filename
);
308 tnxar
->items
[i
].offset
= htonl(offset
); /* network byte order */
309 tnxar
->items
[i
].key
= 0xaa ^ ((i
<<2)%0x100);
310 tnxar_xormem(tnxar
->blobs
[i
], tnxar
->items
[i
].length
, tnxar
->items
[i
].key
);
311 memsize
[i
] = tnxar
->items
[i
].length
;
312 offset
+= tnxar
->items
[i
].length
;
313 tnxar
->items
[i
].length
= htonl(tnxar
->items
[i
].length
); /* network byte order */
314 tnxar_xormem((char*)(tnxar
->items
+ i
), sizeof(TennixArchiveItem
), tnxar
->header
.key
);
315 if (i
!= tnxar
->header
.items
-1) {
316 fprintf(stderr
, ", ");
321 fprintf(stderr
, "Writing: %s", filename
);
323 fwrite(&(tnxar
->header
), sizeof(TennixArchiveHeader
), 1, tnxar
->fp
);
325 fwrite(tnxar
->items
, sizeof(TennixArchiveItem
), tnxar
->header
.items
, tnxar
->fp
);
327 for (i
=0; i
<tnxar
->header
.items
; i
++) {
328 fwrite(tnxar
->blobs
[i
], memsize
[i
], 1, tnxar
->fp
);
329 free(tnxar
->blobs
[i
]);
332 fprintf(stderr
, "OK\n");