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]);
84 } else if(strcmp(bn
, "extract") == 0) {
85 if (argc
< 2 || argc
> 3) {
86 fprintf(stderr
, "Usage: %s archive.tnx [file]\n", bn
);
89 tnxar
= tnxar_open(argv
[1]);
91 while (!tnxar_eof(tnxar
)) {
92 filename
= tnxar_get_current_filename(tnxar
);
93 fprintf(stderr
, "Extracting: %s", filename
);
94 data
= tnxar_read_current(tnxar
);
95 len
= tnxar_size_current(tnxar
);
96 fprintf(stderr
, " (%d bytes)", len
);
97 fp
= fopen(filename
, "wb");
99 fwrite(data
, len
, 1, fp
);
102 fprintf(stderr
, ".OK\n");
106 } else if (argc
== 3) {
108 if (tnxar_set_current_filename(tnxar
, filename
) != 0) {
109 fprintf(stderr
, "Extracting: %s", filename
);
110 data
= tnxar_read_current(tnxar
);
111 len
= tnxar_size_current(tnxar
);
112 fprintf(stderr
, " (%d bytes)", len
);
113 fp
= fopen(filename
, "wb");
115 fwrite(data
, len
, 1, fp
);
118 fprintf(stderr
, ".OK\n");
121 fprintf(stderr
, "File not found in %s: %s\n", argv
[1], filename
);
133 TennixArchive
* tnxar_open(char* filename
)
137 TennixArchive
*tnxar
= (TennixArchive
*)malloc(sizeof(TennixArchive
));
138 assert(tnxar
!= NULL
);
140 tnxar
->fp
= fopen(filename
, "rb");
141 assert(tnxar
->fp
!= NULL
);
143 tnxar
->offset
= sizeof(TennixArchiveHeader
)*fread(&(tnxar
->header
), sizeof(TennixArchiveHeader
), 1, tnxar
->fp
);
144 assert(tnxar
->offset
== sizeof(TennixArchiveHeader
));
145 assert(strcmp(tnxar
->header
.header
, TENNIX_ARCHIVE_HEADER
) == 0);
146 assert(tnxar
->header
.versionmajor
== TENNIX_ARCHIVE_VERSIONMAJOR
);
147 assert(tnxar
->header
.versionminor
== TENNIX_ARCHIVE_VERSIONMINOR
);
149 tnxar
->items
= (TennixArchiveItem
*)calloc(tnxar
->header
.items
, sizeof(TennixArchiveItem
));
150 assert(tnxar
->items
!= NULL
);
151 tnxar
->offset
+= sizeof(TennixArchiveItem
)*fread(tnxar
->items
, sizeof(TennixArchiveItem
), tnxar
->header
.items
, tnxar
->fp
);
152 assert(tnxar
->offset
== sizeof(TennixArchiveHeader
) + tnxar
->header
.items
*sizeof(TennixArchiveItem
));
154 tnxar_xormem((char*)(tnxar
->items
), tnxar
->header
.items
*sizeof(TennixArchiveItem
), tnxar
->header
.key
);
156 for (i
=0; i
<tnxar
->header
.items
; i
++) {
157 /* convert offset + length from network byte order */
158 tnxar
->items
[i
].offset
= ntohl(tnxar
->items
[i
].offset
);
159 tnxar
->items
[i
].length
= ntohl(tnxar
->items
[i
].length
);
162 tnxar
->current_item
= 0;
168 #ifdef TENNIXAR_STANDALONE
169 void tnxar_dump(TennixArchive
* tnxar
)
171 fprintf(stderr
, "Tennix Archive\n");
172 fprintf(stderr
, "Header: %s\n", tnxar
->header
.header
);
173 fprintf(stderr
, "Version: %d.%d\n", tnxar
->header
.versionmajor
, tnxar
->header
.versionminor
);
174 fprintf(stderr
, "Master key: %d\n", tnxar
->header
.key
);
175 fprintf(stderr
, "Items: %d\n", tnxar
->header
.items
);
176 for (tnxar
->current_item
= 0; tnxar
->current_item
< tnxar
->header
.items
; tnxar
->current_item
++) {
177 fprintf(stderr
, "===========\n");
178 fprintf(stderr
, "File: %s (#%d)\n", tnxar
->items
[tnxar
->current_item
].filename
, tnxar
->current_item
);
179 fprintf(stderr
, "Size: %d\n", tnxar
->items
[tnxar
->current_item
].length
);
180 fprintf(stderr
, "Offset: %d\n", tnxar
->items
[tnxar
->current_item
].offset
);
181 fprintf(stderr
, "Key: %d\n", tnxar
->items
[tnxar
->current_item
].key
);
186 int tnxar_set_current_filename(TennixArchive
* tnxar
, char* filename
)
190 for (i
=0; i
<tnxar
->header
.items
; i
++) {
191 if (strcmp(tnxar
->items
[i
].filename
, filename
) == 0) {
192 tnxar
->current_item
= i
;
200 char* tnxar_get_current_filename(TennixArchive
* tnxar
)
202 return tnxar
->items
[tnxar
->current_item
].filename
;
205 char* tnxar_read_current(TennixArchive
* tnxar
)
207 int size
= tnxar_size_current(tnxar
);
208 char* data
= (char*)malloc(size
);
209 fseek(tnxar
->fp
, tnxar
->items
[tnxar
->current_item
].offset
, SEEK_SET
);
210 assert(fread(data
, size
, 1, tnxar
->fp
) == 1);
211 tnxar_xormem(data
, size
, tnxar
->items
[tnxar
->current_item
].key
);
215 size_t tnxar_size_current(TennixArchive
* tnxar
)
217 return tnxar
->items
[tnxar
->current_item
].length
;
220 int tnxar_eof(TennixArchive
* tnxar
)
222 return tnxar
->current_item
>= tnxar
->header
.items
;
225 void tnxar_next(TennixArchive
* tnxar
)
227 tnxar
->current_item
++;
230 void tnxar_close(TennixArchive
*tnxar
)
232 assert(tnxar
!= NULL
);
243 void tnxar_xormem(char* mem
, uint32_t length
, char key
)
245 char *i
= mem
, *end
= mem
+length
;
247 for(; i
!= end
; i
++) {
252 #ifdef TENNIXAR_STANDALONE
253 TennixArchive
* tnxar_create()
255 TennixArchive
*tnxar
= (TennixArchive
*)calloc(sizeof(TennixArchive
), 1);
256 assert(tnxar
!= NULL
);
258 strcpy(tnxar
->header
.header
, TENNIX_ARCHIVE_HEADER
);
259 tnxar
->header
.items
= 0;
262 void tnxar_append(TennixArchive
* tnxar
, char* filename
, char* mem
, uint32_t length
)
265 TennixArchiveItem
*item
;
267 assert(tnxar
!= NULL
);
269 tnxar
->header
.items
++;
270 tnxar
->items
= (TennixArchiveItem
*)realloc(tnxar
->items
, sizeof(TennixArchiveItem
)*tnxar
->header
.items
);
271 tnxar
->blobs
= (char**)realloc(tnxar
->blobs
, sizeof(char*)*tnxar
->header
.items
);
273 item
= &(tnxar
->items
[tnxar
->header
.items
-1]);
274 tnxar
->blobs
[tnxar
->header
.items
-1] = mem
;
275 for (i
=0; i
<TENNIX_ARCHIVE_ITEM_MAXNAME
; i
++) {
276 item
->filename
[i
] = mem
[(i
*2)%length
];
278 strcpy(item
->filename
, filename
);
279 item
->length
= length
;
282 void tnxar_build(TennixArchive
*tnxar
, char* filename
)
286 size_t *memsize
= NULL
;
287 assert(tnxar
!= NULL
);
289 memsize
= (size_t*)calloc(tnxar
->header
.items
, sizeof(size_t));
291 tnxar
->fp
= fopen(filename
, "wb");
293 offset
+= sizeof(TennixArchiveHeader
) + tnxar
->header
.items
*sizeof(TennixArchiveItem
);
295 tnxar
->header
.versionmajor
= TENNIX_ARCHIVE_VERSIONMAJOR
;
296 tnxar
->header
.versionminor
= TENNIX_ARCHIVE_VERSIONMINOR
;
298 tnxar
->header
.key
= (0xaa + 0x77*tnxar
->header
.items
*3) % 0xff;
300 for (i
=0; i
<tnxar
->header
.items
; i
++) {
301 fprintf(stderr
, "Packing: %s", tnxar
->items
[i
].filename
);
302 tnxar
->items
[i
].offset
= htonl(offset
); /* network byte order */
303 tnxar
->items
[i
].key
= 0xaa ^ ((i
<<2)%0x100);
305 tnxar_xormem(tnxar
->blobs
[i
], tnxar
->items
[i
].length
, tnxar
->items
[i
].key
);
307 memsize
[i
] = tnxar
->items
[i
].length
;
308 offset
+= tnxar
->items
[i
].length
;
310 tnxar
->items
[i
].length
= htonl(tnxar
->items
[i
].length
); /* network byte order */
311 tnxar_xormem((char*)(tnxar
->items
+ i
), sizeof(TennixArchiveItem
), tnxar
->header
.key
);
313 fprintf(stderr
, "OK\n");
316 fprintf(stderr
, "Writing: %s", filename
);
318 fwrite(&(tnxar
->header
), sizeof(TennixArchiveHeader
), 1, tnxar
->fp
);
320 fwrite(tnxar
->items
, sizeof(TennixArchiveItem
), tnxar
->header
.items
, tnxar
->fp
);
322 for (i
=0; i
<tnxar
->header
.items
; i
++) {
324 fwrite(tnxar
->blobs
[i
], memsize
[i
], 1, tnxar
->fp
);
325 free(tnxar
->blobs
[i
]);
327 fprintf(stderr
, "OK\n");