Tennix 0.7.0 "Deine Eltern sind auf einem Tennixturnier"
[tennix.git] / archive.c
blobf42a1cb56d8d9307ea530c4d573cfce2d17b2652
2 /**
4 * Tennix Archive File Format
5 * Copyright (C) 2009 Thomas Perl <thp@thpinfo.com>
6 *
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,
20 * MA 02110-1301, USA.
22 **/
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
34 #include <arpa/inet.h>
36 #include "archive.h"
38 #ifdef TENNIXAR_STANDALONE
39 int main(int argc, char* argv[])
41 TennixArchive *tnxar;
42 char *data;
43 FILE *fp;
44 char *filename;
45 char *bn = (char*)basename(argv[0]);
46 int len, i;
47 struct stat st;
49 if(strcmp(bn, "archive") == 0) {
50 if (argc < 2) {
51 fprintf(stderr, "Usage: %s archive.tnx file1 [....]\n", bn);
52 exit(EXIT_FAILURE);
53 } else if (argc == 2) {
54 fprintf(stderr, "Refusing to create an empty archive.\n");
55 exit(EXIT_FAILURE);
58 if (stat(argv[1], &st) != -1) {
59 fprintf(stderr, "File %s already exists. Aborting.\n", argv[1]);
60 exit(EXIT_FAILURE);
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);
68 len = ftell(fp);
69 fseek(fp, 0, SEEK_SET);
70 data = (char*)malloc(len);
71 fread(data, len, 1, fp);
72 fclose(fp);
73 tnxar_append(tnxar, (char*)basename(argv[i]), data, len);
75 tnxar_build(tnxar, argv[1]);
76 } else if(strcmp(bn, "dump") == 0) {
77 if (argc < 2) {
78 fprintf(stderr, "Usage: %s archive.tnx\n", bn);
79 exit(EXIT_FAILURE);
81 tnxar = tnxar_open(argv[1]);
82 assert(tnxar != NULL);
83 tnxar_dump(tnxar);
84 tnxar_close(tnxar);
85 } else if(strcmp(bn, "extract") == 0) {
86 if (argc < 2 || argc > 3) {
87 fprintf(stderr, "Usage: %s archive.tnx [file]\n", bn);
88 exit(EXIT_FAILURE);
90 tnxar = tnxar_open(argv[1]);
91 assert(tnxar != NULL);
92 if (argc == 2) {
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");
100 fputc('.', stderr);
101 fwrite(data, len, 1, fp);
102 fputc('.', stderr);
103 fclose(fp);
104 fprintf(stderr, ".OK\n");
105 free(data);
106 tnxar_next(tnxar);
108 } else if (argc == 3) {
109 filename = argv[2];
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");
116 fputc('.', stderr);
117 fwrite(data, len, 1, fp);
118 fputc('.', stderr);
119 fclose(fp);
120 fprintf(stderr, ".OK\n");
121 free(data);
122 } else {
123 fprintf(stderr, "File not found in %s: %s\n", argv[1], filename);
124 tnxar_close(tnxar);
125 exit(EXIT_FAILURE);
128 tnxar_close(tnxar);
131 return EXIT_SUCCESS;
133 #endif
135 TennixArchive* tnxar_open(char* filename)
137 int i;
139 TennixArchive *tnxar = (TennixArchive*)malloc(sizeof(TennixArchive));
140 assert(tnxar != NULL);
142 tnxar->fp = fopen(filename, "rb");
143 if (tnxar->fp == NULL) {
144 free(tnxar);
145 return 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;
169 return tnxar;
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);
189 #endif
191 int tnxar_set_current_filename(TennixArchive* tnxar, char* filename)
193 int i;
195 for (i=0; i<tnxar->header.items; i++) {
196 if (strcmp(tnxar->items[i].filename, filename) == 0) {
197 tnxar->current_item = i;
198 return 1;
202 return 0;
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);
217 return data;
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);
239 fclose(tnxar->fp);
240 tnxar->fp = NULL;
242 free(tnxar->items);
243 tnxar->items = NULL;
245 free(tnxar);
248 void tnxar_xormem(char* mem, uint32_t length, char key)
250 char *i = mem, *end = mem+length;
252 for(; i != end; i++) {
253 *i ^= key;
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)
269 int i;
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)
289 int i;
290 size_t offset = 0;
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, ", ");
319 fputc('\n', stderr);
321 fprintf(stderr, "Writing: %s", filename);
322 fputc('.', stderr);
323 fwrite(&(tnxar->header), sizeof(TennixArchiveHeader), 1, tnxar->fp);
324 fputc('.', stderr);
325 fwrite(tnxar->items, sizeof(TennixArchiveItem), tnxar->header.items, tnxar->fp);
326 fputc('.', stderr);
327 for (i=0; i<tnxar->header.items; i++) {
328 fwrite(tnxar->blobs[i], memsize[i], 1, tnxar->fp);
329 free(tnxar->blobs[i]);
331 fputc('.', stderr);
332 fprintf(stderr, "OK\n");
334 free(memsize);
335 free(tnxar->blobs);
336 fclose(tnxar->fp);
338 #endif