resource-linker.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #include <stdio.h>
  2. #include "Filesystem.h"
  3. #include "File.h"
  4. #include "Path.h"
  5. #include "String.h"
  6. #include "Hash.h"
  7. #include "Resource.h"
  8. #include "Bundle.h"
  9. #include "DiskFile.h"
  10. #include <cstring>
  11. using namespace crown;
  12. /// Resource linker links together individual compiled resources into a
  13. /// single binary blob ready to be loaded by Crown Engine.
  14. /// Usage: resource-linker <root-path> [resource1, resource2, ..., resourceN]
  15. /// The resources are put into the archive in the order they appear in the command line.
  16. /// This allows to simplify the code and to decouple the linking process from the
  17. /// placement optimization of the resources in the final archive.
  18. int main(int argc, char** argv)
  19. {
  20. //-------------------------------------------------------------------------
  21. // Preliminary checks
  22. //-------------------------------------------------------------------------
  23. if (argc != 3)
  24. {
  25. printf("Usage: %s /path/to/compiled resource.abc.o\n", argv[0]);
  26. return -1;
  27. }
  28. Filesystem fs_root(argv[1]);
  29. const char* resource_name = argv[2];
  30. // FIXME Check the existence of the resource!!!
  31. //-------------------------------------------------------------------------
  32. // Read the archive
  33. //-------------------------------------------------------------------------
  34. // Open the archive file for reading or create if does not exist
  35. if (!fs_root.exists("archive.bin"))
  36. {
  37. fs_root.create_file("archive.bin");
  38. }
  39. // Open the archive file for both reading and writing
  40. DiskFile* archive = (DiskFile*)fs_root.open("archive.bin", (FileOpenMode)(FOM_READ | FOM_WRITE));
  41. // The archive header used throughout the code
  42. ArchiveHeader header;
  43. memset(&header, 0, sizeof(ArchiveHeader));
  44. // If the archive is empty
  45. if (archive->size() == 0)
  46. {
  47. // Initializes the archive header
  48. header.version = ARCHIVE_VERSION;
  49. header.entries_count = 0;
  50. header.checksum = 0; // FIXME Implement checksum
  51. }
  52. else if (archive->size() < sizeof(ArchiveHeader))
  53. {
  54. // If the archive is malformed (i.e. its size is lesser than sizeof(ArchiveHeader))
  55. printf("Fatal: the archive file is malformed. Aborting.\n");
  56. return -1;
  57. }
  58. else
  59. {
  60. // If the archive is well-formed, read the archive header
  61. archive->read(&header, sizeof(ArchiveHeader));
  62. }
  63. // In-Memory representation of the table of entries
  64. ArchiveEntry* entries = NULL;
  65. uint32_t entries_count = 0;
  66. // Read the table of entries if present
  67. if (header.entries_count > 0)
  68. {
  69. entries = new ArchiveEntry[header.entries_count];
  70. archive->read(entries, sizeof(ArchiveEntry) * header.entries_count);
  71. entries_count = header.entries_count;
  72. }
  73. //-------------------------------------------------------------------------
  74. // Read the resource
  75. //-------------------------------------------------------------------------
  76. // Open the resource
  77. DiskFile* resource = (DiskFile*)fs_root.open(resource_name, FOM_READ);
  78. // If the resource is malformed, abort
  79. if (resource->size() < sizeof(ArchiveEntry))
  80. {
  81. printf("Fatal: the resource file is malformed. Aborting.\n");
  82. return -1;
  83. }
  84. // The resource entry used throughout the code
  85. ArchiveEntry resource_entry;
  86. // Read the resource entry
  87. resource->read(&resource_entry, sizeof(ArchiveEntry));
  88. // In-Memory representation of the resource data
  89. uint8_t* resource_data = NULL;
  90. size_t resource_data_size = 0;
  91. // Read the resource data if present
  92. if (resource_entry.size > 0)
  93. {
  94. resource_data = new uint8_t[resource_entry.size];
  95. resource->read(resource_data, resource_entry.size);
  96. resource_data_size = resource_entry.size;
  97. }
  98. //-------------------------------------------------------------------------
  99. // Patch the resource offset and update the archive header
  100. //-------------------------------------------------------------------------
  101. // 1) Obtain the total resource data size
  102. size_t total_resource_data_size = 0;
  103. for (uint32_t i = 0; i < entries_count; i++)
  104. {
  105. total_resource_data_size += entries[i].size;
  106. }
  107. // 2) Read the total resource data in memory (FIXME: ouch... need better strategy, potentially source of
  108. // troubles in case of very large archive files!)
  109. uint8_t* total_resource_data = NULL;
  110. // Read the data only if it is actually there...
  111. if (total_resource_data_size > 0)
  112. {
  113. total_resource_data = new uint8_t[total_resource_data_size];
  114. // The file cursor is right at the start of data section :)
  115. archive->read(total_resource_data, total_resource_data_size);
  116. }
  117. // 3) Patch the previous resource entry offsets
  118. for (uint32_t i = 0; i < entries_count; i++)
  119. {
  120. // Shift everything "down" by the size of the new ArchiveEntry
  121. entries[i].offset += sizeof(ArchiveEntry);
  122. }
  123. // 4) Patch the new resource entry offset
  124. resource_entry.offset = + sizeof(ArchiveHeader) + sizeof(ArchiveEntry) * (entries_count + 1) + total_resource_data_size;
  125. // 5) Path the archive header
  126. header.entries_count += 1;
  127. // 6) Write the new header, the previous entries, the new entry, the previos resource data and the new resource data
  128. // _IN_THAT_ORDER_
  129. archive->seek(0);
  130. // Write the new header
  131. archive->write(&header, sizeof(ArchiveHeader));
  132. // Write the previous entries only if they exist
  133. if (entries_count > 0)
  134. {
  135. archive->write(entries, sizeof(ArchiveEntry) * entries_count);
  136. }
  137. // Write the new resource entry
  138. archive->write(&resource_entry, sizeof(ArchiveEntry));
  139. // Write previous total resource data only if it exist
  140. if (total_resource_data_size > 0)
  141. {
  142. archive->write(total_resource_data, total_resource_data_size);
  143. }
  144. // Write the new resource data only if if exists (a new resource could have no data associated with it)
  145. if (resource_data_size > 0)
  146. {
  147. archive->write(resource_data, resource_data_size);
  148. }
  149. //-------------------------------------------------------------------------
  150. // Free data and close streams
  151. //-------------------------------------------------------------------------
  152. if (entries != NULL)
  153. {
  154. delete[] entries;
  155. }
  156. if (total_resource_data != NULL)
  157. {
  158. delete[] total_resource_data;
  159. }
  160. if (resource_data != NULL)
  161. {
  162. delete[] resource_data;
  163. }
  164. // Close the files, we are done
  165. fs_root.close(resource);
  166. fs_root.close(archive);
  167. return 0;
  168. }