Subversion Repositories Kolibri OS

Rev

Rev 7843 | Rev 7868 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
7843 Boppan 1
#include 
2
#include 
3
#include 
4
#include 
5
#include 
6
#include 
7
 
8
typedef struct {
9
    size_t length;
10
    size_t capacity;
11
    char *data;
12
} String;
13
 
14
typedef struct {
15
    char *image;
16
    int imageSize;
17
    const char *errorMessage;
18
    int bytesPerSector;
19
    int sectorsPerClaster;
20
    int reservedSectorCount;
21
    int numberOfFats;
22
    int maxRootEntries;
23
    int totalSectors;
24
    int sectorsPerFat;
25
    int firstFat;
26
    int rootDirectory;
27
    int dataRegion;
28
} Fat12;
29
 
30
typedef int (*ForEachCallback)(const char *, size_t, const uint8_t *, void *);
31
 
32
// system-dependent
33
static void mkdir(const char *name);
34
// misc
35
static void mkdir_p(const char *_name); // create folder creating its parents
36
static uint16_t get16(const void *_from, int index); // get uint16_t from array at offset
37
static uint32_t get32(const void *_from, int index); // get uint32_t from array at offset
38
// fat12
39
static int fat12__getItemNameSize(const void *_folderEntry);
40
static void fat12__getItemName(const void *_folderEntry, void *_name);
41
static int fat12__getNextClaster(const Fat12 *this, int currentClaster);
42
static int fat12__getFile(const Fat12 *this, void *_buffer, int size, int claster);
43
static int fat12__getOffsetByClaster(const Fat12 *this, int claster);
44
static int fat12__forEachFile_handleFolderEntry(const Fat12 *this, int folderEntryOffset, String *name,
45
                                                ForEachCallback callback, void *callbackParam);
46
static int fat12__forEachFile_handleFolder(const Fat12 *this, int claster, String *name,
47
                                           ForEachCallback callback, void *callbackParam);
48
static int fat12__forEachFile(const Fat12 *this, ForEachCallback callback, void *callbackParam);
49
static int fat12__open(Fat12 *this, const char *img);
50
static int fat12__error(Fat12 *this, const char *errorMessage);
51
 
52
static void mkdir(const char *name) {
53
    struct {
54
        int fn;
55
        int unused[4];
56
        char b;
57
        const char *path __attribute__((packed));
58
    } info;
59
    memset(&info, 0, sizeof(info));
60
    info.fn = 9;
61
    info.b = 0;
62
    info.path = name;
63
    asm volatile ("int $0x40"::"a"(70), "b"(&info));
64
}
65
 
66
static void mkdir_p(const char *_name) {
67
    char *name = calloc(strlen(_name) + 1, 1);
68
 
69
    strcpy(name, _name);
70
    char *ptr = name;
71
    while (ptr) {
72
        if (ptr != name) { *ptr = '/'; }
73
        ptr = strchr(ptr + 1, '/');
74
        if (ptr) { *ptr = 0; }
75
        mkdir(name);
76
    }
77
}
78
 
79
static uint32_t get32(const void *_from, int index) {
80
    const uint8_t *from = _from;
81
    return from[index] |
82
        (from[index + 1] << 8) |
83
        (from[index + 2] << 16) |
84
        (from[index + 3] << 24);
85
}
86
 
87
static uint16_t get16(const void *_from, int index) {
88
    const uint8_t *from = _from;
89
 
90
    return from[index] | (from[index + 1] << 8);
91
}
92
 
93
static int fat12__getNextClaster(const Fat12 *this, int currentClaster) {
94
    int nextClasterOffset = this->firstFat + currentClaster + (currentClaster >> 1);
95
 
96
    if (currentClaster % 2 == 0) {
97
        return get16(this->image, nextClasterOffset) & 0xfff;
98
    } else {
99
        return get16(this->image, nextClasterOffset) >> 4;
100
    }
101
}
102
 
103
static int fat12__getFile(const Fat12 *this, void *_buffer, int size, int claster) {
104
    int offset = 0;
105
    char *buffer = _buffer;
106
 
107
    while (claster < 0xff7) {
108
        int toCopy = this->bytesPerSector * this->sectorsPerClaster;
109
        void *clasterPtr = &this->image[fat12__getOffsetByClaster(this, claster)];
110
 
111
        claster = fat12__getNextClaster(this, claster);
112
        // if next claster is END OF FILE claster, copy only rest of file
113
        if (claster >= 0xff7) { toCopy = size % toCopy; }
114
        memcpy(&buffer[offset], clasterPtr, toCopy);
115
        offset += toCopy;
116
    }
117
    return 1;
118
}
119
 
120
static int fat12__getOffsetByClaster(const Fat12 *this, int claster) {
121
    return this->dataRegion + (claster - 2)
122
        * this->bytesPerSector * this->sectorsPerClaster;
123
}
124
 
125
static int fat12__getItemNameSize(const void *_folderEntry) {
126
    const uint8_t *folderEntry = _folderEntry;
127
 
128
    // Long File Name entry, not a file itself
129
    if ((folderEntry[11] & 0x0f) == 0x0f) { return 0; }
130
    if ((folderEntry[11 - 32] & 0x0f) != 0x0f) {
131
        // regular file "NAME8888" '.' "EXT" '\0'
132
        int length = 13;
133
 
134
        for (int i = 10; folderEntry[i] == ' ' && i != 7; i--) { length--; }
135
        for (int i = 7; folderEntry[i] == ' ' && i != 0 - 1; i--) { length--; }
136
        if (folderEntry[8] == ' ') { length--; } // no ext - no'.'
137
        return length;
138
    } else {
139
        // file with long name
140
        // format of Long File Name etries is described in fat12__getItemName
141
        int length = 1;
142
 
143
        for (int i = 1; i < 255 / 13; i++) {
144
            //! TODO: Add UTF-16 support
145
            length += 13;
146
            if (folderEntry[i * -32] & 0x40) {
147
                // if first char from back is 0xffff, this is stub after name
148
                // otherwice is last character, so we can return calculated length
149
                if (get16(folderEntry, i * -32 + 30) == 0xffff) { length--; } else { return length; }
150
                if (get16(folderEntry, i * -32 + 28) == 0xffff) { length--; } else { return length; }
151
                if (get16(folderEntry, i * -32 + 24) == 0xffff) { length--; } else { return length; }
152
                if (get16(folderEntry, i * -32 + 22) == 0xffff) { length--; } else { return length; }
153
                if (get16(folderEntry, i * -32 + 20) == 0xffff) { length--; } else { return length; }
154
                if (get16(folderEntry, i * -32 + 18) == 0xffff) { length--; } else { return length; }
155
                if (get16(folderEntry, i * -32 + 16) == 0xffff) { length--; } else { return length; }
156
                if (get16(folderEntry, i * -32 + 14) == 0xffff) { length--; } else { return length; }
157
                if (get16(folderEntry, i * -32 + 9) == 0xffff) { length--; } else { return length; }
158
                if (get16(folderEntry, i * -32 + 7) == 0xffff) { length--; } else { return length; }
159
                if (get16(folderEntry, i * -32 + 5) == 0xffff) { length--; } else { return length; }
160
                if (get16(folderEntry, i * -32 + 3) == 0xffff) { length--; } else { return length; }
161
                if (get16(folderEntry, i * -32 + 1) == 0xffff) { length--; } else { return length; }
162
                return length;
163
            }
164
        }
165
    }
166
    return 0; // WAT?
167
}
168
 
169
static void fat12__getItemName(const void *_folderEntry, void *_name) {
170
    const uint8_t *folderEntry = _folderEntry;
171
    uint8_t *name = _name;
172
 
173
    if ((folderEntry[11 - 32] & 0x0f) != 0x0f) {
174
        int length = 8;
175
 
176
        memset(name, 0, 13);
177
        memcpy(name, folderEntry, 8);
178
        while (name[length - 1] == ' ') { length--; }
179
        if (folderEntry[9] != ' ') {
180
            name[length++] = '.';
181
            memcpy(&name[length], &folderEntry[8], 3);
182
            length += 3;
183
        }
184
        while (name[length - 1] == ' ') { length--; }
185
        name[length] = '\0';
186
    } else {
187
        // previous folder entries hold long name in format:
188
        // 0 sequence nmber (in turn back to first Long File Name entry, from 1)
189
        // 1 - 10 file name next characters in utf-16
190
        // 11 file attributes (0x0f - LFN entry)
191
        // 12 reserved
192
        // 13 checksum
193
        // 14 - 25 file name next characters
194
        // 26 - 27 reserved
195
        // 28 - 31 file name next characters
196
        // in these entries name placed in sequential order
197
        // but first characters are located in first previous entry
198
        // next characters - in next previous etc.
199
        // if current entry is orificated by 0x40 - the entry is last (cinains last characters)
200
        // unneed places for characters in the last entry are filled by 0xff
201
        int length = 0;
202
 
203
        for (int i = 1; i < 255 / 13; i++) {
204
            //! TODO: Add unicode support
205
            name[length++] = folderEntry[i * -32 + 1];
206
            name[length++] = folderEntry[i * -32 + 3];
207
            name[length++] = folderEntry[i * -32 + 5];
208
            name[length++] = folderEntry[i * -32 + 7];
209
            name[length++] = folderEntry[i * -32 + 9];
210
            name[length++] = folderEntry[i * -32 + 14];
211
            name[length++] = folderEntry[i * -32 + 16];
212
            name[length++] = folderEntry[i * -32 + 18];
213
            name[length++] = folderEntry[i * -32 + 20];
214
            name[length++] = folderEntry[i * -32 + 22];
215
            name[length++] = folderEntry[i * -32 + 24];
216
            name[length++] = folderEntry[i * -32 + 28];
217
            name[length++] = folderEntry[i * -32 + 30];
218
            if (folderEntry[i * -32] & 0x40) {
219
                while (name[length - 1] == 0xff) { name[--length] = 0; }
220
                name[length++] = 0;
221
                return;
222
            }
223
        }
224
    }
225
}
226
 
227
 
228
static int fat12__forEachFile_handleFolderEntry(const Fat12 *this, int folderEntryOffset, String *name,
229
                                                ForEachCallback callback, void *callbackParam) {
230
    int nameSize = 0;
231
 
232
    if (this->image[folderEntryOffset] == 0) { return 1; } // zero-entry, not file nor folder
233
    nameSize = fat12__getItemNameSize(&this->image[folderEntryOffset]); // includes sizeof '\0'
234
    if (nameSize != 0) {
235
        while (name->capacity < name->length + nameSize + 1) {
236
            name->capacity += name->capacity / 2;
237
            name->data = realloc(name->data, name->capacity);
238
        }
239
        name->data[name->length++] = '/';
240
        fat12__getItemName(&this->image[folderEntryOffset], &name->data[name->length]);
241
        name->length += nameSize - 1;
242
        if ((this->image[folderEntryOffset + 11] & 0x10)) {
243
            // the item is folder
244
            // handle folder only if it isn't current folder or parent one
245
            if (memcmp(&this->image[folderEntryOffset], ".          ", 11) &&
246
                memcmp(&this->image[folderEntryOffset], "..         ", 11)) {
247
                if (!fat12__forEachFile_handleFolder(this, get16(this->image, folderEntryOffset + 26), name, callback, callbackParam)) {
248
                    return 0;
249
                }
250
            }
251
        } else {
252
            // the item is a regular file
253
            void *buffer = NULL;
254
            int size = get32(this->image, folderEntryOffset + 28);
255
            int cluster = get16(this->image, folderEntryOffset + 26);
256
 
257
            buffer = malloc(size);
258
            if (!fat12__getFile(this, buffer, size, cluster)) {
259
                free(buffer);
260
                return 0;
261
            }
262
            callback(name->data, size, buffer, callbackParam);
263
            free(buffer);
264
        }
265
        name->length -= nameSize - 1; // substract length of current item name
266
        name->length--; // substract length of '/'
267
        name->data[name->length] = '\0';
268
    }
269
    return 1;
270
}
271
 
272
static int fat12__forEachFile_handleFolder(const Fat12 *this, int claster, String *name,
273
                                           ForEachCallback callback, void *callbackParam) {
274
    for (; claster < 0xff7; claster = fat12__getNextClaster(this, claster)) {
275
        int offset = fat12__getOffsetByClaster(this, claster);
276
 
277
        for (int i = 0; i < (this->bytesPerSector * this->sectorsPerClaster / 32); i++) {
278
            if (!fat12__forEachFile_handleFolderEntry(this, offset + 32 * i, name, callback, callbackParam)) {
279
                return 0;
280
            }
281
        }
282
    }
283
    return 1;
284
}
285
 
286
static int fat12__forEachFile(const Fat12 *this, ForEachCallback callback, void *callbackParam) {
287
    String name = { 0 };
288
 
289
    name.capacity = 4096;
290
    name.data = malloc(name.capacity);
291
    name.length = 0;
292
    name.data[0] = '\0';
293
 
294
    for (int i = 0; i < this->maxRootEntries; i++) {
295
        if (!fat12__forEachFile_handleFolderEntry(this, this->rootDirectory + 32 * i, &name, callback, callbackParam)) {
296
            free(name.data);
297
            return 0;
298
        }
299
    }
300
    free(name.data);
301
    return 1;
302
}
303
 
304
static int fat12__open(Fat12 *this, const char *img) {
305
    FILE *fp = NULL;
306
 
307
    if (!(fp = fopen(img, "rb"))) {
308
        return fat12__error(this, "Can't open imput file");
309
    }
310
    fseek(fp, 0, SEEK_END);
311
    this->imageSize = ftell(fp);
312
    rewind(fp);
313
    if (!(this->image = malloc(this->imageSize))) {
314
        return fat12__error(this, "Can't allocate memory for image");
315
    }
316
    fread(this->image, 1, this->imageSize, fp);
317
    fclose(fp);
318
    this->bytesPerSector = *(uint16_t *)((uintptr_t)this->image + 11);
319
    this->sectorsPerClaster = *(uint8_t *)((uintptr_t)this->image + 0x0d);
320
    this->reservedSectorCount = *(uint16_t *)((uintptr_t)this->image + 0x0e);
321
    this->numberOfFats = *(uint8_t *)((uintptr_t)this->image + 0x10);
322
    this->maxRootEntries = *(uint16_t *)((uintptr_t)this->image + 0x11);
323
    this->totalSectors = *(uint16_t *)((uintptr_t)this->image + 0x13);
324
    if (!this->totalSectors) {
325
        this->totalSectors = *(uint32_t *)((uintptr_t)this->image + 0x20);
326
    }
327
    this->sectorsPerFat = *(uint16_t *)((uintptr_t)this->image + 0x16);
328
    this->firstFat = (0 + this->reservedSectorCount) * this->bytesPerSector;
329
    this->rootDirectory = this->firstFat + this->numberOfFats
330
        * this->sectorsPerFat * this->bytesPerSector;
331
    this->dataRegion = this->rootDirectory + this->maxRootEntries * 32;
7867 leency 332
    con_printf("\nBytes per sector: %d\n",      this->bytesPerSector);
7843 Boppan 333
    con_printf("Sectors per claster: %d\n",   this->sectorsPerClaster);
334
    con_printf("Reserver sector count: %d\n", this->reservedSectorCount);
335
    con_printf("Number of FATs: %d\n",        this->numberOfFats);
336
    con_printf("Max root entries: %d\n",      this->maxRootEntries);
337
    con_printf("Total sectors: %d\n",         this->totalSectors);
338
    con_printf("Sectors per FAT: %d\n",       this->sectorsPerFat);
339
    con_printf("First FAT: %d\n",             this->firstFat);
340
    con_printf("Root directory: %d\n",        this->rootDirectory);
7867 leency 341
    con_printf("Data region: %d\n\n",           this->dataRegion);
7843 Boppan 342
    return 1;
343
}
344
 
345
static int fat12__error(Fat12 *this, const char *errorMessage) {
346
    this->errorMessage = errorMessage;
347
    return 0;
348
}
349
 
350
static int handleError(const Fat12 *fat12) {
351
    con_printf("Error in Fat12: %s\n", fat12->errorMessage);
352
    con_exit(0);
353
    return -1;
354
}
355
 
356
static int callback(const char *name, size_t size, const uint8_t *data, void *param) {
357
    FILE *fp = NULL;
358
    String *outputPath = param;
359
 
360
    while (outputPath->capacity < outputPath->length + strlen(name) + 1 + 1) {
361
        outputPath->capacity += outputPath->capacity / 2;
362
        outputPath->data = realloc(outputPath->data, outputPath->capacity);
363
    }
364
    strcat(outputPath->data, name);
365
    { // don't let mkdir_p create folder where file should be located
366
        char *fileNameDelim = NULL;
367
 
368
        // no slash = no folders to create, outputPath->data contains only file name
369
        // yes, I know, outputPath->data always contains '/', but who knows...
370
        if ((fileNameDelim = strrchr(outputPath->data, '/'))) {
371
            *fileNameDelim = '\0';
372
            mkdir_p(outputPath->data);
373
            *fileNameDelim = '/';
374
        }
375
    }
7867 leency 376
    con_printf("Extracting %s\n", outputPath->data);
7843 Boppan 377
    if (!(fp = fopen(outputPath->data, "wb"))) { perror(NULL); }
378
    fwrite(data, 1, size, fp);
379
    fclose(fp);
380
    outputPath->data[outputPath->length] = '\0';
381
    return 0;
382
}
383
 
384
 
385
 
386
int main(int argc, char **argv) {
387
    Fat12 fat12 = { 0 };
388
    char *imageFile = NULL;
389
    String outputFolder = { 0 };
390
    int exit = 0;
391
 
7867 leency 392
	char app_title[] = "UnImg - kolibri.img file unpacker";
393
	if (con_init_console_dll_param(-1, -1, -1, 350, app_title)) return -1;
7843 Boppan 394
 
395
    if (argc < 2) {
7867 leency 396
        con_write_asciiz(" Usage:\n");
397
        con_write_asciiz(" unimg \"/path/to/kolibri.img\" \"/optional/extract/path\" [-e]\n");
398
        con_write_asciiz("        where optional key [-e] is exit on success");
7843 Boppan 399
        con_exit(0);
400
        return -1;
7867 leency 401
    } else {
402
    	imageFile = argv[1];
403
    	con_printf("File: %s\n", imageFile);
7843 Boppan 404
    }
405
 
406
 
407
    outputFolder.capacity = 4096;
408
    outputFolder.data = malloc(outputFolder.capacity);
409
 
410
    //! ACHTUNG: possible buffer overflow, is 4096 enough in KolibriOS?
411
    if (argc >= 3 && argv[2][0] != '-') strcpy(outputFolder.data, argv[2]);
7867 leency 412
    else {
413
    	strcpy(outputFolder.data, "/tmp0/1");
414
    	strcat(outputFolder.data, strrchr(imageFile, '/'));
415
    }
7843 Boppan 416
 
417
    outputFolder.length = strlen(outputFolder.data);
418
 
419
    // handle -e parameter - exit on success
420
    if (argc >= 3 && !strcmp(argv[argc - 1], "-e")) { exit = 1; }
421
 
422
    if (!fat12__open(&fat12, imageFile)) {
423
        return handleError(&fat12);
424
    }
425
 
426
    if (!fat12__forEachFile(&fat12, callback, &outputFolder)) {
427
        return handleError(&fat12);
428
    }
429
 
430
    con_write_asciiz("\nDONE!\n\n");
431
    con_exit(exit);
432
}