Subversion Repositories Kolibri OS

Rev

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

  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.  
  12. #include <stdarg.h>
  13. #include <stdio.h>
  14. #include <stdint.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17.  
  18. #define TCC 0
  19. #define GCC 1
  20. #include "compiller.h"
  21.  
  22. #ifdef GCC
  23. #define con_init con_init
  24. #endif
  25.  
  26. typedef struct {
  27.     size_t length;
  28.     size_t capacity;
  29.     char *data;
  30. } String;
  31.  
  32. typedef struct {
  33.     char *image;
  34.     int imageSize;
  35.     const char *errorMessage;
  36.     int bytesPerSector;
  37.     int sectorsPerClaster;
  38.     int reservedSectorCount;
  39.     int numberOfFats;
  40.     int maxRootEntries;
  41.     int totalSectors;
  42.     int sectorsPerFat;
  43.     int firstFat;
  44.     int rootDirectory;
  45.     int dataRegion;
  46. } Fat12;
  47.  
  48. typedef int (*ForEachCallback)(const char *, size_t, const uint8_t *, void *);
  49.  
  50. // system-dependent
  51. static void mkdir(const char *name);
  52. // misc
  53. static void mkdir_p(const char *_name); // create folder creating its parents
  54. static uint16_t get16(const void *_from, int index); // get uint16_t from array at offset
  55. static uint32_t get32(const void *_from, int index); // get uint32_t from array at offset
  56. // fat12
  57. static int fat12__getItemNameSize(const void *_folderEntry);
  58. static void fat12__getItemName(const void *_folderEntry, void *_name);
  59. static int fat12__getNextClaster(const Fat12 *this, int currentClaster);
  60. static int fat12__getFile(const Fat12 *this, void *_buffer, int size, int claster);
  61. static int fat12__getOffsetByClaster(const Fat12 *this, int claster);
  62. static int fat12__forEachFile_handleFolderEntry(const Fat12 *this, int folderEntryOffset, String *name,
  63.                                                 ForEachCallback callback, void *callbackParam);
  64. static int fat12__forEachFile_handleFolder(const Fat12 *this, int claster, String *name,
  65.                                            ForEachCallback callback, void *callbackParam);
  66. static int fat12__forEachFile(const Fat12 *this, ForEachCallback callback, void *callbackParam);
  67. static int fat12__open(Fat12 *this, const char *img);
  68. static int fat12__error(Fat12 *this, const char *errorMessage);
  69.  
  70. static void mkdir(const char *name) {
  71.     #ifdef TCC
  72.     struct {
  73.         int fn;
  74.         int unused[4];
  75.         char b;
  76.         const char *path __attribute__((packed));
  77.     } info;
  78.     #else
  79.     struct {
  80.         int fn;
  81.         int unused[4];
  82.         char b;
  83.         const char *path;
  84.     }  __attribute__((packed)) info;
  85.     #endif    
  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;
  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);
  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) {
  378.     printf("Error in Fat12: %s\n", fat12->errorMessage);
  379.     return -1;
  380. }
  381.  
  382. void writeFile(const char *fileName, int size, const uint8_t *data) {
  383. //    FILE *fp = NULL;
  384. //    if (!(fp = fopen(fileName, "wb"))) { perror(NULL); }
  385. //    fwrite(data, 1, size, fp);
  386. //    fclose(fp);
  387.     struct Info {
  388.         int number;
  389.         int reserved0;
  390.         int reserved1;
  391.         int dataSize;
  392.         const void *data;
  393.         char zero;
  394.         const char *name;
  395.     } __attribute__((packed)) *info = calloc(sizeof(struct Info), 1);
  396.    
  397.     info->number = 2; // create/overwrite file
  398.     info->dataSize = size;
  399.     info->data = data;
  400.     info->zero = 0;
  401.     info->name = fileName;
  402.     asm volatile ("int $0x40" :: "a"(70), "b"(info));
  403. }
  404.  
  405. static int callback(const char *name, size_t size, const uint8_t *data, void *param) {
  406.     String *outputPath = param;
  407.  
  408.     while (outputPath->capacity < outputPath->length + strlen(name) + 1 + 1) {
  409.         outputPath->capacity += outputPath->capacity / 2;
  410.         outputPath->data = realloc(outputPath->data, outputPath->capacity);
  411.     }
  412.     strcat(outputPath->data, name);
  413.     { // don't let mkdir_p create folder where file should be located
  414.         char *fileNameDelim = NULL;
  415.    
  416.         // no slash = no folders to create, outputPath->data contains only file name
  417.         // yes, I know, outputPath->data always contains '/', but who knows...
  418.         if ((fileNameDelim = strrchr(outputPath->data, '/'))) {
  419.             *fileNameDelim = '\0';
  420.             mkdir_p(outputPath->data);
  421.             *fileNameDelim = '/';
  422.         }
  423.     }
  424.     printf("Extracting %s\n", outputPath->data);
  425.     #ifdef TCC
  426.         FILE *fp = NULL;
  427.         if (!(fp = fopen(outputPath->data, "wb"))) { perror(NULL); }
  428.         fwrite(data, 1, size, fp);
  429.         fclose(fp);
  430.     #else
  431.         writeFile(outputPath->data, size, data);
  432.     #endif
  433.     outputPath->data[outputPath->length] = '\0';
  434.     return 0;
  435. }
  436.  
  437.  
  438.  
  439. int main(int argc, char* argv[]) {
  440.     Fat12 fat12 = { 0 };
  441.     char *imageFile = NULL;
  442.     String outputFolder = { 0 };
  443.     int exit_code = 0;
  444.  
  445.     char app_title[] = "UnImg - kolibri.img file unpacker";
  446.     con_init(-1, -1, -1, 350, app_title);
  447.  
  448.     if (argc < 2) {
  449.         puts(" Usage:");
  450.         puts(" unimg \"/path/to/kolibri.img\" \"/optional/extract/path\"");
  451.         puts("       where optional key [-e] is exit on success");
  452.         exit(exit_code);
  453.         return -1;
  454.     } else {
  455.         imageFile = argv[1];
  456.         printf("File: %s\n", imageFile);
  457.     }
  458.    
  459.     outputFolder.capacity = 4096;
  460.     outputFolder.data = malloc(outputFolder.capacity);
  461.  
  462.     //! ACHTUNG: possible buffer overflow, is 4096 enough in KolibriOS?
  463.     if (argc >= 3 && argv[2][0] != '-') strcpy(outputFolder.data, argv[2]);
  464.     else {
  465.         strcpy(outputFolder.data, "/tmp0/1");
  466.         strcat(outputFolder.data, strrchr(imageFile, '/'));
  467.     }
  468.  
  469.     outputFolder.length = strlen(outputFolder.data);
  470.  
  471.     // handle -e parameter - exit on success
  472.     if (argc >= 3 && !strcmp(argv[argc - 1], "-e")) { exit_code = 1; }
  473.  
  474.     if (!fat12__open(&fat12, imageFile)) {
  475.         return handleError(&fat12);
  476.     }
  477.  
  478.     if (!fat12__forEachFile(&fat12, callback, &outputFolder)) {
  479.         return handleError(&fat12);
  480.     }
  481.  
  482.     puts("\nDONE!");
  483.     exit(exit_code);
  484. }
  485.