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