tga-compiler.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. printf("Debug: w = %d, h = %d, channels = %d\n", header.width, header.height, channels);
  87. // Determine image type (compressed/uncompressed) and call proper function to load TGA
  88. switch (header.image_type)
  89. {
  90. case 0:
  91. {
  92. printf("Fatal: The resource does not contain image data. Aborting.");
  93. return -1;
  94. }
  95. case 2:
  96. {
  97. printf("Debug: loading uncompressed...\n");
  98. load_uncompressed(image_data, src_file, header.width, header.height, channels);
  99. break;
  100. }
  101. case 10:
  102. {
  103. printf("Debug: loading compressed...\n");
  104. load_compressed(image_data, src_file, header.width, header.height, channels);
  105. break;
  106. }
  107. default:
  108. {
  109. printf("Fatal: Image type not supported. Aborting.");
  110. return -1;
  111. }
  112. }
  113. // FIXME Fixed options for now until proper settings management implemented
  114. TextureMode mode = TM_MODULATE;
  115. TextureFilter filter = TF_BILINEAR;
  116. TextureWrap wrap = TW_REPEAT;
  117. // Open output file
  118. FileStream* dest_file = (FileStream*)fs_root.open(resource_out, SOM_WRITE);
  119. ArchiveEntry archive_entry;
  120. archive_entry.name = resource_basename_hash;
  121. archive_entry.type = resource_extension_hash;
  122. archive_entry.offset = sizeof(ArchiveEntry);
  123. archive_entry.size = image_size * channels + sizeof(PixelFormat) + sizeof(uint16_t) * 2 +
  124. sizeof(TextureMode) + sizeof(TextureFilter) + sizeof(TextureWrap);
  125. // Write out the archive entry
  126. dest_file->write(&archive_entry, sizeof(ArchiveEntry));
  127. // Write out the data
  128. dest_file->write(&format, sizeof(PixelFormat));
  129. dest_file->write(&header.width, sizeof(uint16_t));
  130. dest_file->write(&header.height, sizeof(uint16_t));
  131. dest_file->write(&mode, sizeof(TextureMode));
  132. dest_file->write(&filter, sizeof(TextureFilter));
  133. dest_file->write(&wrap, sizeof(TextureWrap));
  134. dest_file->write(image_data, image_size * channels);
  135. // Done, free the resources and exit
  136. if (image_data != NULL)
  137. {
  138. delete[] image_data;
  139. }
  140. fs_root.close(dest_file);
  141. return 0;
  142. }
  143. //-----------------------------------------------------------------------------
  144. void parse_command_line(int argc, char** argv)
  145. {
  146. // Parse arguments
  147. ArgsOption options[] =
  148. {
  149. "help", AOA_NO_ARGUMENT, NULL, 'h',
  150. "root-path", AOA_REQUIRED_ARGUMENT, NULL, 'r',
  151. "resource-in", AOA_REQUIRED_ARGUMENT, NULL, 'i',
  152. "resource-out", AOA_REQUIRED_ARGUMENT, NULL, 'o',
  153. NULL, 0, NULL, 0
  154. };
  155. Args args(argc, argv, "", options);
  156. while (1)
  157. {
  158. int32_t ret = args.next_option();
  159. switch (ret)
  160. {
  161. case -1:
  162. {
  163. return;
  164. }
  165. // Help message
  166. case 'h':
  167. {
  168. print_help_message(argv[0]);
  169. exit(0);
  170. }
  171. // Root path
  172. case 'r':
  173. {
  174. if (args.option_argument() == NULL)
  175. {
  176. printf("%s: ERROR: missing path after `--root-path`\n", argv[0]);
  177. exit(-1);
  178. }
  179. root_path = args.option_argument();
  180. break;
  181. }
  182. // Resource in
  183. case 'i':
  184. {
  185. if (args.option_argument() == NULL)
  186. {
  187. printf("%s: ERROR: missing path after `--resource-in`\n", argv[0]);
  188. exit(-1);
  189. }
  190. resource_in = args.option_argument();
  191. break;
  192. }
  193. // Resource out
  194. case 'o':
  195. {
  196. if (args.option_argument() == NULL)
  197. {
  198. printf("%s: ERROR: missing path after `--resource-out`\n", argv[0]);
  199. exit(-1);
  200. }
  201. resource_out = args.option_argument();
  202. break;
  203. }
  204. default:
  205. {
  206. break;
  207. }
  208. }
  209. }
  210. }
  211. //-----------------------------------------------------------------------------
  212. void print_help_message(const char* program_name)
  213. {
  214. printf("Usage: %s [options]\n", program_name);
  215. printf("Options:\n\n");
  216. printf(" --help Show this help.\n");
  217. printf(" --root-path <path> The _absolute_ <path> whether to look for the input resource.\n");
  218. printf(" --resource-in <path> The _relative_ <path> of the input resource.\n");
  219. printf(" --resource-out <width> The _relative_ <path> of the output resource.\n");
  220. }
  221. //-----------------------------------------------------------------------------
  222. void load_uncompressed(void* dest, Stream* stream, uint32_t width, uint32_t height, uint32_t channels)
  223. {
  224. uint64_t size = width * height;
  225. uint8_t* data = (uint8_t*)dest;
  226. if (channels == 2)
  227. {
  228. int32_t j = 0;
  229. for (uint64_t i = 0; i < size * channels; i++)
  230. {
  231. uint16_t pixel_data;
  232. stream->read(&pixel_data, sizeof(pixel_data));
  233. data[j + 0] = (pixel_data & 0x7c) >> 10;
  234. data[j + 1] = (pixel_data & 0x3e) >> 5;
  235. data[j + 2] = (pixel_data & 0x1f);
  236. j += 3;
  237. }
  238. }
  239. else
  240. {
  241. stream->read(data, (size_t)(size * channels));
  242. swap_red_blue(data, size * channels, channels);
  243. }
  244. }
  245. //-----------------------------------------------------------------------------
  246. void load_compressed(void* dest, Stream* stream, uint32_t width, uint32_t height, uint32_t channels)
  247. {
  248. uint8_t rle_id = 0;
  249. uint32_t i = 0;
  250. uint32_t colors_read = 0;
  251. uint64_t size = width * height;
  252. uint8_t* data = (uint8_t*)dest;
  253. uint8_t* colors = new uint8_t[channels];
  254. while (i < size)
  255. {
  256. stream->read(&rle_id, sizeof(uint8_t));
  257. // If MSB == 1
  258. if (rle_id & 0x80)
  259. {
  260. rle_id -= 127;
  261. stream->read(colors, channels);
  262. while (rle_id)
  263. {
  264. data[colors_read + 0] = colors[2];
  265. data[colors_read + 1] = colors[1];
  266. data[colors_read + 2] = colors[0];
  267. if (channels == 4)
  268. {
  269. data[colors_read + 3] = colors[3];
  270. }
  271. rle_id--;
  272. colors_read += channels;
  273. i++;
  274. }
  275. }
  276. else
  277. {
  278. rle_id++;
  279. while (rle_id)
  280. {
  281. stream->read(colors, channels);
  282. data[colors_read + 0] = colors[2];
  283. data[colors_read + 1] = colors[1];
  284. data[colors_read + 2] = colors[0];
  285. if (channels == 4)
  286. {
  287. data[colors_read + 3] = colors[3];
  288. }
  289. rle_id--;
  290. colors_read += channels;
  291. i++;
  292. }
  293. }
  294. }
  295. delete[] colors;
  296. }
  297. //-----------------------------------------------------------------------------
  298. void swap_red_blue(uint8_t* data, uint64_t size, uint32_t channels)
  299. {
  300. for (uint64_t i = 0; i < size; i += channels)
  301. {
  302. data[i] ^= data[i+2];
  303. data[i+2] ^= data[i];
  304. data[i] ^= data[i+2];
  305. }
  306. }