Subversion Repositories Kolibri OS

Rev

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