tga-compiler.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "Crown.h"
  4. const char* root_path = NULL;
  5. const char* resource_in = NULL;
  6. const char* resource_out = NULL;
  7. void print_help_message(const char* program_name);
  8. void parse_command_line(int argc, char** argv);
  9. using namespace crown;
  10. struct TGAHeader
  11. {
  12. char id_length; // 00h Size of Image ID field
  13. char color_map_type; // 01h Color map type
  14. char image_type; // 02h Image type code
  15. char c_map_spec[5]; // 03h Color map origin 05h Color map length 07h Depth of color map entries
  16. uint16_t x_offset; // 08h X origin of image
  17. uint16_t y_offset; // 0Ah Y origin of image
  18. uint16_t width; // 0Ch Width of image
  19. uint16_t height; // 0Eh Height of image
  20. char pixel_depth; // 10h Image pixel size
  21. char image_descriptor; // 11h Image descriptor byte
  22. };
  23. void print_help_message(const char* program_name);
  24. void parse_command_line(int argc, char** argv);
  25. void load_uncompressed(void* dest, Stream* stream, uint32_t width, uint32_t height, uint32_t channels);
  26. void load_compressed(void* dest, Stream* stream, uint32_t width, uint32_t height, uint32_t channels);
  27. void swap_red_blue(uint8_t* data, uint64_t size, uint32_t channels);
  28. /// TGA compiler for "tga" resource type
  29. /// TODO: Explain supported formats, usage etc.
  30. int main(int argc, char** argv)
  31. {
  32. parse_command_line(argc, argv);
  33. // FIXME: validate input
  34. Filesystem fs_root(root_path);
  35. if (!fs_root.exists(resource_in))
  36. {
  37. printf("%s: ERROR: %s does not exist. Aborting.\n", argv[0], resource_in);
  38. return -1;
  39. }
  40. char resource_basename[256];
  41. char resource_extension[256];
  42. path::filename_without_extension(resource_in, resource_basename, 256);
  43. path::extension(resource_in, resource_extension, 256);
  44. uint32_t resource_basename_hash = hash::fnv1a_32(resource_basename, string::strlen(resource_basename));
  45. uint32_t resource_extension_hash = hash::fnv1a_32(resource_extension, string::strlen(resource_extension));
  46. FileStream* src_file = (FileStream*)fs_root.open(resource_in, SOM_READ);
  47. //-------------------------------------------------------------------------
  48. // Read TGA Header
  49. //-------------------------------------------------------------------------
  50. // The TGA header used throughout the code
  51. TGAHeader header;
  52. memset(&header, 0, sizeof(TGAHeader));
  53. // Read the header
  54. src_file->read(&header, sizeof(TGAHeader));
  55. // Skip TGA ID
  56. src_file->skip(header.id_length);
  57. // Pixel format currently unknown
  58. PixelFormat format = PF_UNKNOWN;
  59. // Compute color channels
  60. uint32_t channels = header.pixel_depth / 8;
  61. // Compute image size
  62. uint64_t image_size = header.width * header.height;
  63. uint8_t* image_data = NULL;
  64. // Select the appropriate pixel format and allocate resource data based on tga size and channels
  65. switch (channels)
  66. {
  67. case 2:
  68. case 3:
  69. {
  70. format = PF_RGB_8;
  71. image_data = new uint8_t[(uint32_t)(image_size * 3)];
  72. break;
  73. }
  74. case 4:
  75. {
  76. format = PF_RGBA_8;
  77. image_data = new uint8_t[(uint32_t)(image_size * channels)];
  78. break;
  79. }
  80. default:
  81. {
  82. printf("Fatal: Unable to determine TGA channels. Aborting.\n");
  83. return -1;
  84. }
  85. }
  86. // Determine image type (compressed/uncompressed) and call proper function to load TGA
  87. switch (header.image_type)
  88. {
  89. case 0:
  90. {
  91. printf("Fatal: The resource does not contain image data. Aborting.");
  92. exit(-1);
  93. }
  94. case 2:
  95. {
  96. load_uncompressed(image_data, src_file, header.width, header.height, channels);
  97. break;
  98. }
  99. case 10:
  100. {
  101. load_compressed(image_data, src_file, header.width, header.height, channels);
  102. break;
  103. }
  104. default:
  105. {
  106. printf("Fatal: Image type not supported. Aborting.");
  107. exit(-1);
  108. }
  109. }
  110. // Open output file
  111. FileStream* dest_file = (FileStream*)fs_root.open(resource_out, SOM_WRITE);
  112. ArchiveEntry archive_entry;
  113. archive_entry.name = resource_basename_hash;
  114. archive_entry.type = resource_extension_hash;
  115. archive_entry.offset = sizeof(ArchiveEntry);
  116. archive_entry.size = image_size * channels + sizeof(PixelFormat) + sizeof(uint16_t) * 2;
  117. // Write out the archive entry
  118. dest_file->write(&archive_entry, sizeof(ArchiveEntry));
  119. // Write out the data
  120. dest_file->write(&format, sizeof(PixelFormat));
  121. dest_file->write(&header.width, sizeof(uint16_t));
  122. dest_file->write(&header.height, sizeof(uint16_t));
  123. dest_file->write(image_data, image_size * channels);
  124. // Done, free the resources and exit
  125. if (image_data != NULL)
  126. {
  127. delete[] image_data;
  128. }
  129. fs_root.close(dest_file);
  130. return 0;
  131. }
  132. //-----------------------------------------------------------------------------
  133. void parse_command_line(int argc, char** argv)
  134. {
  135. // Parse arguments
  136. ArgsOption options[] =
  137. {
  138. "help", AOA_NO_ARGUMENT, NULL, 'h',
  139. "root-path", AOA_REQUIRED_ARGUMENT, NULL, 'r',
  140. "resource-in", AOA_REQUIRED_ARGUMENT, NULL, 'i',
  141. "resource-out", AOA_REQUIRED_ARGUMENT, NULL, 'o',
  142. NULL, 0, NULL, 0
  143. };
  144. Args args(argc, argv, "", options);
  145. while (1)
  146. {
  147. int32_t ret = args.next_option();
  148. switch (ret)
  149. {
  150. case -1:
  151. {
  152. return;
  153. }
  154. // Help message
  155. case 'h':
  156. {
  157. print_help_message(argv[0]);
  158. exit(0);
  159. }
  160. // Root path
  161. case 'r':
  162. {
  163. if (args.option_argument() == NULL)
  164. {
  165. printf("%s: ERROR: missing path after `--root-path`\n", argv[0]);
  166. exit(-1);
  167. }
  168. root_path = args.option_argument();
  169. break;
  170. }
  171. // Resource in
  172. case 'i':
  173. {
  174. if (args.option_argument() == NULL)
  175. {
  176. printf("%s: ERROR: missing path after `--resource-in`\n", argv[0]);
  177. exit(-1);
  178. }
  179. resource_in = args.option_argument();
  180. break;
  181. }
  182. // Resource out
  183. case 'o':
  184. {
  185. if (args.option_argument() == NULL)
  186. {
  187. printf("%s: ERROR: missing path after `--resource-out`\n", argv[0]);
  188. exit(-1);
  189. }
  190. resource_out = args.option_argument();
  191. break;
  192. }
  193. default:
  194. {
  195. break;
  196. }
  197. }
  198. }
  199. }
  200. //-----------------------------------------------------------------------------
  201. void print_help_message(const char* program_name)
  202. {
  203. printf("Usage: %s [options]\n", program_name);
  204. printf("Options:\n\n");
  205. printf(" --help Show this help.\n");
  206. printf(" --root-path <path> The _absolute_ <path> whether to look for the input resource.\n");
  207. printf(" --resource-in <path> The _relative_ <path> of the input resource.\n");
  208. printf(" --resource-out <width> The _relative_ <path> of the output resource.\n");
  209. }
  210. //-----------------------------------------------------------------------------
  211. void load_uncompressed(void* dest, Stream* stream, uint32_t width, uint32_t height, uint32_t channels)
  212. {
  213. uint64_t size = width * height;
  214. uint8_t* data = (uint8_t*)dest;
  215. if (channels == 2)
  216. {
  217. int32_t j = 0;
  218. for (uint64_t i = 0; i < size * channels; i++)
  219. {
  220. uint16_t pixel_data;
  221. stream->read(&pixel_data, sizeof(pixel_data));
  222. data[j + 0] = (pixel_data & 0x7c) >> 10;
  223. data[j + 1] = (pixel_data & 0x3e) >> 5;
  224. data[j + 2] = (pixel_data & 0x1f);
  225. j += 3;
  226. }
  227. }
  228. else
  229. {
  230. stream->read(data, (size_t)(size * channels));
  231. swap_red_blue(data, size * channels, channels);
  232. }
  233. }
  234. //-----------------------------------------------------------------------------
  235. void load_compressed(void* dest, Stream* stream, uint32_t width, uint32_t height, uint32_t channels)
  236. {
  237. uint8_t rle_id = 0;
  238. uint32_t i = 0;
  239. uint32_t colors_read = 0;
  240. uint64_t size = width * height;
  241. uint8_t* data = (uint8_t*)dest;
  242. uint8_t* colors = new uint8_t[channels];
  243. while (i < size)
  244. {
  245. stream->read(&rle_id, sizeof(uint8_t));
  246. // If MSB == 1
  247. if (rle_id & 0x80)
  248. {
  249. rle_id -= 127;
  250. stream->read(colors, channels);
  251. while (rle_id)
  252. {
  253. data[colors_read + 0] = colors[2];
  254. data[colors_read + 1] = colors[1];
  255. data[colors_read + 2] = colors[0];
  256. if (channels == 4)
  257. {
  258. data[colors_read + 3] = colors[3];
  259. }
  260. rle_id--;
  261. colors_read += channels;
  262. i++;
  263. }
  264. }
  265. else
  266. {
  267. rle_id++;
  268. while (rle_id)
  269. {
  270. stream->read(colors, channels);
  271. data[colors_read + 0] = colors[2];
  272. data[colors_read + 1] = colors[1];
  273. data[colors_read + 2] = colors[0];
  274. if (channels == 4)
  275. {
  276. data[colors_read + 3] = colors[3];
  277. }
  278. rle_id--;
  279. colors_read += channels;
  280. i++;
  281. }
  282. }
  283. }
  284. delete[] colors;
  285. }
  286. //-----------------------------------------------------------------------------
  287. void swap_red_blue(uint8_t* data, uint64_t size, uint32_t channels)
  288. {
  289. for (uint64_t i = 0; i < size; i += channels)
  290. {
  291. data[i] ^= data[i+2];
  292. data[i+2] ^= data[i];
  293. data[i] ^= data[i+2];
  294. }
  295. }