Subversion Repositories Kolibri OS

Rev

Rev 7869 | Details | Compare with Previous | Last modification | View Log | RSS feed

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