]> icculus.org git repositories - taylor/freespace2.git/blob - src/menuui/readyroom.cpp
fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
[taylor/freespace2.git] / src / menuui / readyroom.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/MenuUI/ReadyRoom.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Ready Room code, which is the UI screen for selecting Campaign/mission to play next mainly.
16  *
17  * $Log$
18  * Revision 1.5  2004/07/04 11:39:06  taylor
19  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
20  *
21  * Revision 1.4  2003/05/25 02:30:42  taylor
22  * Freespace 1 support
23  *
24  * Revision 1.3  2002/06/09 04:41:22  relnev
25  * added copyright header
26  *
27  * Revision 1.2  2002/06/02 04:26:34  relnev
28  * warning cleanup
29  *
30  * Revision 1.1.1.1  2002/05/03 03:28:09  root
31  * Initial import.
32  *
33  * 
34  * 20    9/30/99 5:59p Jefff
35  * fixed compile error in OEM ver
36  * 
37  * 19    9/08/99 12:28p Jefff
38  * 
39  * 18    9/06/99 6:38p Dave
40  * Improved CD detection code.
41  * 
42  * 17    9/02/99 2:33p Jefff
43  * column heading coordinate changes
44  * 
45  * 16    8/22/99 4:16p Jefff
46  * scroll button coord fixes
47  * 
48  * 15    7/20/99 1:49p Dave
49  * Peter Drake build. Fixed some release build warnings.
50  * 
51  * 14    7/19/99 2:13p Dave
52  * Added some new strings for Heiko.
53  * 
54  * 13    7/15/99 9:20a Andsager
55  * FS2_DEMO initial checkin
56  * 
57  * 12    7/09/99 5:54p Dave
58  * Seperated cruiser types into individual types. Added tons of new
59  * briefing icons. Campaign screen.
60  * 
61  * 11    2/01/99 5:55p Dave
62  * Removed the idea of explicit bitmaps for buttons. Fixed text
63  * highlighting for disabled gadgets.
64  * 
65  * 10    1/30/99 9:01p Dave
66  * Coord fixes.
67  * 
68  * 9     1/30/99 5:08p Dave
69  * More new hi-res stuff.Support for nice D3D textures.
70  * 
71  * 8     1/29/99 12:47a Dave
72  * Put in sounds for beam weapon. A bunch of interface screens (tech
73  * database stuff).
74  * 
75  * 7     12/11/98 4:35p Andsager
76  * Fix sim room bug when no standalone missions
77  * 
78  * 6     12/07/98 5:02p Dan
79  * Removed improper use of bitmap filename with extension,
80  * 
81  * 5     10/13/98 9:28a Dave
82  * Started neatening up freespace.h. Many variables renamed and
83  * reorganized. Added AlphaColors.[h,cpp]
84  *    
85  *
86  * $NoKeywords: $
87  */
88
89 #include <ctype.h>
90
91 #include "2d.h"
92 #include "font.h"
93 #include "ui.h"
94 #include "uidefs.h"
95 #include "key.h"
96 #include "bmpman.h"
97 #include "gamesequence.h"
98 #include "missioncampaign.h"
99 #include "sound.h"
100 #include "gamesnd.h"
101 #include "missionscreencommon.h"
102 #include "freespace.h"
103 #include "missionparse.h"
104 #include "player.h"
105 #include "managepilot.h"
106 #include "popup.h"
107 #include "contexthelp.h"
108 #include "cfilesystem.h"
109 #include "freespace.h"
110 #include "alphacolors.h"
111
112 #define MAX_MISSIONS    1024
113
114 int Mission_list_coords[GR_NUM_RESOLUTIONS][4] = {
115         { // GR_640
116 #ifdef MAKE_FS1
117                 82, 108, 409, 277
118 #else
119                 33, 108, 402, 279
120 #endif
121         },
122         { // GR_1024
123                 43, 175, 402, 279
124         }
125 };
126
127 int Campaign_list_coords[GR_NUM_RESOLUTIONS][4] = {
128         { // GR_640
129 #ifdef MAKE_FS1
130                 491, 108, 113, 277
131 #else
132                 491, 108, 115, 279
133 #endif
134         },
135         { // GR_1024
136                 491, 175, 115, 279
137         }
138 };
139
140
141 // x coordinate offsets for data when campaign tab active
142 #define C_TEXT_X                0
143 #define C_SUBTEXT_X     19
144
145 // x coordinate offsets for data when mission tab active
146 #define M_TEXT_X                0
147
148 #define MODE_CAMPAIGNS  0
149 #define MODE_MISSIONS   1
150
151 #define MAX_LINES                                       200
152 #define MAX_DESC_LINES                  200
153 #define NUM_BUTTONS                             11
154 #define LIST_BUTTONS_MAX                40
155
156 #define SCROLL_UP_BUTTON                0
157 #define SCROLL_DOWN_BUTTON              1
158 #define MISSION_TAB                             2
159 #define CAMPAIGN_TAB                            3
160 #define HELP_BUTTON                             4
161 #define COMMIT_BUTTON                   5
162 #define OPTIONS_BUTTON                  6
163 #define TECH_DATABASE_BUTTON    7
164 #define SIMULATOR_BUTTON                8
165 #define CUTSCENES_BUTTON                9
166 #define CREDITS_BUTTON                  10
167
168 #define CAMPAIGN_MISSION_HASH_SIZE 307
169
170 struct sim_room_buttons {
171         char *filename;
172         int x, y, xt, yt;
173         int hotspot;
174         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
175
176         sim_room_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {}
177 };
178
179 static sim_room_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
180 //XSTR:OFF
181         {               // GR_640
182 #ifdef MAKE_FS1
183                 sim_room_buttons("LMB_04",              7,              135,    -1, -1, 4),             // up arrows
184                 sim_room_buttons("LMB_05",              7,              182,    -1, -1, 5),             // down arrows
185                 sim_room_buttons("LMB_06",              23,             396,    -1, -1, 6),             // single missions
186                 sim_room_buttons("LMB_07",              198,    396,    -1, -1, 7),             // campaign missions
187                 sim_room_buttons("LMB_08",              469,    427,    -1, -1, 8),             // help
188                 sim_room_buttons("LMB_09",              554,    411,    -1, -1, 9),             // commit
189                 sim_room_buttons("LMB_10",              447,    452,    -1,     -1, 10),        // options
190
191                 sim_room_buttons("TDB_00",              0,              0,              -1, -1, 0),             // technical database
192                 sim_room_buttons("TDB_01",              0,              19,             -1, -1, 1),             // mission simulator
193                 sim_room_buttons("TDB_02",              0,              35,             -1, -1, 2),             // cutscenes
194                 sim_room_buttons("TDB_03",              0,              56,             -1,     -1,     3),             // credits
195 #else
196                 sim_room_buttons("LMB_04",              1,              99,     -1,     -1,     4),
197                 sim_room_buttons("LMB_05",              1,              381,    -1,     -1,     5),
198                 sim_room_buttons("LMB_06",              6,              438,    40,     445,    6),
199                 sim_room_buttons("LMB_07",              6,              457,    40,     462,    7),
200                 sim_room_buttons("LMB_08",              534,    426,    500,    440,    8),
201                 sim_room_buttons("LMB_09",              571,    426,    572,    413,    9),
202                 sim_room_buttons("LMB_10",              534,    455,    480,    462,    10),
203
204                 sim_room_buttons("TDB_00",              7,              3,              37,     7,              0),
205                 sim_room_buttons("TDB_01",              7,              18,     37,     23,     1),
206                 sim_room_buttons("TDB_02",              7,              34,     37,     38,     2),
207                 sim_room_buttons("TDB_03",              7,              49,     37,     54,     3),
208 #endif
209         },
210         {               // GR_1024
211                 sim_room_buttons("2_LMB_04",    2,              159,    -1,     -1,     4),
212                 sim_room_buttons("2_LMB_05",    2,              609,    -1,     -1,     5),
213                 sim_room_buttons("2_LMB_06",    10,     701,    64,     712,    6),
214                 sim_room_buttons("2_LMB_07",    10,     732,    64,     739,    7),
215                 sim_room_buttons("2_LMB_08",    854,    681,    800, 704,       8),
216                 sim_room_buttons("2_LMB_09",    914,    681,    915, 660,       9),
217                 sim_room_buttons("2_LMB_10",    854,    728,    800, 728,       10),
218
219                 sim_room_buttons("2_TDB_00",    12,     5,              59,     12,     0),
220                 sim_room_buttons("2_TDB_01",    12,     31,     59,     37,     1),
221                 sim_room_buttons("2_TDB_02",    12,     56,     59,     62,     2),
222                 sim_room_buttons("2_TDB_03",    12,     81,     59,     88,     3),
223         }
224 //XSTR:ON
225 };
226
227 char *Sim_filename[GR_NUM_RESOLUTIONS] = {
228         "LoadMission",
229         "2_LoadMission"
230 };
231 char *Sim_mask_filename[GR_NUM_RESOLUTIONS] = {
232         "LoadMission-m",
233         "2_LoadMission-m"
234 };
235
236 char *Campaign_filename[GR_NUM_RESOLUTIONS] = {
237         "Campaign",
238         "2_Campaign"
239 };
240 char *Campaign_mask_filename[GR_NUM_RESOLUTIONS] = {
241         "Campaign-m",
242         "2_Campaign-m"
243 };
244
245 // misc text. ("Mission" and "Filename"
246 #ifdef MAKE_FS1
247         #define NUM_SIM_MISC_TEXT                               0
248 #else
249         #define NUM_SIM_MISC_TEXT                               2
250 #endif
251 #define SIM_MISC_TEXT_MISSION                   0
252 #define SIM_MISC_TEXT_FILENAME          1
253 int Sim_misc_text_coords[GR_NUM_RESOLUTIONS][NUM_SIM_MISC_TEXT][2] = {
254         { // GR_640
255                 // nothing needed for FS1
256 #ifndef MAKE_FS1
257                 {33, 95},
258                 {491, 95}
259 #endif
260         }, 
261         { // GR_1024
262                 // nothing needed for FS1
263 #ifndef MAKE_FS1
264                 {43, 155},
265                 {491, 155}
266 #endif
267         }
268 };
269
270 // readyroom text line stuff
271 #define READYROOM_LINE_CAMPAIGN 1
272 #define READYROOM_LINE_CMISSION 2
273 #define READYROOM_LINE_MISSION  3
274
275 #define READYROOM_FLAG_FROM_VOLITION                    (1<<0)                  // volition made
276 #ifdef MAKE_FS1
277 #define READYROOM_FLAG_FROM_MDISK                               (1<<1)                  // mission disk
278 #endif
279 static struct { 
280         int type;                                       // see READYROOM_LINE_* defines above
281         char *name;
282         char *filename;
283         int x;                                          // X coordinate of line
284         int y;                                          // Y coordinate of line
285         int flags;                                      // special flags, see READYROOM_FLAG_* defines above
286 } sim_room_lines[MAX_LINES];
287
288 static char Cur_campaign[MAX_FILENAME_LEN];
289 static char *Mission_filenames[MAX_MISSIONS];
290 static char *Standalone_mission_names[MAX_MISSIONS];
291 static int  Standalone_mission_flags[MAX_MISSIONS];
292 static char *Campaign_missions[MAX_MISSIONS];
293 static char *Campaign_mission_names[MAX_CAMPAIGN_MISSIONS];
294 static int Campaign_mission_flags[MAX_MISSIONS];
295 static char *Campaign_descs[MAX_CAMPAIGNS];
296 static char *Campaign_descs_temp[MAX_CAMPAIGNS];
297 static char *Campaign_file_names_temp[MAX_CAMPAIGNS];
298 static int Standalone_mission_names_inited = 0;
299 static int Campaign_names_inited = 0;
300 static int Campaign_mission_names_inited = 0;
301 static int Num_standalone_missions;
302 static int Num_campaign_missions;
303 static int Scroll_offset;
304 static int Selected_line;
305 static int Num_lines;
306 static int Num_campaign_missions_with_info = 0;
307 static int Num_standalone_missions_with_info = 0;
308 static int list_x1;
309 static int list_x2;
310 static int list_y;
311 static int list_w1;
312 static int list_w2;
313 static int list_h;
314 static int Background_bitmap;
315 static UI_WINDOW Ui_window;
316 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
317
318 typedef struct hash_node {
319         hash_node *next;
320         char *filename;
321 } hash_node;
322
323 static hash_node *Campaign_mission_hash_table[CAMPAIGN_MISSION_HASH_SIZE];
324 static int Hash_table_inited;
325
326 // special icons (1.04 + stuff)
327 #ifdef MAKE_FS1
328 #define NUM_MISSION_ICONS                       2
329 #else
330 #define NUM_MISSION_ICONS                       1
331 #endif
332 #define MISSION_ICON_VOLITION           0                               // mini volition death's head :)
333 #ifdef MAKE_FS1
334 #define MISSION_ICON_MDISK                      1                               // Silent Threat mini icon
335 #endif
336
337 // icon offsets (see LIST_ defines above
338 //#define MISSION_ICON_VOLITION_X                               (46)
339 #define MISSION_ICON_VOLITION_Y_OFFSET          (-1)
340 #ifdef MAKE_FS1
341 #define MISSION_ICON_MDISK_Y_OFFSET                     (0)
342 #endif
343
344 // icon offsets
345 static int Sim_volition_icon_x[GR_NUM_RESOLUTIONS] = {
346 #ifdef MAKE_FS1
347         45,
348 #else
349         38,
350 #endif
351         49
352 };
353
354 #ifdef MAKE_FS1
355 static int Sim_silent_icon_x[GR_NUM_RESOLUTIONS] = {
356         58,
357         58
358 };
359 #endif
360
361 // special icons themselves
362 int Mission_icon_bitmaps[NUM_MISSION_ICONS];
363 //XSTR:OFF
364 char *Mission_icon_bitmap_filenames[NUM_MISSION_ICONS] = {
365 #ifdef MAKE_FS1
366         "icon-volition",
367         "icon-silent"
368 #else
369         "icon-volition" 
370 #endif
371 };
372 //XSTR:ON
373 void sim_room_load_mission_icons();
374 void sim_room_unload_mission_icons();
375 void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb = NULL, int is_md = 0);
376
377 // Finds a hash value for mission filename
378 //
379 // returns hash value
380 int hash_filename(char *filename) {
381         unsigned __int64 hash_val = 0;
382         char *ptr = filename;
383         
384         // Dont hash .fsm extension, convert all to upper case
385         for (int i=0; i < ((signed int)(strlen(filename)) - 4); i++) {
386                 hash_val = (hash_val << 4) + toupper(*ptr++);
387         }
388
389         return int(hash_val % CAMPAIGN_MISSION_HASH_SIZE);
390 }
391
392 // insert filename into Campaign_mission_hash_table
393 //
394 // returns 1 if successful, 0 if could not allocate memory
395 int hash_insert(char *filename) {
396         int hash_val = hash_filename(filename);
397         hash_node *cur_node;
398
399         // Check if table empty
400         if (Campaign_mission_hash_table[hash_val] == NULL) {
401                 Campaign_mission_hash_table[hash_val] = new hash_node;
402
403                 cur_node = Campaign_mission_hash_table[hash_val];
404
405                 if (cur_node == NULL) {
406                         // Unable to allocate memory
407                         return 0;
408                 }
409         } else {
410                 // Walk down list to first empty node
411                 cur_node = Campaign_mission_hash_table[hash_val];
412                 while (cur_node->next != NULL) {
413                         cur_node = cur_node->next;
414                 }
415
416                 // Create new node
417                 cur_node->next = new hash_node;
418
419                 if (cur_node->next == NULL) {
420                         // unable to allocate memory
421                         return 0;
422                 } else {
423                         cur_node = cur_node->next;
424                 }
425         }
426
427         // Initialize data
428         cur_node->next = NULL;
429         cur_node->filename = filename;
430
431         // Return successs
432         return 1;
433 }
434
435 // Checks if a filename already exitst in the hash table
436 //
437 // returns 1 if found (collision), 0 if no collision
438 int campaign_mission_hash_collision(char *filename)
439 {
440         int hash_val = hash_filename(filename);
441         hash_node *cur_node = Campaign_mission_hash_table[hash_val];
442
443         if (cur_node == NULL) {
444                 return 0;
445         }
446
447         do {
448                 if (!stricmp(filename, cur_node->filename)) {
449                         return 1;
450                 }
451
452                 cur_node = cur_node->next;
453         } while (cur_node != NULL);
454
455         // Ran out of stuff to check
456         return 0;
457 }
458
459 // builds hash table of campaign mission filenames
460 //
461 // returns 1 if successful, 0 if not successful
462 int build_campaign_mission_filename_hash_table()
463 {
464         int rval;
465         // Go through all campaign missions
466         for (int i=0; i<Num_campaign_missions; i++) {
467                 rval = hash_insert(Campaign_missions[i]);
468                 if (rval == 0) {
469                         return 0;
470                 }
471         }
472
473         // successful
474         return 1;
475 }
476
477 // deletes hash table nodes
478 //
479 void campaign_mission_hash_table_delete()
480 {
481         hash_node *cur_node;
482
483         for (int i=0; i<CAMPAIGN_MISSION_HASH_SIZE; i++) {
484                 // Look for entries into array
485                 if (Campaign_mission_hash_table[i] != NULL) {
486                         cur_node = Campaign_mission_hash_table[i];
487
488                         // Walk down the list deleting self
489                         while (cur_node->next != NULL) {
490                                 hash_node *temp = cur_node->next;
491                                 delete cur_node;
492                                 cur_node = temp;
493                         }
494
495                         // Delete last node
496                         delete cur_node;
497                         Campaign_mission_hash_table[i] = NULL;
498                 }
499         }
500 }
501
502
503 // add a line of sim_room smuck to end of list
504 int sim_room_line_add(int type, char *name, char *filename, int x, int y, int flags)
505 {
506         if (Num_lines >= MAX_LINES)
507                 return 0;
508
509         sim_room_lines[Num_lines].type = type;
510         sim_room_lines[Num_lines].name = name;
511         sim_room_lines[Num_lines].filename = filename;
512         sim_room_lines[Num_lines].x = x;
513         sim_room_lines[Num_lines].y = y;
514         sim_room_lines[Num_lines].flags = flags;
515         return Num_lines++;
516 }
517
518 // filter out all multiplayer campaigns
519 int campaign_room_campaign_filter(char *filename)
520 {
521         int type, max_players;
522         char name[NAME_LENGTH], *desc = NULL;
523
524         #ifdef OEM_BUILD
525         // also need to check if this is the builtin campaign
526         if ( game_find_builtin_mission(filename) && mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) {
527         #else
528         if ( mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) {
529         #endif
530                 if ( type == CAMPAIGN_TYPE_SINGLE ) {                   
531                         Campaign_file_names_temp[Num_campaigns] = strdup(filename);
532                         Campaign_descs_temp[Num_campaigns++] = desc;
533                         return 1;                       
534                 }
535         }
536
537         if (desc){
538                 free(desc);
539         }
540
541         return 0;
542 }
543
544 // build up a list of all missions in all campaigns.
545 int sim_room_campaign_mission_filter(char *filename)
546 {
547         int num;
548
549         num = mission_campaign_get_mission_list(filename, &Campaign_missions[Num_campaign_missions], MAX_CAMPAIGN_MISSIONS - Num_campaign_missions);
550         if (num < 0)
551                 return 0;
552
553         Num_campaign_missions += num;
554         return 1;
555 }
556
557 // filter out all missions already used in existing campaigns
558 int sim_room_standalone_mission_filter(char *filename)
559 {
560         int type;
561         char mission_name[255];
562
563         // Check if a campaign mission (single and multi)
564         if (campaign_mission_hash_collision(filename)) {
565                 return 0;
566         } 
567
568         // Check if a standalone multi mission OR Mdisk mission with data
569         type = mission_parse_is_multi(filename, mission_name);
570         if (type && !(type & MISSION_TYPE_SINGLE))
571                 return 0;
572
573         return 1;
574 }
575
576 // builds up list of standalone missions and adds them to missions simulator
577 // processes one mission per frame
578 //
579 // returns 1 if finished with all missions, 0 otherwise
580 //
581 int build_standalone_mission_list_do_frame()
582 {
583         int font_height = gr_get_font_height();
584         char filename[MAX_FILENAME_LEN];
585         char str[256];
586         
587         // When no standalone missions in data directory
588         if (Num_standalone_missions == 0) {
589                 Standalone_mission_names_inited = 1;
590                 return 1;
591         }
592
593         // Set global variable so we we'll have list available next time
594         Standalone_mission_names[Num_standalone_missions_with_info] = NULL;
595         Standalone_mission_flags[Num_standalone_missions_with_info] = 0;
596
597         if (Num_standalone_missions > 0) {  // sanity check
598                 if (strlen(Mission_filenames[Num_standalone_missions_with_info]) < MAX_FILENAME_LEN - 4) { // sanity check?
599                         strcpy(filename, Mission_filenames[Num_standalone_missions_with_info]);
600
601                         // update popup         
602                         memset(str, 0, 256);
603                         sprintf(str, XSTR("Single Mission\n\n%s",989), filename);
604                         popup_change_text(str);
605
606                         // tack on an extension
607                         strcat(filename, FS_MISSION_FILE_EXT);
608                         if (!get_mission_info(filename)) {                      
609                                 Standalone_mission_names[Num_standalone_missions_with_info] = strdup(The_mission.name);
610                                 Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type;
611                                 int y = Num_lines * (font_height + 2);
612
613                                 // determine some extra information
614                                 int flags = 0;
615                                 fs_builtin_mission *fb = game_find_builtin_mission(filename);                           
616                                 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
617                                         flags |= READYROOM_FLAG_FROM_VOLITION;
618                                 }                               
619 #ifdef MAKE_FS1
620                                 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
621                                         flags |= READYROOM_FLAG_FROM_MDISK;
622                                 }
623 #endif
624
625                                 // add the line
626                                 sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[Num_standalone_missions_with_info], Mission_filenames[Num_standalone_missions_with_info], list_x1 + M_TEXT_X, y, flags);                     
627                         }
628                 }
629
630                 Num_standalone_missions_with_info++;
631         }
632
633         if (Num_standalone_missions_with_info == Num_standalone_missions) {
634                 Standalone_mission_names_inited = 1;
635                 return 1;
636         } else {
637                 return 0;
638         }
639 }
640
641 // builds up list of already played missions in a campaign and adds them to missions simulator
642 // processes one mission per frame
643 //
644 // returns 1 if finished with all missions, 0 otherwise
645 //
646 int build_campaign_mission_list_do_frame()
647 {
648         int font_height = gr_get_font_height();
649         char str[256];
650
651         // When no campaign files in data directory
652         if (Campaign.num_missions == 0) {
653                 Campaign_mission_names_inited = 1;
654                 return 1;
655         }
656
657         // change popup
658         memset(str, 0, 256);
659         sprintf(str, XSTR("Campaign Mission\n\n%s",990), Campaign.missions[Num_campaign_missions_with_info].name);
660         popup_change_text(str);
661
662         // Set global variable so we we'll have list available next time
663         Campaign_mission_names[Num_campaign_missions_with_info] = NULL;
664         Campaign_mission_flags[Num_campaign_missions_with_info] = 0;
665         
666         // Only allow missions already completed
667         if (Campaign.missions[Num_campaign_missions_with_info].completed) {
668                 if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name)) {
669                         // add to list
670                         Campaign_mission_names[Num_campaign_missions_with_info] = strdup(The_mission.name);
671                         Campaign_mission_flags[Num_campaign_missions_with_info] = The_mission.game_type;
672                         int y = Num_campaign_missions_with_info * (font_height + 2);
673
674                         // determine some extra information
675                         int flags = 0;
676                         fs_builtin_mission *fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name);                            
677                         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
678                                 flags |= READYROOM_FLAG_FROM_VOLITION;
679                         }                               
680
681 #ifdef MAKE_FS1
682                         if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
683                                 flags |= READYROOM_FLAG_FROM_MDISK;
684                         }
685 #endif
686         
687                         sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[Num_campaign_missions_with_info], Campaign.missions[Num_campaign_missions_with_info].name, list_x1 + C_SUBTEXT_X, y, flags);
688                 }
689         }
690
691         Num_campaign_missions_with_info++;
692
693         if (Num_campaign_missions_with_info == Campaign.num_missions) {
694                 Campaign_mission_names_inited = 1;
695                 return 1;
696         } else {
697                 return 0;
698         }
699 }
700
701 // Builds list of either (1) standalone missions or (2) already played missions in current campaign
702 // First time through, it builds list, next time it uses precompiled list
703 void sim_room_build_listing()
704 {
705         int i, y;
706         int font_height = gr_get_font_height();
707         char full_filename[256];
708
709         Num_lines = y = 0;
710         list_y = Mission_list_coords[gr_screen.res][1];
711         list_h = Mission_list_coords[gr_screen.res][3];
712
713         // Stand alone single player missions.
714         if (Player->readyroom_listing_mode == MODE_MISSIONS) {
715                 if (Hash_table_inited) {
716                         if (!Standalone_mission_names_inited) {  // Is this the first time through
717                                 // build_list_do_frame builds list and adds sim room line and sets Standalone_mission_names_inited
718                                 popup_till_condition(build_standalone_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading missions", 991) );
719                         } else {
720                                 for (i=0; i<Num_standalone_missions_with_info; i++) {
721                                         if (Standalone_mission_names[i]) {
722                                                 // determine some extra information
723                                                 int flags = 0;
724                                                 memset(full_filename, 0, 256);
725                                                 strcpy(full_filename, cf_add_ext(Mission_filenames[i], FS_MISSION_FILE_EXT));
726                                                 fs_builtin_mission *fb = game_find_builtin_mission(full_filename);                                              
727                                                 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
728                                                         flags |= READYROOM_FLAG_FROM_VOLITION;
729                                                 }
730
731 #ifdef MAKE_FS1
732                                                 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
733                                                         flags |= READYROOM_FLAG_FROM_MDISK;
734                                                 }
735 #endif
736                                                 
737                                                 sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[i], Mission_filenames[i], list_x1 + M_TEXT_X, y, flags);
738                                                 y += font_height + 2;
739                                         }
740                                 }
741                         }
742                 }
743         } else {
744                 // Campaign missions
745                 list_y += font_height + 2;
746                 list_h -= font_height - 2;
747
748                 if (!Campaign_mission_names_inited) {  // Is this the first time through
749                         popup_till_condition(build_campaign_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading campaign missions",992));
750                         // builds list, adds sim room line and sets Campaign_mission_names_inited
751                 } else {
752                         for (i=0; i<Num_campaign_missions_with_info; i++) {
753                                 if (Campaign_mission_names[i]) {
754                                         // determine some extra information
755                                         int flags = 0;
756                                         memset(full_filename, 0, 256);
757                                         strcpy(full_filename, cf_add_ext(Campaign.missions[i].name, FS_MISSION_FILE_EXT));
758                                         fs_builtin_mission *fb = game_find_builtin_mission(full_filename);
759                                         if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
760                                                 flags |= READYROOM_FLAG_FROM_VOLITION;
761                                         }                                       
762
763 #ifdef MAKE_FS1
764                                         if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
765                                                 flags |= READYROOM_FLAG_FROM_MDISK;
766                                         }
767 #endif
768
769                                         sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[i], Campaign.missions[i].name, list_x1 + C_SUBTEXT_X, y, flags);
770                                         y += font_height;
771                                 }
772                         }
773                 }
774         }
775 }
776
777 int sim_room_line_query_visible(int n)
778 {
779         int y;
780
781         if ((n < 0) || (n >= Num_lines))
782                 return 0;
783         
784         y = sim_room_lines[n].y - sim_room_lines[Scroll_offset].y;
785         if ((y < 0) || (y + gr_get_font_height() > list_h))
786                 return 0;
787
788         return 1;
789 }
790
791 void sim_room_scroll_screen_up()
792 {
793         if (Player->readyroom_listing_mode != MODE_MISSIONS) {
794                 if (Scroll_offset) {
795                         Scroll_offset--;
796                         gamesnd_play_iface(SND_SCROLL);
797
798                 } else
799                         gamesnd_play_iface(SND_GENERAL_FAIL);
800
801                 return;
802         }
803
804         if (Scroll_offset) {
805                 Scroll_offset--;
806                 Assert(Selected_line > Scroll_offset);
807                 while (!sim_room_line_query_visible(Selected_line))
808                         Selected_line--;
809
810                 gamesnd_play_iface(SND_SCROLL);
811
812         } else
813                 gamesnd_play_iface(SND_GENERAL_FAIL);
814 }
815
816 void sim_room_scroll_line_up()
817 {
818         if (Selected_line) {
819                 Selected_line--;
820                 if (Selected_line < Scroll_offset)
821                         Scroll_offset = Selected_line;
822
823                 gamesnd_play_iface(SND_SCROLL);
824
825         } else
826                 gamesnd_play_iface(SND_GENERAL_FAIL);
827 }
828
829 void sim_room_scroll_screen_down()
830 {
831         if (Player->readyroom_listing_mode != MODE_MISSIONS) {
832                 if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) {
833                         Scroll_offset++;
834                         gamesnd_play_iface(SND_SCROLL);
835
836                 } else
837                         gamesnd_play_iface(SND_GENERAL_FAIL);
838
839                 return;
840         }
841
842         if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) {
843                 Scroll_offset++;
844                 while (!sim_room_line_query_visible(Selected_line)) {
845                         Selected_line++;
846                         Assert(Selected_line < Num_lines);
847                 }
848
849                 gamesnd_play_iface(SND_SCROLL);
850
851         } else
852                 gamesnd_play_iface(SND_GENERAL_FAIL);
853 }
854
855 void sim_room_scroll_line_down()
856 {
857         if (Selected_line < Num_lines - 1) {
858                 Selected_line++;
859
860                 Assert(Selected_line > Scroll_offset);
861                 while (!sim_room_line_query_visible(Selected_line))
862                         Scroll_offset++;
863
864                 gamesnd_play_iface(SND_SCROLL);
865
866         } else
867                 gamesnd_play_iface(SND_GENERAL_FAIL);
868 }
869
870 // returns: 0 = success, !0 = aborted or failed
871 int ready_room_reset_campaign()
872 {
873         int z, rval = 1;
874
875         z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110) );
876
877         if (z) {
878                 mission_campaign_savefile_delete(Campaign.filename);
879                 mission_campaign_load(Campaign.filename);
880                 rval = 0;
881         }
882
883         return rval;
884 }
885
886 // Decide if we should offer choice to resume this savegame
887 int sim_room_can_resume_savegame(char *savegame_filename)
888 {
889         #ifdef FREESPACE_SAVERESTORE_SYSTEM
890         char savegame_mission[MAX_FILENAME_LEN];
891
892         if (state_read_description(savegame_filename, NULL, savegame_mission)) {
893                 return 0;
894         }
895
896         if (stricmp(Game_current_mission_filename, savegame_mission)) {
897                 return 0;
898         }
899
900         return 1;
901         #else
902         return 0;
903         #endif
904 }
905
906 // Decide wether to resume a save game or not
907 // exit:        1       =>      savegame has been restored
908 //                      0       =>      no restore, proceed to briefing
909 //                      -1      =>      don't start mission at all
910 int sim_room_maybe_resume_savegame()
911 {
912         // MWA -- 3/26/98 -- removed all savegame references in game
913         return 0;
914
915         /*
916         char savegame_filename[_MAX_FNAME];
917         int popup_rval = -1, resume_savegame = 0;
918
919         // Generate the save-game filename for this campaign
920         memset(savegame_filename, 0, _MAX_FNAME);
921         mission_campaign_savefile_generate_root(savegame_filename);
922         strcat(savegame_filename, NOX("svg"));
923
924         // Decide if we should offer choice to resume this savegame
925         if ( sim_room_can_resume_savegame(savegame_filename) ) {
926                 popup_rval = popup(0, 3, XSTR("&Cancel",-1), XSTR("&Overwrite",-1), XSTR("&Resume",-1), XSTR("A save game for this mission exists.", -1));
927                 switch ( popup_rval ) {
928                 case 0:
929                 case -1:
930                         resume_savegame = -1;
931                         break;
932                 case 1:
933                         resume_savegame = 0;
934                         break;
935                 case 2:
936                         resume_savegame = 1;
937                         break;
938                 default:
939                         Int3();
940                         resume_savegame = -1;
941                         break;
942                 }
943         } else {
944                 resume_savegame = 0;
945         }
946
947         if (resume_savegame == 1) {
948                 if ( state_restore_all(savegame_filename) == -1 ) {
949                         popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR("Error\nSaved misison could not be loaded.\nDo you wish to start this mission from the beginning?", -1));
950                         if (popup_rval == 1) {
951                                 resume_savegame = 0;
952                         } else {
953                                 resume_savegame = -1;
954                         }
955
956                 } else {
957                         resume_savegame = 1;
958                 }
959         }
960
961         // If we are resuming this savegame, then delete the file
962         if (resume_savegame == 1) {
963                 cf_delete(savegame_filename);
964         }
965
966         return resume_savegame;
967         */
968 }
969
970 int readyroom_continue_campaign()
971 {
972         if (mission_campaign_next_mission()) {  // is campaign and next mission valid?
973
974 #if defined(FS2_DEMO) || defined(FS1_DEMO)
975                 int reset_campaign = 0;
976                 reset_campaign = popup(PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Demo Campaign Is Over.  Would you like to play the campaign again?", 111) );
977                 if ( reset_campaign == 1 ) {
978                         mission_campaign_savefile_delete(Campaign.filename);
979                         mission_campaign_load(Campaign.filename);
980                         mission_campaign_next_mission();
981                 } else {
982                         return -1;
983                 }
984 #else
985                 gamesnd_play_iface(SND_GENERAL_FAIL);
986 #ifdef MAKE_FS1
987                 // make it the bottom button so that the graphic looks right
988                 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "The campaign is over.  To replay the campaign, either create a new pilot or restart the campaign in the campaign room.", 112) );
989 #else
990                 popup(0, 1, POPUP_OK, XSTR( "The campaign is over.  To replay the campaign, either create a new pilot or restart the campaign in the campaign room.", 112) );
991 #endif
992                 return -1;
993 #endif
994         }
995
996         // CD CHECK
997         if(!game_do_cd_mission_check(Game_current_mission_filename)){           
998                 return -1;
999         }
1000
1001         // set the bit for campaign mode
1002         Game_mode |= GM_CAMPAIGN_MODE;
1003         gameseq_post_event( GS_EVENT_START_GAME );      
1004
1005         return 0;
1006 }
1007
1008 void sim_room_commit()
1009 {
1010         if ((Selected_line >= Num_lines) || !sim_room_lines[Selected_line].filename) {
1011                 gamesnd_play_iface(SND_GENERAL_FAIL);
1012                 return;
1013         }
1014
1015         strncpy(Game_current_mission_filename, sim_room_lines[Selected_line].filename, MAX_FILENAME_LEN);
1016
1017         Game_mode &= ~(GM_CAMPAIGN_MODE);                                               // be sure this bit is clear
1018
1019         // CD CHECK
1020         if(game_do_cd_mission_check(Game_current_mission_filename)){            
1021                 // don't resume savegame, proceed to briefing
1022                 gameseq_post_event(GS_EVENT_START_GAME);
1023                 gamesnd_play_iface(SND_COMMIT_PRESSED);
1024         }
1025 }
1026
1027 int sim_room_button_pressed(int n)
1028 {
1029         switch (n) {
1030                 case SCROLL_UP_BUTTON:
1031                         sim_room_scroll_screen_up();
1032                         break;
1033
1034                 case SCROLL_DOWN_BUTTON:
1035                         sim_room_scroll_screen_down();
1036                         break;
1037
1038                 case MISSION_TAB:
1039 #ifdef OEM_BUILD
1040                         game_feature_not_in_demo_popup();
1041 //                      gamesnd_play_iface(SND_GENERAL_FAIL);
1042                         break;
1043 #else
1044                         Player->readyroom_listing_mode = MODE_MISSIONS;
1045                         Selected_line = Scroll_offset = 0;
1046                         gamesnd_play_iface(SND_USER_SELECT);
1047                         sim_room_build_listing();
1048                         break;
1049 #endif
1050
1051                 case CAMPAIGN_TAB:
1052                         Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1053                         Scroll_offset = 0;
1054                         gamesnd_play_iface(SND_USER_SELECT);
1055                         sim_room_build_listing();
1056                         break;
1057
1058                 case COMMIT_BUTTON:
1059                         sim_room_commit();
1060                         break;
1061
1062                 case HELP_BUTTON:
1063                         launch_context_help();
1064                         gamesnd_play_iface(SND_HELP_PRESSED);
1065                         break;
1066
1067                 case OPTIONS_BUTTON:
1068                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1069                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1070                         return 1;
1071
1072                 case TECH_DATABASE_BUTTON:
1073                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1074                         gameseq_post_event(GS_EVENT_TECH_MENU);
1075                         return 1;
1076
1077                 case CUTSCENES_BUTTON:
1078                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1079                         gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
1080                         return 1;
1081
1082                 case CREDITS_BUTTON:
1083                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1084                         gameseq_post_event(GS_EVENT_CREDITS);
1085                         return 1;
1086         }
1087
1088         return 0;
1089 }
1090
1091 // ---------------------------------------------------------------------
1092 // mission_sim_room_init()
1093 //
1094 // Initialize the sim_room assignment screen system.  Called when GS_STATE_sim_room_SCREEN
1095 // is entered.
1096 //
1097 void sim_room_init()
1098 {
1099         int i;
1100         sim_room_buttons *b;
1101         char wild_card[256];
1102
1103         list_x1 = Mission_list_coords[gr_screen.res][0];
1104         list_x2 = Campaign_list_coords[gr_screen.res][0];
1105         list_y = Mission_list_coords[gr_screen.res][1];
1106         list_w1 = Mission_list_coords[gr_screen.res][2];
1107         list_w2 = Campaign_list_coords[gr_screen.res][2];
1108         list_h = Mission_list_coords[gr_screen.res][3];
1109
1110         // force mode to valid range.  I once had a bogus value here.  Don't know how that happened, though.
1111         if (Player->readyroom_listing_mode != MODE_MISSIONS)
1112                 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1113
1114         *Game_current_mission_filename = 0;
1115         common_set_interface_palette("InterfacePalette");  // set the interface palette
1116         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1117         Ui_window.set_mask_bmap(Sim_mask_filename[gr_screen.res]);
1118
1119         for (i=0; i<NUM_BUTTONS; i++) {
1120                 b = &Buttons[gr_screen.res][i];
1121
1122                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1123                 // set up callback for when a mouse first goes over a button
1124                 b->button.set_highlight_action(common_play_highlight_sound);
1125                 b->button.set_bmaps(b->filename);
1126                 b->button.link_hotspot(b->hotspot);
1127         }
1128
1129 #ifndef MAKE_FS1
1130         // screen/button specific text
1131         Ui_window.add_XSTR("Single Missions", 1060, Buttons[gr_screen.res][MISSION_TAB].xt, Buttons[gr_screen.res][MISSION_TAB].yt, &Buttons[gr_screen.res][MISSION_TAB].button, UI_XSTR_COLOR_GREEN);
1132         Ui_window.add_XSTR("Campaign Missions", 1061, Buttons[gr_screen.res][CAMPAIGN_TAB].xt, Buttons[gr_screen.res][CAMPAIGN_TAB].yt, &Buttons[gr_screen.res][CAMPAIGN_TAB].button, UI_XSTR_COLOR_GREEN);
1133         Ui_window.add_XSTR("Help", 928, Buttons[gr_screen.res][HELP_BUTTON].xt, Buttons[gr_screen.res][HELP_BUTTON].yt, &Buttons[gr_screen.res][HELP_BUTTON].button, UI_XSTR_COLOR_GREEN);
1134         Ui_window.add_XSTR("Commit", 1062, Buttons[gr_screen.res][COMMIT_BUTTON].xt, Buttons[gr_screen.res][COMMIT_BUTTON].yt, &Buttons[gr_screen.res][COMMIT_BUTTON].button, UI_XSTR_COLOR_PINK);
1135         Ui_window.add_XSTR("Options", 1036, Buttons[gr_screen.res][OPTIONS_BUTTON].xt, Buttons[gr_screen.res][OPTIONS_BUTTON].yt, &Buttons[gr_screen.res][OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1136         
1137         // common tab button text
1138         Ui_window.add_XSTR("Technical Database", 1055, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].xt,  Buttons[gr_screen.res][TECH_DATABASE_BUTTON].yt, &Buttons[gr_screen.res][TECH_DATABASE_BUTTON].button, UI_XSTR_COLOR_GREEN);
1139         Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[gr_screen.res][SIMULATOR_BUTTON].xt,  Buttons[gr_screen.res][SIMULATOR_BUTTON].yt, &Buttons[gr_screen.res][SIMULATOR_BUTTON].button, UI_XSTR_COLOR_GREEN);
1140         Ui_window.add_XSTR("Cutscenes", 1057, Buttons[gr_screen.res][CUTSCENES_BUTTON].xt,  Buttons[gr_screen.res][CUTSCENES_BUTTON].yt, &Buttons[gr_screen.res][CUTSCENES_BUTTON].button, UI_XSTR_COLOR_GREEN);
1141         Ui_window.add_XSTR("Credits", 1058, Buttons[gr_screen.res][CREDITS_BUTTON].xt,  Buttons[gr_screen.res][CREDITS_BUTTON].yt, &Buttons[gr_screen.res][CREDITS_BUTTON].button, UI_XSTR_COLOR_GREEN);
1142
1143         // misc text - not associated with any buttons
1144         Ui_window.add_XSTR("Mission", 1063, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][1], NULL, UI_XSTR_COLOR_GREEN);
1145         Ui_window.add_XSTR("Filename", 1064, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][1], NULL, UI_XSTR_COLOR_GREEN);
1146 #endif
1147
1148         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1149                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1150                 List_buttons[i].hide();
1151                 List_buttons[i].disable();
1152         }
1153
1154         // set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
1155         Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1156         Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1157         Buttons[gr_screen.res][COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);        
1158
1159         Background_bitmap = bm_load(Sim_filename[gr_screen.res]);
1160
1161         // load in help overlay bitmap  
1162         help_overlay_load(SIM_ROOM_OVERLAY);
1163         help_overlay_set_state(SIM_ROOM_OVERLAY,0);
1164
1165         Scroll_offset = Selected_line = 0;
1166
1167         strcpy(Cur_campaign, Player->current_campaign);
1168         mission_load_up_campaign();
1169         mission_campaign_next_mission();
1170
1171         Num_campaigns = Num_campaign_missions = 0;
1172         Get_file_list_filter = sim_room_campaign_mission_filter;
1173         memset(wild_card, 0, 256);
1174         strcpy(wild_card, NOX("*"));
1175         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
1176         Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1177
1178         Hash_table_inited = 0;
1179         if (build_campaign_mission_filename_hash_table()) {
1180                 Hash_table_inited = 1;
1181         }
1182
1183         // HACK
1184         GR_MAYBE_CLEAR_RES(Background_bitmap);
1185         if(Background_bitmap != -1){
1186                 gr_set_bitmap(Background_bitmap);
1187                 gr_bitmap(0, 0);
1188         }
1189         Ui_window.draw();
1190         gr_flip();              
1191
1192         Get_file_list_filter = sim_room_standalone_mission_filter;
1193         memset(wild_card, 0, 256);
1194         strcpy(wild_card, NOX("*"));
1195         strcat(wild_card, FS_MISSION_FILE_EXT);
1196         Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1197
1198         Num_campaign_missions_with_info = Num_standalone_missions_with_info = Standalone_mission_names_inited = Campaign_names_inited = Campaign_mission_names_inited = 0;
1199         sim_room_build_listing();
1200
1201         // load special mission icons
1202         sim_room_load_mission_icons();
1203 }
1204
1205 // ---------------------------------------------------------------------
1206 // sim_room_close()
1207 //
1208 // Cleanup the sim_room assignment screen system.  Called when GS_STATE_sim_room_SCREEN
1209 // is left.
1210 //
1211 void sim_room_close()
1212 {
1213         int i;
1214
1215         for (i=0; i<Num_campaign_missions; i++) {
1216                 if (Campaign_missions[i] != NULL) {
1217                         free(Campaign_missions[i]);
1218                         Campaign_missions[i] = NULL;
1219                 }
1220         }
1221
1222         if (Background_bitmap >= 0)
1223                 bm_unload(Background_bitmap);
1224
1225         if (Standalone_mission_names_inited){
1226                 for (i=0; i<Num_standalone_missions; i++){
1227                         if (Standalone_mission_names[i] != NULL) {
1228                                 free(Standalone_mission_names[i]);
1229                                 Standalone_mission_names[i] = NULL;
1230                         }
1231                         Standalone_mission_flags[i] = 0;
1232                 }
1233         }
1234
1235         if (Campaign_names_inited) {
1236                 for (i=0; i<Num_campaigns; i++) {
1237                         if (Campaign_names[i] != NULL) {
1238                                 free(Campaign_names[i]);
1239                                 Campaign_names[i] = NULL;
1240                         }
1241                 }
1242         }
1243
1244         if (Campaign_mission_names_inited) {
1245                 for (i=0; i<Campaign.num_missions; i++) {
1246                         if (Campaign_mission_names[i] != NULL) {
1247                                 free(Campaign_mission_names[i]);
1248                                 Campaign_mission_names[i] = NULL;
1249                         }
1250                 }
1251         }
1252
1253         for (i=0; i<Num_campaigns; i++) {
1254                 if (Campaign_file_names[i] != NULL) {
1255                         free(Campaign_file_names[i]);
1256                         Campaign_file_names[i] = NULL;
1257                 }
1258         }
1259         
1260
1261         for (i=0; i<Num_standalone_missions; i++) {
1262                 if (Mission_filenames[i] != NULL) {
1263                         free(Mission_filenames[i]);
1264                         Mission_filenames[i] = NULL;
1265                 }
1266         }
1267
1268         // unload the overlay bitmap
1269         help_overlay_unload(SIM_ROOM_OVERLAY);
1270
1271         campaign_mission_hash_table_delete();
1272
1273         Ui_window.destroy();
1274         common_free_interface_palette();                // restore game palette
1275         write_pilot_file();
1276
1277         // unload special mission icons
1278         sim_room_unload_mission_icons();
1279 }
1280
1281 // ---------------------------------------------------------------------
1282 // sim_room_do_frame()
1283 //
1284 // Called once per frame to process user input for the sim_room Assignment Screen
1285 //
1286 void sim_room_do_frame(float frametime)
1287 {
1288         char buf[256];
1289         int i, k, y, z, line;
1290         int font_height = gr_get_font_height();
1291         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released      
1292
1293         z = -1;
1294         for (i=0; i<Num_campaigns; i++)
1295                 if (!stricmp(Campaign_file_names[i], Campaign.filename)) {
1296                         z = i;
1297                         break;
1298                 }
1299
1300         if ( help_overlay_active(SIM_ROOM_OVERLAY) ) {
1301                 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1302                 Ui_window.set_ignore_gadgets(1);
1303         }
1304
1305         k = Ui_window.process() & ~KEY_DEBUGGED;
1306
1307         if ( (k > 0) || B1_JUST_RELEASED ) {
1308                 if ( help_overlay_active(SIM_ROOM_OVERLAY) ) {
1309                         help_overlay_set_state(SIM_ROOM_OVERLAY, 0);
1310                         Ui_window.set_ignore_gadgets(0);
1311                         k = 0;
1312                 }
1313         }
1314
1315         if ( !help_overlay_active(SIM_ROOM_OVERLAY) ) {
1316                 Ui_window.set_ignore_gadgets(0);
1317         }
1318
1319         switch (k) {
1320                 case KEY_DOWN:  // scroll list down
1321                         sim_room_scroll_line_down();
1322                         break;
1323
1324                 case KEY_UP:  // scroll list up
1325                         sim_room_scroll_line_up();
1326                         break;
1327
1328                 case KEY_ESC:
1329                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1330                         break;
1331
1332                 case KEY_CTRLED | KEY_UP:
1333                         sim_room_button_pressed(TECH_DATABASE_BUTTON);
1334                         break;
1335
1336                 case KEY_CTRLED | KEY_DOWN:
1337                         sim_room_button_pressed(CUTSCENES_BUTTON);
1338                         break;
1339
1340                 case KEY_TAB:
1341                         if (Player->readyroom_listing_mode == MODE_CAMPAIGNS)
1342                                 Player->readyroom_listing_mode = MODE_MISSIONS;
1343                         else
1344                                 Player->readyroom_listing_mode = MODE_CAMPAIGNS;
1345
1346                         Selected_line = Scroll_offset = 0;
1347                         gamesnd_play_iface(SND_USER_SELECT);
1348                         sim_room_build_listing();
1349                         break;
1350
1351                 case KEY_F2:
1352                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1353                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1354                         break;
1355         }       // end switch
1356
1357         for (i=0; i<NUM_BUTTONS; i++){
1358                 if (Buttons[gr_screen.res][i].button.pressed()){
1359                         if (sim_room_button_pressed(i)){
1360                                 return;
1361                         }
1362                 }
1363         }
1364
1365         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1366                 if (List_buttons[i].is_mouse_on())
1367                         select_tease_line = i + Scroll_offset;
1368         
1369                 if (List_buttons[i].pressed()) {
1370                         Selected_line = i + Scroll_offset;
1371                         gamesnd_play_iface(SND_USER_SELECT);
1372                 }
1373         }
1374
1375         GR_MAYBE_CLEAR_RES(Background_bitmap);
1376         if (Background_bitmap >= 0) {
1377                 gr_set_bitmap(Background_bitmap);
1378                 gr_bitmap(0, 0);
1379         }
1380
1381         Ui_window.draw();
1382
1383         for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
1384                 if (Buttons[gr_screen.res][i].button.button_down()){
1385                         break;
1386                 }
1387         }
1388
1389         if (i > CREDITS_BUTTON){
1390                 Buttons[gr_screen.res][SIMULATOR_BUTTON].button.draw_forced(2);
1391         }
1392
1393         if (!Buttons[gr_screen.res][CAMPAIGN_TAB].button.button_down() && !Buttons[gr_screen.res][MISSION_TAB].button.button_down()) {
1394                 if (Player->readyroom_listing_mode == MODE_CAMPAIGNS){
1395                         Buttons[gr_screen.res][CAMPAIGN_TAB].button.draw_forced(2);
1396                 } else if (Player->readyroom_listing_mode == MODE_MISSIONS){
1397                         Buttons[gr_screen.res][MISSION_TAB].button.draw_forced(2);
1398                 }
1399         }
1400
1401         gr_set_font(FONT1);
1402         if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) {
1403                 gr_set_color_fast(&Color_text_heading);
1404                 strcpy(buf, Campaign.name);
1405                 gr_force_fit_string(buf, 255, list_w1);
1406                 gr_printf(list_x1, Mission_list_coords[gr_screen.res][1], buf);
1407
1408                 if (Campaign.filename) {                        
1409                         sprintf(buf, NOX("%s%s"), Campaign.filename, FS_CAMPAIGN_FILE_EXT);
1410                         gr_force_fit_string(buf, 255, list_w2);
1411                         gr_printf(list_x2, Mission_list_coords[gr_screen.res][1], buf);         
1412
1413                         // blit the proper icons if necessary
1414                         char full_name[256];
1415                         memset(full_name, 0, 256);
1416                         strcpy(full_name, cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT));
1417                         fs_builtin_mission *fb = game_find_builtin_mission(full_name);
1418                         if(fb != NULL){
1419                                 // sim_room_blit_icons(0, Mission_list_coords[gr_screen.res][1], fb, 0);
1420                         }
1421                 }
1422         }
1423
1424         line = Scroll_offset;
1425         while (sim_room_line_query_visible(line)) {
1426                 y = list_y + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y;
1427
1428                 if (sim_room_lines[line].type != READYROOM_LINE_CAMPAIGN) {
1429                         List_buttons[line - Scroll_offset].update_dimensions(list_x1, y, list_x2 + list_w2 - list_x1, font_height);
1430                         List_buttons[line - Scroll_offset].enable();
1431
1432                 } else
1433                         List_buttons[line - Scroll_offset].disable();
1434
1435                 if (line == Selected_line)
1436                         gr_set_color_fast(&Color_text_selected);
1437                 else if (line == select_tease_line)
1438                         gr_set_color_fast(&Color_text_subselected);
1439                 else
1440                         gr_set_color_fast(&Color_text_normal);
1441
1442                 strcpy(buf, sim_room_lines[line].name);
1443                 gr_force_fit_string(buf, 255, list_x1 + list_w1 - sim_room_lines[line].x);
1444                 gr_printf(sim_room_lines[line].x, y, buf);
1445
1446                 if (sim_room_lines[line].filename) {
1447                         strcpy(buf, sim_room_lines[line].filename);
1448                         gr_force_fit_string(buf, 255, list_w2);
1449                         gr_printf(list_x2, y, buf);
1450                 }
1451
1452                 // blit additional icon information
1453                 sim_room_blit_icons(line, y);
1454
1455                 line++;
1456         }
1457
1458         i = line - Scroll_offset;
1459         while (i < LIST_BUTTONS_MAX)
1460                 List_buttons[i++].disable();
1461
1462         // blit help overlay if active
1463         help_overlay_maybe_blit(SIM_ROOM_OVERLAY);
1464
1465         gr_flip();
1466 }
1467
1468 void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb, int is_md)
1469 {
1470         int is_from_volition = 0;       
1471
1472         // determine icon status
1473         if(fb == NULL){
1474                 is_from_volition = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_VOLITION) ? 1 : 0;           
1475 #ifdef MAKE_FS1
1476                 is_md = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_MDISK) ? 1 : 0;
1477 #endif
1478         } else {
1479                 is_from_volition = (fb->flags & FSB_FROM_VOLITION) ? 1 : 0;             
1480 #ifdef MAKE_FS1
1481                 is_md = (fb->flags & FSB_FROM_MDISK) ? 1 : 0;
1482 #endif
1483         }
1484
1485         // if the line is flagged as a volition file
1486         if(is_from_volition && (Mission_icon_bitmaps[MISSION_ICON_VOLITION] >= 0)){             
1487                 gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_VOLITION]);
1488                 gr_bitmap(Sim_volition_icon_x[gr_screen.res], y_start + MISSION_ICON_VOLITION_Y_OFFSET);
1489         }       
1490
1491 #ifdef MAKE_FS1
1492         if(is_md && (Mission_icon_bitmaps[MISSION_ICON_MDISK] >= 0)){
1493                 gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_MDISK]);
1494                 gr_bitmap(Sim_silent_icon_x[gr_screen.res], y_start + MISSION_ICON_MDISK_Y_OFFSET);
1495         }
1496 #endif
1497 }
1498
1499 ///  Campaign room stuff below
1500 int Cr_list_coords[GR_NUM_RESOLUTIONS][4] = {
1501         { // GR_640
1502 #ifdef MAKE_FS1
1503                 47, 55, 561, 195
1504 #else
1505                 47, 21, 565, 233
1506 #endif
1507         },
1508         { // GR_1024
1509                 64, 34, 916, 373
1510         }
1511 };
1512
1513 int Cr_info_coords[GR_NUM_RESOLUTIONS][4] = {
1514         { // GR_640
1515 #ifdef MAKE_FS1
1516                 64, 280, 407, 75
1517 #else
1518                 28, 267, 476, 103
1519 #endif
1520         },
1521         { // GR_1024
1522                 45, 427, 761, 165
1523         },
1524 };
1525
1526 #define CR_NUM_BUTTONS                                  6
1527
1528 #define CR_SCROLL_UP_BUTTON                     0
1529 #define CR_SCROLL_DOWN_BUTTON                   1
1530 #define CR_SCROLL_INFO_UP_BUTTON                2
1531 #define CR_SCROLL_INFO_DOWN_BUTTON      3
1532 #define CR_RESET_BUTTON                                 4
1533 #define CR_COMMIT_BUTTON                                5
1534
1535 #define MAX_INFO_LINES          20
1536
1537 #define MAX_INFO_LINE_LEN       256
1538
1539 ui_button_info Cr_buttons[GR_NUM_RESOLUTIONS][CR_NUM_BUTTONS] = {
1540         { // GR_640
1541 #ifdef MAKE_FS1
1542                 ui_button_info("CAB_00",        0,              95,             -1,     -1,     0),             // scroll up
1543                 ui_button_info("CAB_01",        0,              142,    -1,     -1,     1),             // scroll down
1544                 ui_button_info("CAB_02",        17,             259,    -1,     -1,     2),             // info scroll up
1545                 ui_button_info("CAB_03",        17,             307,    -1,     -1,     3),             // info scroll down
1546                 ui_button_info("CAB_07",        545,    323,    -1,     -1,     7),             // reset
1547                 ui_button_info("CAB_04",        561,    411,    -1,     -1,     4),             // select
1548 #else
1549                 ui_button_info("CAB_00",        2,              42,     -1,     -1,     0),
1550                 ui_button_info("CAB_01",        2,              89,     -1,     -1,     1),
1551                 ui_button_info("CAB_02",        2,              279,    -1,     -1,     2),
1552                 ui_button_info("CAB_03",        2,              325,    -1,     -1,     3),
1553                 ui_button_info("CAB_04",        579,    353,    -1,     -1,     4),
1554                 ui_button_info("CAB_05",        575,    434,    -1,     -1,     5),
1555 #endif
1556         },
1557         { // GR_1024
1558                 ui_button_info("2_CAB_00",      3,              68,     -1,     -1,     0),
1559                 ui_button_info("2_CAB_01",      3,              142,    -1,     -1,     1),
1560                 ui_button_info("2_CAB_02",      3,              446,    -1,     -1,     2),
1561                 ui_button_info("2_CAB_03",      3,              520,    -1,     -1,     3),
1562                 ui_button_info("2_CAB_04",      927,    565,    -1,     -1,     4),
1563                 ui_button_info("2_CAB_05",      920,    694,    -1,     -1,     5),
1564         }
1565 };
1566
1567 // text
1568 #ifdef MAKE_FS1
1569         #define CR_NUM_TEXT                     0
1570 #else
1571         #define CR_NUM_TEXT                     3
1572 #endif
1573 UI_XSTR Cr_text[GR_NUM_RESOLUTIONS][CR_NUM_TEXT] = {
1574         { // GR_640
1575                 // not needed for FS1
1576 #ifndef MAKE_FS1
1577                 { "Restart",            1403,           569,    326, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[0][CR_RESET_BUTTON].button },
1578                 { "Campaign",           1404,           569,    337, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[0][CR_RESET_BUTTON].button },
1579                 { "Select",                     1409,           568,    413, UI_XSTR_COLOR_PINK,        -1, &Cr_buttons[0][CR_COMMIT_BUTTON].button },
1580 #endif
1581         },
1582         { // GR_1024
1583                 // not needed for FS1
1584 #ifndef MAKE_FS1
1585                 { "Restart",            1403,           922,    523, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[1][CR_RESET_BUTTON].button },
1586                 { "Campaign",           1404,           922,    538, UI_XSTR_COLOR_GREEN,       -1, &Cr_buttons[1][CR_RESET_BUTTON].button },
1587                 { "Select",                     1409,           921,    665, UI_XSTR_COLOR_PINK,        -1, &Cr_buttons[1][CR_COMMIT_BUTTON].button },
1588 #endif
1589         }
1590 };
1591
1592 static int Num_desc_lines;
1593 static int Desc_scroll_offset;
1594 static int Selected_campaign_index;
1595 static int Active_campaign_index;
1596
1597 char *Info_text_ptrs[MAX_INFO_LINES];
1598 int Num_info_lines, Info_text_line_size[MAX_INFO_LINES];
1599
1600 void campaign_room_build_listing()
1601 {
1602         int c, i, y, type, max_players; 
1603         int font_height = gr_get_font_height();
1604         char name[NAME_LENGTH];
1605
1606         Num_lines = y = 0;
1607         if (!Campaign_names_inited) {
1608                 for (i=0; i<Num_campaigns; i++) {
1609                         Campaign_names[i] = NULL;
1610                         if ( mission_campaign_get_info(Campaign_file_names[i], name, &type, &max_players) ) {
1611 #ifdef PD_BUILD
1612                                 if((game_find_builtin_mission(name) == NULL) && !strstr(name, "peterdrake")){
1613                                         continue;
1614                                 } else {
1615                                         Campaign_names[i] = strdup(name);
1616                                 }
1617 #else
1618                                 if (type == CAMPAIGN_TYPE_SINGLE){
1619                                         Campaign_names[i] = strdup(name);
1620                                 }
1621 #endif
1622                         }
1623                 }
1624
1625                 Campaign_names_inited = 1;
1626         }
1627
1628         for (c=0; c<Num_campaigns; c++) {
1629                 if (Campaign_names[c]) {
1630
1631                         // determine some extra information
1632                         int flags = 0;
1633                         fs_builtin_mission *fb = game_find_builtin_mission(Campaign_file_names[c]);
1634                         if(fb != NULL){
1635                                 if(fb->flags & FSB_FROM_VOLITION){
1636                                         flags |= READYROOM_FLAG_FROM_VOLITION;
1637                                 }                               
1638 #ifdef MAKE_FS1
1639                                 if(fb->flags & FSB_FROM_MDISK){
1640                                         flags |= READYROOM_FLAG_FROM_MDISK;
1641                                 }
1642 #endif
1643                         }
1644
1645                         sim_room_line_add(READYROOM_LINE_CAMPAIGN, Campaign_names[c], Campaign_file_names[c], Cr_list_coords[gr_screen.res][0], y, flags);
1646                         y += font_height + 2;
1647                 }
1648         }
1649 }
1650
1651 void set_new_campaign_line(int n)
1652 {
1653         char *str;
1654
1655         Selected_campaign_index = n;
1656         str = Campaign_descs[Selected_campaign_index];
1657         Num_info_lines = 0;
1658         if (str) {
1659                 Num_info_lines = split_str(str, Cr_info_coords[gr_screen.res][2], Info_text_line_size, Info_text_ptrs, MAX_INFO_LINES);
1660                 Assert(Num_info_lines >= 0);
1661         }
1662
1663         Desc_scroll_offset = 0;
1664 }
1665
1666 void campaign_room_scroll_info_up()
1667 {
1668         if (Desc_scroll_offset) {
1669                 Desc_scroll_offset--;
1670                 gamesnd_play_iface(SND_SCROLL);
1671
1672         } else
1673                 gamesnd_play_iface(SND_GENERAL_FAIL);
1674 }
1675
1676 void campaign_room_scroll_info_down()
1677 {
1678         if ( (Num_info_lines - Desc_scroll_offset) * gr_get_font_height() > Cr_info_coords[gr_screen.res][3]) {
1679                 Desc_scroll_offset++;
1680                 gamesnd_play_iface(SND_SCROLL);
1681
1682         } else
1683                 gamesnd_play_iface(SND_GENERAL_FAIL);
1684 }
1685
1686 // returns: 0 = success, !0 = aborted or failed
1687 int campaign_room_reset_campaign(int n)
1688 {
1689         char *filename;
1690         int z;
1691
1692         // z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110), Campaign_names[n]);
1693         z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110));
1694         if (z == 1) {
1695                 filename = (char *) malloc(strlen(Campaign_file_names[n]) + 5);
1696                 strcpy(filename, Campaign_file_names[n]);
1697                 strcat(filename, FS_CAMPAIGN_FILE_EXT);
1698
1699                 mission_campaign_savefile_delete(filename);
1700                 mission_campaign_load(filename);
1701                 mission_campaign_next_mission();
1702                 return 0;
1703         }
1704
1705         return 1;
1706 }
1707
1708 void campaign_room_commit()
1709 {
1710         if (Selected_campaign_index < 0) {
1711                 gamesnd_play_iface(SND_GENERAL_FAIL);
1712                 return;
1713         }
1714
1715         if (stricmp(Campaign_file_names[Selected_campaign_index], Campaign.filename)) {  // new campaign selected
1716                 if ((Active_campaign_index >= 0) && campaign_room_reset_campaign(Active_campaign_index)) {
1717                         gamesnd_play_iface(SND_GENERAL_FAIL);
1718                         return;
1719                 }
1720
1721                 mission_campaign_savefile_delete(Campaign_file_names[Selected_campaign_index]);
1722                 mission_campaign_load(Campaign_file_names[Selected_campaign_index]);
1723                 strcpy(Player->current_campaign, Campaign.filename);  // track new campaign for player
1724         }
1725
1726         if (mission_campaign_next_mission()) {  // is campaign and next mission valid?
1727                 gamesnd_play_iface(SND_GENERAL_FAIL);
1728                 return;
1729         }
1730
1731         gameseq_post_event(GS_EVENT_MAIN_MENU);
1732         gamesnd_play_iface(SND_COMMIT_PRESSED);
1733 }
1734
1735 int campaign_room_button_pressed(int n)
1736 {
1737         switch (n) {
1738                 case CR_SCROLL_UP_BUTTON:
1739                         sim_room_scroll_screen_up();
1740                         break;
1741
1742                 case CR_SCROLL_DOWN_BUTTON:
1743                         sim_room_scroll_screen_down();
1744                         break;
1745
1746                 case CR_SCROLL_INFO_UP_BUTTON:
1747                         campaign_room_scroll_info_up();
1748                         break;
1749
1750                 case CR_SCROLL_INFO_DOWN_BUTTON:
1751                         campaign_room_scroll_info_down();
1752                         break;
1753
1754                 case CR_COMMIT_BUTTON:
1755                         campaign_room_commit();
1756                         break;
1757
1758                 /*
1759                 case CR_HELP_BUTTON:
1760                         launch_context_help();
1761                         gamesnd_play_iface(SND_HELP_PRESSED);
1762                         break;
1763
1764                 case CR_OPTIONS_BUTTON:
1765                         gamesnd_play_iface(SND_SWITCH_SCREENS);
1766                         gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1767                         return 1;
1768                 */
1769
1770                 case CR_RESET_BUTTON:
1771                         if ( (Active_campaign_index < 0) || (Active_campaign_index >= Num_campaigns) )
1772                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1773                         else if (campaign_room_reset_campaign(Active_campaign_index))
1774                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1775                         else
1776                                 gamesnd_play_iface(SND_USER_SELECT);
1777
1778                         break;
1779         }
1780
1781         return 0;
1782 }
1783
1784 void campaign_room_init()
1785 {
1786         int i, j, load_failed;
1787         ui_button_info *b;
1788         char wild_card[256];
1789
1790         list_h = Mission_list_coords[gr_screen.res][3];
1791
1792 #ifdef MAKE_FS1
1793         common_set_interface_palette("InterfacePalette");  // set the interface palette
1794 #endif
1795         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1796         Ui_window.set_mask_bmap(Campaign_mask_filename[gr_screen.res]);
1797
1798         for (i=0; i<CR_NUM_BUTTONS; i++) {
1799                 b = &Cr_buttons[gr_screen.res][i];
1800
1801                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
1802                 // set up callback for when a mouse first goes over a button
1803                 b->button.set_highlight_action(common_play_highlight_sound);
1804                 b->button.set_bmaps(b->filename);
1805                 b->button.link_hotspot(b->hotspot);
1806         }
1807
1808         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1809                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1810                 List_buttons[i].hide();
1811                 List_buttons[i].disable();
1812         }
1813
1814         // add xstrs
1815         for(i=0; i<CR_NUM_TEXT; i++){
1816                 Ui_window.add_XSTR(&Cr_text[gr_screen.res][i]);
1817         }
1818
1819         // set up sim_rooms for buttons so we draw the correct animation frame when a key is pressed
1820         Cr_buttons[gr_screen.res][CR_SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
1821         Cr_buttons[gr_screen.res][CR_SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);
1822         Cr_buttons[gr_screen.res][CR_RESET_BUTTON].button.set_hotkey(KEY_DELETE);
1823         Cr_buttons[gr_screen.res][CR_COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
1824         // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.set_hotkey(KEY_F2);
1825
1826         Background_bitmap = bm_load(Campaign_filename[gr_screen.res]);
1827
1828         // load in help overlay bitmap  
1829         help_overlay_load(CAMPAIGN_ROOM_OVERLAY);
1830         help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY,0);
1831
1832         Num_desc_lines = 0;
1833
1834         Desc_scroll_offset = Scroll_offset = 0;
1835         load_failed = mission_load_up_campaign();
1836         if (!load_failed)
1837                 mission_campaign_next_mission();
1838
1839         else {
1840                 Campaign.filename[0] = 0;
1841                 Campaign.num_missions = 0;
1842         }
1843
1844         Num_campaigns = 0;
1845
1846         Get_file_list_filter = campaign_room_campaign_filter;
1847         memset(wild_card, 0, 256);
1848         strcpy(wild_card, NOX("*"));
1849         strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
1850         Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME);
1851
1852         for (i=0; i<Num_campaigns; i++) {
1853                 for (j=0; j<Num_campaigns; j++) {
1854                         if (Campaign_file_names_temp[j]) {
1855                                 if (!strncmp(Campaign_file_names[i], Campaign_file_names_temp[j], strlen(Campaign_file_names_temp[j]) - 4)) {
1856                                         Campaign_descs[i] = Campaign_descs_temp[j];
1857                                         free(Campaign_file_names_temp[j]);
1858                                         Campaign_file_names_temp[j] = NULL;
1859                                         break;
1860                                 }
1861                         }
1862                 }
1863
1864                 Assert(j < Num_campaigns);  // Campaign not found?  How is that possible?
1865         }
1866
1867         Campaign_names_inited = 0;
1868         campaign_room_build_listing();
1869
1870         Selected_campaign_index = Active_campaign_index = -1;
1871         if (!load_failed) {
1872                 for (i=0; i<Num_campaigns; i++)
1873                         if (!stricmp(Campaign_file_names[i], Campaign.filename)) {
1874                                 set_new_campaign_line(i);
1875                                 Active_campaign_index = i;
1876                                 break;
1877                         }
1878         }
1879 }
1880
1881 void campaign_room_close()
1882 {
1883         int i;
1884
1885         for (i=0; i<Num_campaigns; i++)
1886                 if (Campaign_descs[i])
1887                         free(Campaign_descs[i]);
1888
1889         if (Background_bitmap >= 0)
1890                 bm_unload(Background_bitmap);
1891
1892         if (Campaign_names_inited)
1893                 for (i=0; i<Num_campaigns; i++)
1894                         if (Campaign_names[i])
1895                                 free(Campaign_names[i]);
1896
1897         for (i=0; i<Num_campaigns; i++)
1898                 free(Campaign_file_names[i]);
1899
1900         // unload the overlay bitmap
1901         help_overlay_unload(CAMPAIGN_ROOM_OVERLAY);
1902
1903         Ui_window.destroy();
1904         common_free_interface_palette();                // restore game palette
1905         write_pilot_file();
1906 }
1907
1908 void campaign_room_do_frame(float frametime)
1909 {
1910         char buf[256];
1911         char line_text[MAX_INFO_LINE_LEN];
1912         int i, k, y, line;
1913         int font_height = gr_get_font_height();
1914         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1915
1916         if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1917                 // Cr_buttons[gr_screen.res][CR_HELP_BUTTON].button.reset_status();
1918                 Ui_window.set_ignore_gadgets(1);
1919         }
1920
1921         k = Ui_window.process() & ~KEY_DEBUGGED;
1922
1923         if ( (k > 0) || B1_JUST_RELEASED ) {
1924                 if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1925                         help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY, 0);
1926                         Ui_window.set_ignore_gadgets(0);
1927                         k = 0;
1928                 }
1929         }
1930
1931         if ( !help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) {
1932                 Ui_window.set_ignore_gadgets(0);
1933         }
1934
1935         switch (k) {
1936                 case KEY_DOWN:  // scroll list down
1937                         if (Selected_campaign_index < Num_campaigns - 1) {
1938                                 set_new_campaign_line(Selected_campaign_index + 1);
1939                                 gamesnd_play_iface(SND_SCROLL);
1940
1941                         } else
1942                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1943
1944                         break;
1945
1946                 case KEY_UP:  // scroll list up
1947                         if (Selected_campaign_index < 0)
1948                                 Selected_campaign_index = 1;
1949
1950                         if (Selected_campaign_index) {
1951                                 set_new_campaign_line(Selected_campaign_index - 1);
1952                                 gamesnd_play_iface(SND_SCROLL);
1953
1954                         } else
1955                                 gamesnd_play_iface(SND_GENERAL_FAIL);
1956
1957                         break;
1958
1959                 case KEY_ESC:
1960                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1961                         break;
1962         }       // end switch
1963
1964         for (i=0; i<CR_NUM_BUTTONS; i++){
1965                 if (Cr_buttons[gr_screen.res][i].button.pressed()){
1966                         if (campaign_room_button_pressed(i)){
1967                                 return;
1968                         }
1969                 }
1970         }
1971
1972         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1973                 if (List_buttons[i].is_mouse_on()){
1974                         select_tease_line = i + Scroll_offset;
1975                 }
1976         
1977                 if (List_buttons[i].pressed()) {
1978                         set_new_campaign_line(i + Scroll_offset);
1979                         gamesnd_play_iface(SND_USER_SELECT);
1980                 }
1981         }
1982
1983         GR_MAYBE_CLEAR_RES(Background_bitmap);
1984         if (Background_bitmap >= 0) {
1985                 gr_set_bitmap(Background_bitmap);
1986                 gr_bitmap(0, 0);
1987         }
1988
1989         Ui_window.draw();
1990
1991         gr_set_font(FONT1);
1992         line = Scroll_offset;
1993         while (sim_room_line_query_visible(line)) {
1994                 y = Cr_list_coords[gr_screen.res][1] + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y;
1995
1996                 List_buttons[line - Scroll_offset].update_dimensions(Cr_list_coords[gr_screen.res][0], y, Cr_list_coords[gr_screen.res][2], font_height);
1997                 List_buttons[line - Scroll_offset].enable();
1998
1999                 if (!stricmp(sim_room_lines[line].filename, Campaign.filename)) {
2000                         gr_set_color_fast(&Color_white);
2001                         i = y + font_height / 2 - 1;
2002                         gr_circle(Cr_list_coords[gr_screen.res][0] - 6, i, 5);
2003
2004                         gr_set_color_fast(&Color_bright_white);
2005                         gr_line(Cr_list_coords[gr_screen.res][0] - 10, i, Cr_list_coords[gr_screen.res][0] - 8, i);
2006                         gr_line(Cr_list_coords[gr_screen.res][0] - 6, i - 4, Cr_list_coords[gr_screen.res][0] - 6, i - 2);
2007                         gr_line(Cr_list_coords[gr_screen.res][0] - 4, i, Cr_list_coords[gr_screen.res][0] - 2, i);
2008                         gr_line(Cr_list_coords[gr_screen.res][0] - 6, i + 2, Cr_list_coords[gr_screen.res][0] - 6, i + 4);
2009                 }
2010
2011                 if (line == Selected_campaign_index)
2012                         gr_set_color_fast(&Color_text_selected);
2013                 else if (line == select_tease_line)
2014                         gr_set_color_fast(&Color_text_subselected);
2015                 else
2016                         gr_set_color_fast(&Color_text_normal);
2017
2018                 strcpy(buf, sim_room_lines[line].name);
2019                 gr_force_fit_string(buf, 255, Cr_list_coords[gr_screen.res][0] + Cr_list_coords[gr_screen.res][2] - sim_room_lines[line].x);
2020                 gr_printf(sim_room_lines[line].x, y, buf);
2021                 line++;
2022         }
2023
2024         i = line - Scroll_offset;
2025         while (i < LIST_BUTTONS_MAX)
2026                 List_buttons[i++].disable();
2027
2028         y = 0;
2029         i = Desc_scroll_offset;
2030         gr_set_color_fast(&Color_text_normal);
2031
2032         while (y + font_height <= Cr_info_coords[gr_screen.res][3]) {
2033                 if (i >= Num_info_lines)
2034                         break;
2035
2036                 Assert(Info_text_line_size[i] < MAX_INFO_LINE_LEN);
2037                 strncpy(line_text, Info_text_ptrs[i], Info_text_line_size[i]);
2038                 line_text[Info_text_line_size[i]] = 0;
2039                 drop_white_space(line_text);
2040                 gr_string(Cr_info_coords[gr_screen.res][0], Cr_info_coords[gr_screen.res][1] + y, line_text);
2041                 y += font_height;
2042                 i++;
2043         }
2044
2045         // blit help overlay if active
2046         help_overlay_maybe_blit(CAMPAIGN_ROOM_OVERLAY);
2047
2048         gr_flip();
2049 }
2050
2051 void sim_room_load_mission_icons()
2052 {
2053         int idx;
2054         
2055         // load all bitmaps
2056         for(idx=0; idx<NUM_MISSION_ICONS; idx++){
2057                 Mission_icon_bitmaps[idx] = -1;
2058                 Mission_icon_bitmaps[idx] = bm_load(Mission_icon_bitmap_filenames[idx]);
2059         }
2060 }
2061
2062 void sim_room_unload_mission_icons()
2063 {
2064         int idx;
2065
2066         // unload all bitmaps
2067         for(idx=0; idx<NUM_MISSION_ICONS; idx++){
2068                 if(Mission_icon_bitmaps[idx] >= 0){
2069                         bm_unload(Mission_icon_bitmaps[idx]);
2070                         Mission_icon_bitmaps[idx] = -1;
2071                 }
2072         }
2073 }
2074