1 /*
2 * palview.cc
3 * Palette (PLAYPAL & COLORMAP) viewer
4 * AYM 1999-11-11
5 */
6
7
8 /*
9 This file is part of Yadex.
10
11 Yadex incorporates code from DEU 5.21 that was put in the public domain in
12 1994 by Rapha�l Quinet and Brendon Wyber.
13
14 The rest of Yadex is Copyright � 1997-2003 Andr� Majorel and others.
15
16 This program is free software; you can redistribute it and/or modify it under
17 the terms of the GNU General Public License as published by the Free Software
18 Foundation; either version 2 of the License, or (at your option) any later
19 version.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License along with
26 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
27 Place, Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30
31 #include "yadex.h"
32 #include <X11/Xlib.h>
33 #include "colour.h"
34 #include "gcolour2.h"
35 #include "gfx.h"
36 #include "palview.h"
37 #include "rgb.h"
38 #include "wadfile.h"
39 #include "wads.h"
40 #include "ytime.h"
41
42
43 /*
44 * Palette viewer::run
45 * The only public method of the palette viewer.
46 */
47
48 // One COLORMAP entry. Wrapped in struct to avoid array<->pointer problems
49 typedef struct { u8 c[DOOM_COLOURS]; } colormap_entry_t;
50
run()51 void Palette_viewer::run ()
52 {
53 int lines = (ncolours + columns - 1) / columns;
54 const int pwidth = columns * (pixels + 1);
55 const int pheight = lines * (pixels + 1);
56 int width = 2 * BOX_BORDER + 2 * WIDE_HSPACING + pwidth;
57 int height = 2 * BOX_BORDER + 3 * WIDE_VSPACING + pheight + 7 * FONTH;
58 int x0 = (ScrMaxX - width) / 2;
59 int y0 = (ScrMaxY - height) / 2;
60 int nmaps = 0; // Number of entries in the COLORMAP lump
61 colormap_entry_t **colormap = 0;
62 rgb_c *playpal = 0;
63
64 // Load the PLAYPAL lump
65 do
66 {
67 playpal = new rgb_c[DOOM_COLOURS];
68 for (size_t n = 0; n < DOOM_COLOURS; n++)
69 playpal[n].set (0, 0, 0);
70 const char *lump_name = "PLAYPAL";
71 MDirPtr dir = FindMasterDir (MasterDir, lump_name);
72 if (dir == NULL)
73 {
74 warn ("%s: lump not found\n", lump_name);
75 break;
76 }
77 const Wad_file *wf = dir->wadfile;
78 if (dir->dir.size % (3 * DOOM_COLOURS) != 0)
79 {
80 warn ("%s has weird size (%ld, not mult of %d), ignoring tail\n",
81 lump_name, (long) dir->dir.size, (int) (DOOM_COLOURS * 3));
82 }
83 wf->seek (dir->dir.start);
84 if (wf->error ())
85 {
86 warn ("%s: seek error\n", lump_name);
87 break;
88 }
89 playpal = new rgb_c[DOOM_COLOURS];
90 for (size_t n = 0; n < DOOM_COLOURS; n++)
91 {
92 char buf[3];
93 wf->read_bytes (buf, sizeof buf);
94 playpal[n].set (buf[0], buf[1], buf[2]);
95 }
96 if (wf->error ())
97 warn ("%s: read error\n", lump_name);
98 }
99 while (0);
100
101 // Load the COLORMAP lump
102 do
103 {
104 const char *lump_name = "COLORMAP";
105 MDirPtr dir = FindMasterDir (MasterDir, lump_name);
106 if (dir == NULL)
107 {
108 warn ("%s: lump not found\n", lump_name);
109 break;
110 }
111 const Wad_file *wf = dir->wadfile;
112 nmaps = dir->dir.size / DOOM_COLOURS;
113 if ((long) DOOM_COLOURS * nmaps != dir->dir.size)
114 {
115 warn ("%s: has weird size (%ld, not mult of %d), ignoring tail\n",
116 lump_name, (long) dir->dir.size, (int) DOOM_COLOURS);
117 }
118 if (nmaps > 200)
119 {
120 warn ("%s has too many (%d) entries, keeping only 200 first\n",
121 lump_name, nmaps);
122 nmaps = 200;
123 }
124 wf->seek (dir->dir.start);
125 if (wf->error ())
126 {
127 warn ("%s: seek error\n", lump_name);
128 break;
129 }
130 colormap = new colormap_entry_t *[nmaps];
131 for (int n = 0; n < nmaps; n++)
132 {
133 colormap[n] = new colormap_entry_t;
134 wf->read_bytes (colormap[n]->c, sizeof colormap[n]->c);
135 }
136 if (wf->error ())
137 warn ("%s: read error\n", lump_name);
138 }
139 while (0);
140
141 // On to the real business
142 ix0 = x0 + BOX_BORDER + WIDE_HSPACING;
143 iy0 = y0 + BOX_BORDER + WIDE_VSPACING;
144 int tx0 = ix0; // Top left corner of text
145 int ty0 = y0 + BOX_BORDER + 2 * WIDE_VSPACING + pheight;
146 push_colour (0); // Save current colour
147 #define DECIDX(i,n) do { i = (i - n + ncolours) % ncolours; } while (0)
148 #define INCIDX(i,n) do { i = (i + n ) % ncolours; } while (0)
149 int mapno = 0;
150 bool mapping = true;
151 int is_drawn = 0;
152 const int YID_WINDOW = 0x01;
153 const int YID_CURSOR = 0x02;
154 const int YID_PALETTE = 0x04;
155 const int YID_TEXT = 0x08;
156 int cursor_phase = 0;
157 int display_phase = 0;
158 unsigned long cursor_time = 0;
159 i = 0;
160 ofs = 0;
161
162 for (;;)
163 {
164 int mi = colormap[mapno]->c[i]; // Mapped index
165 int ei = mapping ? mi : i; // Effective index
166 int mapped_to = 0; // N. distinct colours that map to i
167 for (int n = 0; n < ncolours; n++)
168 for (int m = 0; m < nmaps; m++)
169 if (colormap[m]->c[n] == i)
170 {
171 mapped_to++;
172 break; // Don't count the same mapper twice
173 }
174 int maps_to = 0; // N. distinct colours that i maps to
175 {
176 bitvec_c mappee (ncolours);
177 for (int m = 0; m < nmaps; m++)
178 mappee.set (colormap[m]->c[i]);
179 for (int n = 0; n < ncolours; n++)
180 if (mappee.get (n))
181 maps_to++;
182 }
183
184 // Draw the window
185 if (! (is_drawn & YID_WINDOW))
186 {
187 DrawScreenBox3D (x0, y0, x0 + width - 1, y0 + height - 1);
188 is_drawn = YID_WINDOW; // Redraw everything else
189 }
190
191 // Draw the cursor (frame around the current cell)
192 {
193 const int cycle = 800; // 800 ms
194 unsigned long current_time = y_milliseconds ();
195 unsigned long elapsed_time = current_time - cursor_time;
196 cursor_time = current_time;
197 #if 0
198 cursor_time += elapsed_time - elapsed_time % cycle; // Normalize
199 elapsed_time = current_time - cursor_time;
200 #endif
201 cursor_phase = (cursor_phase + elapsed_time) % cycle;
202 if ((cursor_phase >= cycle / 2) != (display_phase >= cycle / 2))
203 is_drawn &= ~YID_CURSOR;
204 if (! (is_drawn & YID_CURSOR))
205 {
206 draw_cursor (WINFG, cursor_phase >= cycle / 2);
207 display_phase = cursor_phase;
208 is_drawn |= YID_CURSOR;
209 }
210 }
211
212 // Draw a (pixels x pixels) square for each colour
213 if (! (is_drawn & YID_PALETTE))
214 {
215 int x = 0; // Initialized only to prevent GCC from warning
216 int y = 0; // Initialized only to prevent GCC from warning
217 for (int n = 0; n < ncolours; n++)
218 {
219 if (n % columns == 0)
220 {
221 x = ix0;
222 if (n == 0)
223 y = iy0;
224 else
225 y += pixels + 1;
226 }
227 else
228 x += pixels + 1;
229
230 if (game_colour == 0) // If PLAYPAL not found
231 set_pcolour (0);
232 else
233 {
234 if (mapping)
235 set_pcolour (game_colour[colormap[mapno]->c[(n + ofs) % ncolours]]);
236 else
237 set_pcolour (game_colour[(n + ofs) % ncolours]);
238 }
239 DrawScreenBoxwh (x, y, pixels, pixels);
240 }
241 is_drawn |= YID_PALETTE;
242 set_colour (WINFG_DIM); // Just to force the next set_colour() to do sth
243 }
244
245 // Draw the "caption"
246 if (! (is_drawn & YID_TEXT))
247 {
248 set_colour (WINBG);
249 DrawScreenBoxwh (tx0, ty0, pwidth, 7 * FONTH);
250 set_colour (WINFG);
251 DrawScreenText (tx0, ty0, "Index %3d", i);
252 push_colour (mapping ? WINFG : WINFG_DIM);
253 DrawScreenText (tx0, -1, "Mapped index %3d", mi);
254 pop_colour ();
255 DrawScreenText (tx0, -1, "R %3d", playpal[ei].r);
256 DrawScreenText (tx0, -1, "G %3d", playpal[ei].g);
257 DrawScreenText (tx0, -1, "B %3d", playpal[ei].b);
258 DrawScreenText (tx0, -1, "Mapped to by %3d", mapped_to);
259 DrawScreenText (tx0, -1, "Maps to %3d", maps_to);
260 push_colour (mapping ? WINFG : WINFG_DIM);
261 DrawScreenText (tx0 + 18 * FONTW, ty0, "Colormap %3d", mapno);
262 pop_colour ();
263 is_drawn |= YID_TEXT;
264 }
265
266 // Process any events
267 get_input_status ();
268 if (is.key == YK_PU) // [Pgup] previous colormap
269 {
270 mapno--;
271 if (mapno < 0)
272 mapno = nmaps - 1;
273 is_drawn &= ~(YID_PALETTE | YID_TEXT);
274 }
275 else if (is.key == YK_PD) // [Pgdn] next colormap
276 {
277 mapno++;
278 if (mapno >= nmaps)
279 mapno = 0;
280 is_drawn &= ~(YID_PALETTE | YID_TEXT);
281 }
282 else if (is.key == YK_LEFT) // [Left] previous palette entry
283 {
284 draw_cursor (WINBG, false);
285 DECIDX (i, 1);
286 is_drawn &= ~(YID_PALETTE | YID_TEXT | YID_CURSOR);
287 cursor_phase = 0;
288 }
289 else if (is.key == YK_RIGHT) // [Right] next palette entry
290 {
291 draw_cursor (WINBG, false);
292 INCIDX (i, 1);
293 is_drawn &= ~(YID_TEXT | YID_CURSOR);
294 cursor_phase = 0;
295 }
296 else if (is.key == YK_UP) // [Up] previous palette row
297 {
298 draw_cursor (WINBG, false);
299 DECIDX (i, columns);
300 is_drawn &= ~(YID_TEXT | YID_CURSOR);
301 cursor_phase = 0;
302 }
303 else if (is.key == YK_DOWN) // [Down] next palette row
304 {
305 draw_cursor (WINBG, false);
306 INCIDX (i, columns);
307 is_drawn &= ~(YID_TEXT | YID_CURSOR);
308 cursor_phase = 0;
309 }
310 else if (is.key == YK_END // [End], [$]: end of current line
311 || is.key == '$')
312 {
313 draw_cursor (WINBG, false);
314 i += columns - i % columns - 1;
315 is_drawn &= ~(YID_TEXT | YID_CURSOR);
316 cursor_phase = 0;
317 }
318 else if (is.key == YK_HOME // [Home], [0], [^]: start of cur. line
319 || is.key == '^'
320 || is.key == '0')
321 {
322 draw_cursor (WINBG, false);
323 i -= i % columns;
324 is_drawn &= ~(YID_TEXT | YID_CURSOR);
325 cursor_phase = 0;
326 }
327 else if (is.key == YK_RETURN) // [Return]: beginning of next line
328 {
329 draw_cursor (WINBG, false);
330 i += columns - i % columns;
331 is_drawn &= ~(YID_TEXT | YID_CURSOR);
332 cursor_phase = 0;
333 }
334 #if 0 /* Conflicts with "rotate" */
335 else if (is.key == '-') // [-]: beginning of previous line
336 {
337 draw_cursor (WINBG, false);
338 i -= columns + i % columns;
339 is_drawn &= ~(YID_TEXT | YID_CURSOR);
340 cursor_phase = 0;
341 }
342 #endif
343 else if (is.key == 'G' // [G], [L]: beginning of last line
344 || is.key == 'L')
345 {
346 draw_cursor (WINBG, false);
347 i = (lines - 1) * columns;
348 is_drawn &= ~(YID_TEXT | YID_CURSOR);
349 cursor_phase = 0;
350 }
351 else if (is.key == 'H') // [H] beginning of first line
352 {
353 draw_cursor (WINBG, false);
354 i = 0;
355 is_drawn &= ~(YID_TEXT | YID_CURSOR);
356 cursor_phase = 0;
357 }
358 else if (is.key == 'm') // [m] toggle mapping
359 {
360 mapping = ! mapping;
361 is_drawn &= ~(YID_PALETTE | YID_TEXT);
362 }
363 else if (is.key == 'M') // [M] beginning of middle line
364 {
365 draw_cursor (WINBG, false);
366 i = (lines / 2) * columns;
367 is_drawn &= ~(YID_TEXT | YID_CURSOR);
368 cursor_phase = 0;
369 }
370 else if (is.key == '+' || is.key == '=') // [+] increment offset
371 {
372 INCIDX (ofs, 1);
373 INCIDX (i, 1);
374 is_drawn &= ~(YID_PALETTE | YID_TEXT);
375 }
376 else if (is.key == '-') // [-] decrement offset
377 {
378 DECIDX (ofs, 1);
379 DECIDX (i, 1);
380 is_drawn &= ~(YID_PALETTE | YID_TEXT);
381 }
382 else if (is.key == YK_ESC) // [Esc] quit
383 {
384 break;
385 }
386 else if (is.key == YE_EXPOSE)
387 {
388 is_drawn = 0; // Redraw everything
389 }
390 else
391 ;
392 }
393
394 pop_colour (); // Restore current colour
395 delete[] playpal;
396 for (int n = 0; n < nmaps; n++)
397 delete colormap[n];
398 delete[] colormap;
399 }
400
401
draw_cursor(int c,bool phase)402 void Palette_viewer::draw_cursor (int c, bool phase)
403 {
404 const int a = (i + ncolours - ofs) % ncolours;
405 const int side = pixels + 2;
406 const int x0 = ix0 - 1 + (side - 1) * (a % columns);
407 const int y0 = iy0 - 1 + (side - 1) * (a / columns);
408 const int x1 = x0 + side - 1;
409 const int y1 = y0 + side - 1;
410 if (c == WINBG)
411 {
412 set_colour (c);
413 DrawScreenRect (x0, y0, side, side);
414 }
415 else
416 {
417 const int l1 = side / 2;
418 const int l2 = side - l1;
419 // FIXME this cursor looks ugly
420 set_colour (phase ? BLACK : WHITE);
421 DrawScreenLineLen (x0, y0, l1, 0);
422 DrawScreenLineLen (x0, y0, 0, l1);
423 DrawScreenLineLen (x1, y1, 0, -l1);
424 DrawScreenLineLen (x1, y1, -l1, 0);
425 set_colour (phase ? WHITE : BLACK);
426 DrawScreenLineLen (x1, y0, -l2, 0);
427 DrawScreenLineLen (x1, y0, 0, l2);
428 DrawScreenLineLen (x0, y1, 0, -l2);
429 DrawScreenLineLen (x0, y1, l2, 0);
430 }
431 }
432
433
434