libppf.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*****************************************************************************
  2. libppf v0.1-rc1 - A library for handling PPF patch files
  3. Copyright (C), Daniel Ekström <dv01dem@cs.umu.se>, 2007 - 2008
  4. More information can be found at http://oakstream.mine.nu
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ******************************************************************************/
  17. // Unless specified, every function returns 0 for success or an error code
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include "libppf.h"
  22. #define DEFAULT_CHUNK_ALLOC 4096
  23. static void addChunk(ppf_t *ppf, ppf_chunk_t *chunk) {
  24. // reallocate if out of room
  25. if (ppf->chunkCount >= ppf->chunkAlloc) {
  26. ppf_chunk_t *newChunks;
  27. int newAlloc = ppf->chunkAlloc * 2;
  28. newChunks = malloc(newAlloc * sizeof(ppf_chunk_t));
  29. memcpy(newChunks, ppf->chunks, ppf->chunkAlloc * sizeof(ppf_chunk_t));
  30. free(ppf->chunks);
  31. ppf->chunks = newChunks;
  32. ppf->chunkAlloc = newAlloc;
  33. }
  34. memcpy(&(ppf->chunks[ppf->chunkCount]), chunk, sizeof(ppf_chunk_t));
  35. ppf->chunkCount++;
  36. }
  37. /*
  38. * Takes a user defined size char array of data creates a long integer
  39. * representing the data offset in big endian order
  40. */
  41. static void chunkSetOffset(ppf_chunk_t *chunk, unsigned char *buf, int bytes) {
  42. int i;
  43. bool start = false;
  44. // Reset current data offset value
  45. chunk->offset = 0;
  46. // Go through array to calculate
  47. for (i = bytes - 1; i > -1; i--) {
  48. // Make sure that the leading zeroes won't be used
  49. if (start == false && buf[i] != 0)
  50. start = true;
  51. if (start)
  52. chunk->offset += (long)buf[i] * (1L << (i * 8));
  53. }
  54. }
  55. void ppf_init(ppf_t *ppf)
  56. {
  57. // Set default options
  58. ppf->loaded = false;
  59. ppf->ppfDesc = "";
  60. ppf->ppfName = "";
  61. ppf->isoName = "";
  62. ppf->totalSize = 0;
  63. ppf->type = TYPE_BIN;
  64. ppf->hasUndo = false;
  65. ppf->validation = false;
  66. ppf->version = 1;
  67. ppf->chunks = malloc(DEFAULT_CHUNK_ALLOC * sizeof(ppf_chunk_t));
  68. ppf->chunkAlloc = DEFAULT_CHUNK_ALLOC;
  69. ppf->chunkCount = 0;
  70. }
  71. /*
  72. * Load and parse the given PPF patch file
  73. */
  74. int ppf_loadPatch(ppf_t *ppf, char *filename) {
  75. FILE *file;
  76. unsigned char buf[256];
  77. int chunkSize, offsetSize, size;
  78. bool repeat;
  79. // Save filename
  80. ppf->ppfName = filename;
  81. // Try to open the patch
  82. if ((file = fopen(filename, "rb")) == NULL)
  83. return ERROR_PPF_OPEN;
  84. // Check if this really is a PPF file
  85. if (fread(buf, 1, 5, file) != 5)
  86. return ERROR_PPF_READ;
  87. if (buf[0] != 'P' || buf[1] != 'P' || buf[2] != 'F')
  88. return ERROR_PPF_FORMAT;
  89. // Get version number
  90. ppf->version = fgetc(file) + 1;
  91. // Get patch description
  92. if (fread(buf, 1, 50, file) != 50)
  93. return ERROR_PPF_READ;
  94. // Add it as a string
  95. ppf->ppfDesc = malloc(64);
  96. memcpy(ppf->ppfDesc, buf, 50);
  97. // TODO: null terminate?
  98. // The following only applies to PPF v3.0
  99. if (ppf->version == 3) {
  100. // Get image type
  101. ppf->type = fgetc(file);
  102. // Find out if validation is available
  103. if (fgetc(file) == 0x01)
  104. ppf->validation = true;
  105. // Find out if undo data is available
  106. if (fgetc(file) == 0x01)
  107. ppf->hasUndo = true;
  108. }
  109. // Set patch file pointer at the data position and set offset size,
  110. // which is 32-bit (4 bytes) for PPFv1 and 64-bit (8 bytes) for PPFv3
  111. if (ppf->version == 1) {
  112. offsetSize = 4;
  113. fseek(file, 56, SEEK_SET);
  114. } else if (ppf->version == 3) {
  115. offsetSize = 8;
  116. if (ppf->validation)
  117. fseek(file, 1084, SEEK_SET);
  118. else
  119. fseek(file, 60, SEEK_SET);
  120. } else {
  121. return ERROR_PPF_VERSION;
  122. }
  123. while ((size = fread(buf, 1, offsetSize, file)) != 0) {
  124. // Size must be 0, or offsetSize
  125. if (size != offsetSize)
  126. return ERROR_PPF_READ;
  127. // Create a new data chunk
  128. ppf_chunk_t chunk;
  129. chunk.offset = 0;
  130. chunk.size = 0;
  131. // Get offset
  132. chunkSetOffset(&chunk, buf, offsetSize);
  133. // Read chunk size and add it to the total chunk size
  134. if (fread(buf, 1, 1, file) != 1)
  135. return ERROR_PPF_READ;
  136. else
  137. chunkSize = buf[0];
  138. ppf->totalSize += chunkSize;
  139. // Read and add chunk data. If chunkSize is 0, the first byte is the
  140. // data and the second is number of repetitions. This is only valid
  141. // for PPFv1 patches
  142. repeat = false;
  143. if (chunkSize == 0) {
  144. if (ppf->version == 3)
  145. return ERROR_PPF_FORMAT;
  146. else if (ppf->version == 1) {
  147. chunkSize = 2;
  148. repeat = true;
  149. }
  150. }
  151. if ((signed)fread(buf, 1, chunkSize, file) != chunkSize)
  152. return ERROR_PPF_READ;
  153. else {
  154. chunk.size = chunkSize;
  155. if (repeat) {
  156. // If size is 2, the first byte in buffer is the data and the second
  157. // is number of repetitions of this data
  158. memset(chunk.data, buf[0], buf[1]);
  159. } else {
  160. memcpy(chunk.data, buf, chunkSize);
  161. }
  162. }
  163. // Read and add optional undo data
  164. if (ppf->version == 3 && ppf->hasUndo) {
  165. if ((signed)fread(buf, 1, chunkSize, file) != chunkSize)
  166. return ERROR_PPF_READ;
  167. else {
  168. memcpy(chunk.undo, buf, chunkSize);
  169. }
  170. }
  171. // Add chunk to ppf vector
  172. addChunk(ppf, &chunk);
  173. }
  174. // Close file
  175. if (fclose(file) != 0)
  176. return ERROR_PPF_CLOSE;
  177. // Set patch as loaded
  178. ppf->loaded = true;
  179. return 0;
  180. }
  181. /*
  182. * Apply the loaded patch or undo data on the given file
  183. */
  184. int ppf_applyPatch(ppf_t *ppf, char *filename, bool undo) {
  185. FILE *file;
  186. unsigned i;
  187. // Save filename
  188. ppf->isoName = filename;
  189. // Make sure that the patch has been loaded and that undo data is
  190. // available if specified
  191. if (!ppf->loaded)
  192. return ERROR_PPF_LOADED;
  193. if (undo && !ppf->hasUndo)
  194. return ERROR_PPF_UNDO;
  195. // Make sure that file exists by trying to open it in read mode first
  196. if ((file = fopen(filename, "rb")) == NULL)
  197. return ERROR_ISO_EXISTS;
  198. else
  199. fclose(file);
  200. // Open the given file
  201. if ((file = fopen(filename, "r+b")) == NULL)
  202. return ERROR_ISO_OPEN;
  203. // Go through patch chunks one by one
  204. for (i = 0; i < ppf->chunkCount; i++) {
  205. // Position binary file pointer at this chunk's offset
  206. fseek(file, ppf->chunks[i].offset, SEEK_SET);
  207. // Write chunk data or undo data to file
  208. unsigned chunkSize = ppf->chunks[i].size;
  209. if (undo) {
  210. if (fwrite(ppf->chunks[i].undo, 1, chunkSize, file) != chunkSize)
  211. return ERROR_ISO_WRITE;
  212. } else {
  213. if (fwrite(ppf->chunks[i].data, 1, chunkSize, file) != chunkSize)
  214. return ERROR_ISO_WRITE;
  215. }
  216. }
  217. // Close files
  218. if (fclose(file) == -1)
  219. return ERROR_ISO_CLOSE;
  220. return 0;
  221. }
  222. /*
  223. * Print patch info / status
  224. */
  225. void ppf_dumpInfo(ppf_t *ppf) {
  226. fprintf(stderr, "Linux PPF Patcher v0.1-rc1\n\n");
  227. fprintf(stderr, "Patch info:\n");
  228. fprintf(stderr, " Name: %s\n", ppf->ppfName);
  229. fprintf(stderr, " Version: %d.0\n", ppf->version);
  230. fprintf(stderr, " Chunks: %d\n", ppf->chunkCount);
  231. fprintf(stderr, " Total Size: %d\n", ppf->totalSize);
  232. fprintf(stderr, " Image type: ");
  233. if (ppf->type == TYPE_BIN)
  234. fprintf(stderr, "BIN\n");
  235. else if (ppf->type == TYPE_GI)
  236. fprintf(stderr, "GI\n");
  237. else
  238. fprintf(stderr, "Unknown\n");
  239. fprintf(stderr, " Validation: ");
  240. if (ppf->validation)
  241. fprintf(stderr, "Available\n");
  242. else
  243. fprintf(stderr, "Not available\n");
  244. fprintf(stderr, " Undo data: ");
  245. if (ppf->hasUndo)
  246. fprintf(stderr, "Available\n");
  247. else
  248. fprintf(stderr, "Not available\n");
  249. // fprintf(stderr, " Description: " << desc << "\n";
  250. }