Subversion Repositories Kolibri OS

Rev

Rev 7867 | Rev 7869 | 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.  
  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;
  56.         const char *path;
  57.     } __attribute__((packed)) info;
  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;
  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);
  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) {
  350.     printf("Error in Fat12: %s\n", fat12->errorMessage);
  351.     return -1;
  352. }
  353.  
  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.  
  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.     }
  396.     printf("Extracting %s\n", outputPath->data);
  397.     writeFile(outputPath->data, size, data);
  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) {
  410.         puts(" Usage:\n");
  411.         puts(" unimg \"/path/to/kolibri.img\" \"/optional/extract/path\"\n");
  412.         return -1;
  413.     } else {
  414.         imageFile = argv[1];
  415.         printf("File: %s\n", imageFile);
  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]);
  424.     else {
  425.         strcpy(outputFolder.data, "/tmp0/1");
  426.         strcat(outputFolder.data, strrchr(imageFile, '/'));
  427.     }
  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.  
  439.     puts("\nDONE!\n");
  440. }
  441.