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