1/*****************************************************************************/
2// tgainfo
3// Written by Michael Wilber, Haiku Translation Kit Team
4//
5// Version:
6//
7// tgainfo is a command line program for displaying information about
8// TGA images.
9//
10//
11// This application and all source files used in its construction, except
12// where noted, are licensed under the MIT License, and have been written
13// and are:
14//
15// Copyright (c) 2003 Haiku Project
16//
17// Permission is hereby granted, free of charge, to any person obtaining a
18// copy of this software and associated documentation files (the "Software"),
19// to deal in the Software without restriction, including without limitation
20// the rights to use, copy, modify, merge, publish, distribute, sublicense,
21// and/or sell copies of the Software, and to permit persons to whom the
22// Software is furnished to do so, subject to the following conditions:
23//
24// The above copyright notice and this permission notice shall be included
25// in all copies or substantial portions of the Software.
26//
27// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33// DEALINGS IN THE SOFTWARE.
34/*****************************************************************************/
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <ByteOrder.h>
39#include <Catalog.h>
40#include <File.h>
41#include <TranslatorFormats.h>
42#include <StorageDefs.h>
43
44#undef B_TRANSLATION_CONTEXT
45#define B_TRANSLATION_CONTEXT "tgainfo"
46
47#define max(x,y) ((x > y) ? x : y)
48#define DATA_BUFFER_SIZE 64
49
50struct TGAFileHeader {
51	uint8 idlength;
52		// Number of bytes in the Image ID field
53	uint8 colormaptype;
54	uint8 imagetype;
55};
56
57#define TGA_NO_COLORMAP			0
58#define TGA_COLORMAP			1
59
60#define TGA_NO_IMAGE_DATA		0
61
62#define TGA_NOCOMP_COLORMAP		1
63#define TGA_NOCOMP_TRUECOLOR	2
64#define TGA_NOCOMP_BW			3
65#define TGA_RLE_COLORMAP		9
66#define TGA_RLE_TRUECOLOR		10
67#define TGA_RLE_BW				11
68
69// Information about the color map (palette). These bytes are
70// always present, but are zero if no color map is present
71struct TGAColorMapSpec {
72	uint16 firstentry;		// first useful entry in the color map
73	uint16 length;			// number of color map entries
74	uint8 entrysize;		// number of bits per entry
75};
76
77struct TGAImageSpec {
78	uint16 xorigin;
79	uint16 yorigin;
80	uint16 width;
81	uint16 height;
82	uint8 depth;
83	uint8 descriptor;
84};
85
86#define TGA_ORIGIN_VERT_BIT	0x20
87#define TGA_ORIGIN_BOTTOM	0
88#define TGA_ORIGIN_TOP		1
89
90#define TGA_ORIGIN_HORZ_BIT	0x10
91#define TGA_ORIGIN_LEFT		0
92#define TGA_ORIGIN_RIGHT	1
93
94#define TGA_DESC_BITS76		0xc0
95#define TGA_DESC_ALPHABITS	0x0f
96
97#define TGA_HEADERS_SIZE 18
98#define TGA_FTR_LEN 26
99#define TGA_EXT_LEN 495
100#define LINE_LEN 82
101
102const char *
103colormaptype(uint8 n)
104{
105	switch (n) {
106		case 0: return B_TRANSLATE("No colormap");
107		case 1: return B_TRANSLATE("colormap");
108	}
109	return "unknown";
110}
111
112const char *
113imagetype(uint8 n)
114{
115	switch (n) {
116		case 0:  return B_TRANSLATE("No Image Data");
117		case 1:  return B_TRANSLATE("colormap");
118		case 2:  return B_TRANSLATE("true color");
119		case 3:  return B_TRANSLATE("grayscale");
120		case 9:  return B_TRANSLATE("RLE colormap");
121		case 10: return B_TRANSLATE("RLE true color");
122		case 11: return B_TRANSLATE("RLE grayscale");
123		default: break;
124	}
125	return B_TRANSLATE("unknown");
126}
127
128uint16
129tga_uint16(char *buffer, int32 offset)
130{
131	return B_LENDIAN_TO_HOST_INT16(*(reinterpret_cast<uint16 *>(buffer + offset)));
132}
133
134uint32
135tga_uint32(char *buffer, int32 offset)
136{
137	return B_LENDIAN_TO_HOST_INT32(*(reinterpret_cast<uint32 *>(buffer + offset)));
138}
139
140void
141print_tga_info(BFile &file)
142{
143	uint8 buf[TGA_HEADERS_SIZE];
144
145	// read in TGA headers
146	ssize_t size = TGA_HEADERS_SIZE;
147	if (size > 0 && file.Read(buf, size) != size) {
148		printf(B_TRANSLATE("Error: unable to read all TGA headers\n"));
149		return;
150	}
151
152	// TGA file header
153	TGAFileHeader fh;
154	fh.idlength = buf[0];
155	fh.colormaptype = buf[1];
156	fh.imagetype = buf[2];
157
158	printf(B_TRANSLATE("\nFile Header:\n"));
159	printf(B_TRANSLATE("    id length: %d\n"), static_cast<int>(fh.idlength));
160
161	printf(B_TRANSLATE("colormap type: %d (%s)\n"),
162		static_cast<int>(fh.colormaptype),
163		static_cast<const char *>(colormaptype(fh.colormaptype)));
164	printf(B_TRANSLATE("   image type: %d (%s)\n"),
165		static_cast<int>(fh.imagetype),
166		static_cast<const char *>(imagetype(fh.imagetype)));
167
168
169	// TGA color map spec
170	TGAColorMapSpec mapspec;
171	mapspec.firstentry = tga_uint16(reinterpret_cast<char *>(buf), 3);
172	mapspec.length = tga_uint16(reinterpret_cast<char *>(buf), 5);
173	mapspec.entrysize = buf[7];
174
175	printf(B_TRANSLATE("\nColormap Spec:\n"));
176	printf(B_TRANSLATE("first entry: %d\n"),
177		static_cast<int>(mapspec.firstentry));
178	printf(B_TRANSLATE("     length: %d\n"),
179		static_cast<int>(mapspec.length));
180	printf(B_TRANSLATE(" entry size: %d\n"),
181		static_cast<int>(mapspec.entrysize));
182
183
184	// TGA image spec
185	TGAImageSpec imagespec;
186	imagespec.xorigin = tga_uint16(reinterpret_cast<char *>(buf), 8);
187	imagespec.yorigin = tga_uint16(reinterpret_cast<char *>(buf), 10);
188	imagespec.width = tga_uint16(reinterpret_cast<char *>(buf), 12);
189	imagespec.height = tga_uint16(reinterpret_cast<char *>(buf), 14);
190	imagespec.depth = buf[16];
191	imagespec.descriptor = buf[17];
192
193	printf(B_TRANSLATE("\nImage Spec:\n"));
194	printf(B_TRANSLATE("  x origin: %d\n"),
195		static_cast<int>(imagespec.xorigin));
196	printf(B_TRANSLATE("  y origin: %d\n"),
197		static_cast<int>(imagespec.yorigin));
198	printf(B_TRANSLATE("     width: %d\n"),
199		static_cast<int>(imagespec.width));
200	printf(B_TRANSLATE("    height: %d\n"),
201		static_cast<int>(imagespec.height));
202	printf(B_TRANSLATE("     depth: %d\n"),
203		static_cast<int>(imagespec.depth));
204	printf(B_TRANSLATE("descriptor: 0x%.2x\n"),
205		static_cast<int>(imagespec.descriptor));
206	printf(B_TRANSLATE("\talpha (attr): %d\n"),
207		static_cast<int>(imagespec.descriptor & TGA_DESC_ALPHABITS));
208	if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT)
209		if (imagespec.descriptor & TGA_ORIGIN_HORZ_BIT)
210			printf(B_TRANSLATE("\t      origin: %d (%s %s)\n"),
211				static_cast<int>(imagespec.descriptor & (TGA_ORIGIN_VERT_BIT
212				| TGA_ORIGIN_HORZ_BIT)), static_cast<const char *>("top"),
213				static_cast<const char *>("right"));
214		else
215			printf(B_TRANSLATE("\t      origin: %d (%s %s)\n"),
216			static_cast<int>(imagespec.descriptor & (TGA_ORIGIN_VERT_BIT
217				| TGA_ORIGIN_HORZ_BIT)), static_cast<const char *>("top"),
218				static_cast<const char *>("left"));
219	else
220		if (imagespec.descriptor & TGA_ORIGIN_HORZ_BIT)
221			printf(B_TRANSLATE("\t      origin: %d (%s %s)\n"),
222				static_cast<int>(imagespec.descriptor & (TGA_ORIGIN_VERT_BIT
223				| TGA_ORIGIN_HORZ_BIT)), static_cast<const char *>("bottom"),
224				static_cast<const char *>("right"));
225		else
226			printf(B_TRANSLATE("\t      origin: %d (%s %s)\n"),
227			static_cast<int>(imagespec.descriptor & (TGA_ORIGIN_VERT_BIT
228				| TGA_ORIGIN_HORZ_BIT)), static_cast<const char *>("bottom"),
229				static_cast<const char *>("left"));
230
231
232	printf(B_TRANSLATE("\t  bits 7 & 6: %d\n"),
233		static_cast<int>(imagespec.descriptor & TGA_DESC_BITS76));
234
235
236	// Optional TGA Footer
237	off_t filesize = 0;
238	if (file.GetSize(&filesize) == B_OK) {
239
240		char tgafooter[TGA_FTR_LEN + 1] = { 0 };
241		if (file.ReadAt(filesize - TGA_FTR_LEN, tgafooter, TGA_FTR_LEN) == TGA_FTR_LEN) {
242
243			if (strcmp(tgafooter + 8, "TRUEVISION-XFILE.") == 0) {
244
245				uint32 extoffset = 0, devoffset = 0;
246				extoffset = tga_uint32(tgafooter, 0);
247				devoffset = tga_uint32(tgafooter, 4);
248
249				printf(B_TRANSLATE("\nTGA Footer:\n"));
250				printf(B_TRANSLATE("extension offset: 0x%.8lx (%ld)\n"),
251					static_cast<long int>(extoffset),
252					static_cast<long int>(extoffset));
253				printf(B_TRANSLATE("developer offset: 0x%.8lx (%ld)\n"),
254					static_cast<long int>(devoffset),
255					static_cast<long int>(devoffset));
256				printf(B_TRANSLATE("signature: %s\n"), tgafooter + 8);
257
258				if (extoffset) {
259					char extbuf[TGA_EXT_LEN];
260					if (file.ReadAt(extoffset, extbuf, TGA_EXT_LEN) == TGA_EXT_LEN) {
261
262						printf(B_TRANSLATE("\nExtension Area:\n"));
263
264						char strbuffer[LINE_LEN];
265
266						uint16 extsize = tga_uint16(extbuf, 0);
267						if (extsize < TGA_EXT_LEN) {
268							printf(B_TRANSLATE("\nError: extension "
269								"area is too small (%d)\n"), extsize);
270							return;
271						}
272						printf(B_TRANSLATE("size: %d\n"), extsize);
273
274						memset(strbuffer, 0, LINE_LEN);
275						strncpy(strbuffer, extbuf + 2, 41);
276						printf("author: \"%s\"\n", strbuffer);
277
278						printf(B_TRANSLATE("comments:\n"));
279						for (int32 i = 0; i < 4; i++) {
280							memset(strbuffer, 0, LINE_LEN);
281							strcpy(strbuffer, extbuf + 43 + (i * 81));
282							printf(B_TRANSLATE("\tline %ld: \"%s\"\n"),
283								static_cast<long int>(i + 1),
284								static_cast<const char *>(strbuffer));
285						}
286
287						printf(B_TRANSLATE("date/time (yyyy-mm-dd hh:mm:ss): "
288							"%.4d-%.2d-%.2d %.2d:%.2d:%.2d\n"),
289							tga_uint16(extbuf, 367), tga_uint16(extbuf, 369),
290							tga_uint16(extbuf, 371), tga_uint16(extbuf, 373),
291							tga_uint16(extbuf, 375), tga_uint16(extbuf, 377));
292
293						memset(strbuffer, 0, LINE_LEN);
294						strncpy(strbuffer, extbuf + 379, 41);
295						printf(B_TRANSLATE("job name: \"%s\"\n"), strbuffer);
296
297						printf(B_TRANSLATE("job time (hh:mm:ss): "
298							"%.2d:%.2d:%.2d\n"), tga_uint16(extbuf, 420),
299							tga_uint16(extbuf, 422), tga_uint16(extbuf, 424));
300
301						memset(strbuffer, 0, LINE_LEN);
302						strncpy(strbuffer, extbuf + 426, 41);
303						printf(B_TRANSLATE("software id: \"%s\"\n"),
304							strbuffer);
305
306						char strver[] = "[null]";
307						if (extbuf[469] != '\0') {
308							strver[0] = extbuf[469];
309							strver[1] = '\0';
310						}
311						printf(B_TRANSLATE("software version, letter: %d, "
312							"%s\n"), tga_uint16(extbuf, 467), strver);
313
314						printf(B_TRANSLATE("key color (A,R,G,B): %d, %d, %d, "
315							"%d\n"), extbuf[470], extbuf[471], extbuf[472],
316							extbuf[473]);
317
318						printf(B_TRANSLATE("pixel aspect ratio: %d / %d\n"),
319							tga_uint16(extbuf, 474), tga_uint16(extbuf, 476));
320
321						printf(B_TRANSLATE("gamma value: %d / %d\n"),
322							tga_uint16(extbuf, 478), tga_uint16(extbuf, 480));
323
324						printf(B_TRANSLATE("color correction offset: 0x%.8lx "
325							"(%ld)\n"),	tga_uint32(extbuf, 482),
326							tga_uint32(extbuf, 482));
327						printf(B_TRANSLATE("postage stamp offset: 0x%.8lx "
328							"(%ld)\n"),	tga_uint32(extbuf, 486),
329							tga_uint32(extbuf, 486));
330						printf(B_TRANSLATE("scan line offset: 0x%.8lx "
331							"(%ld)\n"), tga_uint32(extbuf, 490),
332							tga_uint32(extbuf, 490));
333
334						const char *strattrtype = NULL;
335						uint8 attrtype = extbuf[494];
336						switch (attrtype) {
337							case 0: strattrtype
338								= B_TRANSLATE("no alpha"); break;
339							case 1: strattrtype
340								= B_TRANSLATE("undefined, ignore"); break;
341							case 2: strattrtype
342								= B_TRANSLATE("undefined, retain"); break;
343							case 3: strattrtype
344								= B_TRANSLATE("alpha"); break;
345							case 4: strattrtype
346								= B_TRANSLATE("pre-multiplied alpha"); break;
347							default:
348								if (attrtype > 4 && attrtype < 128)
349									strattrtype = B_TRANSLATE("reserved");
350								else
351									strattrtype = B_TRANSLATE("unassigned");
352								break;
353						}
354						printf(B_TRANSLATE("attributes type: %d (%s)\n"),
355							attrtype, strattrtype);
356
357					} else
358						printf(B_TRANSLATE("\nError: Unable to read entire "
359							"extension area\n"));
360				}
361
362			} else
363				printf(B_TRANSLATE("\nTGA footer not found\n"));
364
365		} else
366			printf(B_TRANSLATE("\nError: Unable to read TGA footer "
367				"section\n"));
368
369	} else
370		printf(B_TRANSLATE("\nError: Unable to get file size\n"));
371}
372
373int
374main(int argc, char **argv)
375{
376	printf("\n");
377
378	if (argc == 1) {
379		printf(B_TRANSLATE("tgainfo - reports information about a TGA image file\n"));
380		printf(B_TRANSLATE("\nUsage:\n"));
381		printf(B_TRANSLATE("tgainfo filename.tga\n"));
382	}
383	else {
384		BFile file;
385
386		for (int32 i = 1; i < argc; i++) {
387			if (file.SetTo(argv[i], B_READ_ONLY) != B_OK)
388				printf(B_TRANSLATE("\nError opening %s\n"), argv[i]);
389			else {
390				printf(B_TRANSLATE("\nTGA image information for: %s\n"), argv[i]);
391				print_tga_info(file);
392			}
393		}
394
395	}
396
397	printf("\n");
398
399	return 0;
400}
401
402