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