Archive.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include <cstring>
  2. #include <cstdio>
  3. #include <memory>
  4. #include <zlib.h>
  5. #include "Archive.h"
  6. #include "Quirks.h"
  7. #include "utility/misc.h"
  8. using tinystl::string;
  9. using WalrusRPG::PIAF::ARCHIVE_VERSION;
  10. using WalrusRPG::PIAF::Archive;
  11. using WalrusRPG::PIAF::File;
  12. using WalrusRPG::PIAF::FileType;
  13. using WalrusRPG::PIAF::CompressionType;
  14. namespace
  15. {
  16. // Must get a pointer on the file table.
  17. /**
  18. * Reads the file table from given data pointer to an empty FileEntry
  19. * array long enough and a given number of files to load.
  20. * The pointer must directly access the file entry region of the archive.
  21. */
  22. void load_file_table(File *entries, uint32_t *data_offsets, char *data, uint32_t nb_files)
  23. {
  24. for (unsigned index = 0; index < nb_files; index++)
  25. {
  26. // Offsetting the data pointer to the current file entry.
  27. char *current_entry_data = &data[index * 24];
  28. // Copying the filename to the FileEntry
  29. memcpy(entries[index].filename, current_entry_data, 8);
  30. // Making sure there is a terminating \0 character in the filename.
  31. entries[index].filename[8] = '\0';
  32. // /!\ Parsing a value to an enumeration.
  33. // Parses the file type and store it.
  34. entries[index].file_type =
  35. (FileType) read_big_endian_value<uint32_t>(&current_entry_data[8]);
  36. // /!\ Parsing a value to an enumeration.
  37. // Parses the compression type and store it.
  38. entries[index].compression_type =
  39. (CompressionType) read_big_endian_value<uint32_t>(
  40. &current_entry_data[12]);
  41. // Parsign the file size and storing it.
  42. entries[index].file_size =
  43. read_big_endian_value<uint32_t>(&current_entry_data[16]);
  44. // Reading the file's data position in the archive data section.
  45. data_offsets[index] =
  46. read_big_endian_value<uint32_t>(&current_entry_data[20]);
  47. }
  48. }
  49. }
  50. Archive::Archive(string &filepath) : Archive(filepath.c_str())
  51. {
  52. }
  53. Archive::Archive(const char *filepath) : file(nullptr), entries(nullptr), files_data(nullptr), files_loaded(nullptr)
  54. {
  55. // Null pointer exception trigger
  56. if (filepath == nullptr)
  57. {
  58. // TODO : throw NPE
  59. // fprintf(stderr, "Null filepath\n");
  60. }
  61. // Solves the absolute path for given relative path.
  62. // Must be needed in targets like Ndless as it doesn't support environment
  63. // vars and thus PATH.
  64. std::unique_ptr<char> real_filename(Quirks::solve_absolute_path(filepath));
  65. // Open the archive
  66. file = fopen(real_filename.get(), "rb");
  67. // Again another null pointer trigger
  68. if (file == nullptr)
  69. {
  70. // TODO : throw Couldn't open
  71. // fprintf(stderr, "Unable to open %s\n", filepath);
  72. }
  73. // Loading stuff happens NOW
  74. // Checking if the file is long enough to have a header
  75. fseek(file, 0L, SEEK_END);
  76. uint64_t filesize = ftell(file);
  77. fseek(file, 0L, SEEK_SET);
  78. // File to small exception trigger
  79. if (filesize < 32)
  80. {
  81. // TODO : throw file too small
  82. // fprintf(stderr, "File too small\n");
  83. }
  84. // Tempoary buffer to contain the header.
  85. char header_container[32] = {0};
  86. // Read the headers and trigger exceptions on errors
  87. if (fread(header_container, sizeof(char), 32, file))
  88. {
  89. // So we coudln't load the whole header.
  90. // TODO : check flags and return correct exceptions
  91. // fprintf(stderr, "Error loading header\n");
  92. }
  93. // Check if the magic cookie is the same.
  94. // It's a first way to detect if the file is correctly an archive.
  95. if (strncmp(header_container, "WRPGPIAF", 8) != 0)
  96. {
  97. // TODO throw bad header
  98. // fprintf(stderr, "Bad header magic word\n");
  99. }
  100. // Checksum time! Let's check if the header hasn"t been altered.
  101. uint32_t expected_checksum = read_big_endian_value<uint32_t>(&header_container[8]);
  102. uint32_t calculated_checksum = crc32(0L, (unsigned char *) &header_container[16], 16);
  103. if (expected_checksum != calculated_checksum)
  104. {
  105. // TODO throw bad checksum
  106. // fprintf(stderr, "Bad header checksum : %x != %x\n", expected_checksum,
  107. // calculated_checksum);
  108. }
  109. // TODO : version checking
  110. version = read_big_endian_value<uint32_t>(&header_container[16]);
  111. if (version != ARCHIVE_VERSION)
  112. {
  113. // std::exception up;
  114. // throw up; // haha
  115. }
  116. // At this point, the archive header looks unaltered and we finally can parse
  117. // and load the header.
  118. // Read the archive's number of files
  119. nb_files = read_big_endian_value<uint32_t>(&header_container[20]);
  120. // printf("nb_files : %u\n", nb_files);
  121. data_size = read_big_endian_value<uint32_t>(&header_container[24]);
  122. uint64_t calculated_data_size = filesize - 32 - 24 * nb_files;
  123. if (data_size != calculated_data_size)
  124. {
  125. // T0D0 : throw wrong size exception
  126. // fprintf(stderr, "Bad data size : expected %u, got %lld\n", data_size,
  127. // calculated_data_size);
  128. }
  129. // Check if there are files to manage.
  130. if (nb_files != 0)
  131. {
  132. // So, the file table is not empty. let's check if it's
  133. // not altered.
  134. uint32_t expected_filetable_checksum =
  135. read_big_endian_value<uint32_t>(&header_container[12]);
  136. char *file_entry_data = new char[24 * nb_files];
  137. fseek(file, 32, SEEK_SET);
  138. fread(file_entry_data, sizeof(char), 24 * nb_files, file);
  139. // Compare and trigger an exception if the checksum doesn't match.
  140. if (expected_filetable_checksum !=
  141. crc32(0L, (unsigned char *) file_entry_data, 24 * nb_files))
  142. {
  143. // TODO : checksum exception
  144. // fprintf(stderr, "Bad filetable checksum\n");
  145. }
  146. // Create the filetable.
  147. entries = new File[nb_files];
  148. // Parse and story the filetable.
  149. files_data = new uint8_t*[nb_files];
  150. files_loaded = new bool[nb_files];
  151. files_data_offset = new uint32_t[nb_files];
  152. for(unsigned i = 0; i < nb_files; i++)
  153. {
  154. files_data[i] = nullptr;
  155. files_loaded[i] = false;
  156. }
  157. load_file_table(entries, files_data_offset, file_entry_data, nb_files);
  158. delete[] file_entry_data;
  159. }
  160. }
  161. Archive::~Archive()
  162. {
  163. if (file != nullptr)
  164. fclose(file);
  165. if (entries != nullptr)
  166. delete[] entries;
  167. if(files_data != nullptr)
  168. {
  169. for(unsigned i = 0; i < nb_files; i++)
  170. {
  171. if(files_data[i] != nullptr)
  172. {
  173. delete[] files_data[i];
  174. }
  175. }
  176. }
  177. delete[] files_data;
  178. delete[] files_loaded;
  179. }
  180. bool Archive::has(const char *filename)
  181. {
  182. for (unsigned index = 0; index < nb_files; index++)
  183. if (strncmp(filename, entries[index].filename, 8) == 0)
  184. return true;
  185. return false;
  186. }
  187. /**
  188. * Returns the stored file's data from giving a filename.
  189. */
  190. File Archive::get(const char *filename)
  191. {
  192. for (unsigned index = 0; index < nb_files; index++)
  193. {
  194. if (strncmp(filename, entries[index].filename, 8) == 0)
  195. {
  196. // On demand load
  197. if(!files_loaded[index])
  198. {
  199. uint8_t *data = new uint8_t[entries[index].file_size];
  200. fseek(file, files_data_offset[index] + 32 + 24 * nb_files, SEEK_SET);
  201. if (fread(data, sizeof(uint8_t), entries[index].file_size, file) !=
  202. entries[index].file_size)
  203. {
  204. // throw loading error
  205. }
  206. else
  207. {
  208. files_data[index] = data;
  209. entries[index].data = data;
  210. }
  211. files_loaded[index] = true;
  212. }
  213. return entries[index];
  214. }
  215. }
  216. return File();
  217. // throw not found exception
  218. }
  219. File::File(uint8_t *data): data(data)
  220. {
  221. }
  222. File::File() : data(nullptr)
  223. {
  224. }
  225. File::~File()
  226. {
  227. }
  228. const uint8_t* File::get()
  229. {
  230. return data;
  231. }