Subversion Repositories Kolibri OS

Rev

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

  1. #include <stdarg.h>
  2. #include <stdio.h>
  3. #include <stdint.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <conio.h>
  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;
  332.     con_printf("\nBytes per sector: %d\n",      this->bytesPerSector);
  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);
  341.     con_printf("Data region: %d\n\n",           this->dataRegion);
  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.     }
  376.     con_printf("Extracting %s\n", outputPath->data);
  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.  
  392.         char app_title[] = "UnImg - kolibri.img file unpacker";
  393.         if (con_init_console_dll_param(-1, -1, -1, 350, app_title)) return -1;
  394.  
  395.     if (argc < 2) {
  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");
  399.         con_exit(0);
  400.         return -1;
  401.     } else {
  402.         imageFile = argv[1];
  403.         con_printf("File: %s\n", imageFile);
  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]);
  412.     else {
  413.         strcpy(outputFolder.data, "/tmp0/1");
  414.         strcat(outputFolder.data, strrchr(imageFile, '/'));
  415.     }
  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. }
  433.