resource-linker.cpp 5.8 KB

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