Remove dependency on local state in step()
[tennix.git] / archive.c
blob0a24cabc97d118810f5b7b5108ec387a75f19fc6
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 <libgen.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
35 #include <arpa/inet.h>
37 #include "archive.h"
39 #ifdef TENNIXAR_STANDALONE
40 int main(int argc, char* argv[])
42 TennixArchive *tnxar;
43 char *data;
44 FILE *fp;
45 char *filename;
46 char *bn = (char*)basename(argv[0]);
47 int len, i;
48 struct stat st;
50 if(strcmp(bn, "archive") == 0) {
51 if (argc < 2) {
52 fprintf(stderr, "Usage: %s archive.tnx file1 [....]\n", bn);
53 exit(EXIT_FAILURE);
54 } else if (argc == 2) {
55 fprintf(stderr, "Refusing to create an empty archive.\n");
56 exit(EXIT_FAILURE);
59 if (stat(argv[1], &st) != -1) {
60 fprintf(stderr, "File %s already exists. Aborting.\n", argv[1]);
61 exit(EXIT_FAILURE);
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);
69 len = ftell(fp);
70 fseek(fp, 0, SEEK_SET);
71 data = (char*)malloc(len);
72 assert(fread(data, len, 1, fp) == 1);
73 fclose(fp);
74 tnxar_append(tnxar, (char*)basename(argv[i]), data, len);
76 tnxar_build(tnxar, argv[1]);
77 } else if(strcmp(bn, "dump") == 0) {
78 if (argc < 2) {
79 fprintf(stderr, "Usage: %s archive.tnx\n", bn);
80 exit(EXIT_FAILURE);
82 tnxar = tnxar_open(argv[1]);
83 assert(tnxar != NULL);
84 tnxar_dump(tnxar);
85 tnxar_close(tnxar);
86 } else if(strcmp(bn, "extract") == 0) {
87 if (argc < 2 || argc > 3) {
88 fprintf(stderr, "Usage: %s archive.tnx [file]\n", bn);
89 exit(EXIT_FAILURE);
91 tnxar = tnxar_open(argv[1]);
92 assert(tnxar != NULL);
93 if (argc == 2) {
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");
101 fputc('.', stderr);
102 assert(fwrite(data, len, 1, fp) == 1);
103 fputc('.', stderr);
104 fclose(fp);
105 fprintf(stderr, ".OK\n");
106 free(data);
107 tnxar_next(tnxar);
109 } else if (argc == 3) {
110 filename = argv[2];
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");
117 fputc('.', stderr);
118 assert(fwrite(data, len, 1, fp) == 1);
119 fputc('.', stderr);
120 fclose(fp);
121 fprintf(stderr, ".OK\n");
122 free(data);
123 } else {
124 fprintf(stderr, "File not found in %s: %s\n", argv[1], filename);
125 tnxar_close(tnxar);
126 exit(EXIT_FAILURE);
129 tnxar_close(tnxar);
132 return EXIT_SUCCESS;
134 #endif
136 TennixArchive* tnxar_open(const char* filename)
138 int i;
140 TennixArchive *tnxar = (TennixArchive*)malloc(sizeof(TennixArchive));
141 assert(tnxar != NULL);
143 tnxar->fp = fopen(filename, "rb");
144 if (tnxar->fp == NULL) {
145 free(tnxar);
146 return 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;
170 return tnxar;
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);
190 #endif
192 int tnxar_set_current_filename(TennixArchive* tnxar, const char* filename)
194 int i;
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;
199 return 1;
203 return 0;
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 */
216 data[size]='\0';
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);
220 return data;
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);
242 fclose(tnxar->fp);
243 tnxar->fp = NULL;
245 free(tnxar->items);
246 tnxar->items = NULL;
248 free(tnxar);
251 void tnxar_xormem(char* mem, uint32_t length, char key)
253 char *i = mem, *end = mem+length;
255 for(; i != end; i++) {
256 *i ^= key;
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;
269 return tnxar;
272 void tnxar_append(TennixArchive* tnxar, char* filename, char* mem, uint32_t length)
274 int i;
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)
294 int i;
295 size_t offset = 0;
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, ", ");
324 fputc('\n', stderr);
326 fprintf(stderr, "Writing: %s", filename);
327 fputc('.', stderr);
328 assert(fwrite(&(tnxar->header), sizeof(TennixArchiveHeader), 1, tnxar->fp) == 1);
329 fputc('.', stderr);
330 assert(fwrite(tnxar->items, sizeof(TennixArchiveItem), tnxar->header.items, tnxar->fp) == tnxar->header.items);
331 fputc('.', stderr);
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]);
336 fputc('.', stderr);
337 fprintf(stderr, "OK\n");
339 free(memsize);
340 free(tnxar->blobs);
341 fclose(tnxar->fp);
343 #endif