1 #include "Button_System.h"
2 #include "Directories.h"
3 #include "FileMan.h"
4 #include "Font.h"
5 #include "Font_Control.h"
6 #include "HImage.h"
7 #include "Local.h"
8 #include "RenderWorld.h"
9 #include "Render_Dirty.h"
10 #include "LoadScreen.h"
11 #include "SelectWin.h"
12 #include "EditorDefines.h"
13 #include "MessageBox.h"
14 #include "Text_Input.h"
15 #include "Soldier_Create.h"
16 #include "Soldier_Init_List.h"
17 #include "EditorBuildings.h"
18 #include "Editor_Taskbar_Utils.h"
19 #include "Editor_Undo.h"
20 #include "EditScreen.h"
21 #include "StrategicMap.h"
22 #include "Editor_Modes.h"
23 #include "Map_Information.h"
24 #include "Sys_Globals.h"
25 #include "Sector_Summary.h"
26 #include "NewSmooth.h"
27 #include "Simple_Render_Utils.h"
28 #include "Animated_ProgressBar.h"
29 #include "EditorMercs.h"
30 #include "Lighting.h"
31 #include "EditorMapInfo.h"
32 #include "Environment.h"
33 #include "Edit_Sys.h"
34 #include "EditorItems.h"
35 #include "English.h"
36 #include "GameLoop.h"
37 #include "Message.h"
38 #include "Pits.h"
39 #include "Item_Statistics.h"
40 #include "Scheduling.h"
41 #include "Debug.h"
42 #include "JAScreens.h"
43 #include "MemMan.h"
44 #include "MessageBoxScreen.h"
45 #include "Timer_Control.h"
46 #include "VObject.h"
47 #include "VSurface.h"
48 #include "Video.h"
49 #include "WorldDef.h"
50 #include "UILayout.h"
51 #include "GameState.h"
52 #include "GameRes.h"
53
54 #include "ContentManager.h"
55 #include "GameInstance.h"
56
57 #include <string_theory/format>
58 #include <string_theory/string>
59
60 #include <cstdarg>
61
62
63 static BOOLEAN gfErrorCatch = FALSE;
64 static ST::string gzErrorCatchString;
65
66
SetErrorCatchString(const ST::string & str)67 void SetErrorCatchString(const ST::string& str)
68 {
69 gzErrorCatchString = str;
70 gfErrorCatch = TRUE;
71 }
72
73
74 enum{
75 DIALOG_NONE,
76 DIALOG_SAVE,
77 DIALOG_LOAD,
78 DIALOG_CANCEL,
79 DIALOG_DELETE
80 };
81
82 static INT32 iTotalFiles;
83 static INT32 iTopFileShown;
84 static INT32 iCurrFileShown;
85 static INT32 iLastFileClicked;
86 static INT32 iLastClickTime;
87
88 static ST::string gzFilename;
89
90 static FDLG_LIST* FileList = NULL;
91
92 static INT32 iFDlgState = DIALOG_NONE;
93 static GUIButtonRef iFileDlgButtons[7];
94
95 static BOOLEAN gfLoadError;
96 static bool gfReadOnly;
97 static BOOLEAN gfFileExists;
98 static BOOLEAN gfIllegalName;
99 static BOOLEAN gfDeleteFile;
100 static BOOLEAN gfNoFiles;
101
102 static BOOLEAN fEnteringLoadSaveScreen = TRUE;
103
104 static MOUSE_REGION BlanketRegion;
105
106 static ST::string gMapFileForRemoval;
107
108 enum{
109 IOSTATUS_NONE,
110 INITIATE_MAP_SAVE,
111 SAVING_MAP,
112 INITIATE_MAP_LOAD,
113 LOADING_MAP
114 };
115 static INT8 gbCurrentFileIOStatus; // 1 init saving message, 2 save, 3 init loading message, 4 load, 0 none
116
117 static bool gfUpdateSummaryInfo = true;
118
119
120 static void CreateFileDialog(const ST::string& zTitle);
121 static void TrashFDlgList(FDLG_LIST*);
122
123
LoadSaveScreenEntry(void)124 static void LoadSaveScreenEntry(void)
125 {
126 fEnteringLoadSaveScreen = FALSE;
127 gbCurrentFileIOStatus = IOSTATUS_NONE;
128
129 gfReadOnly = false;
130 gfFileExists = FALSE;
131 gfLoadError = FALSE;
132 gfIllegalName = FALSE;
133 gfDeleteFile = FALSE;
134 gfNoFiles = FALSE;
135
136 // setup filename dialog box
137 // (*.dat and *.map) as file filter
138
139 // If user clicks on a filename in the window, then turn off string focus and re-init the string with the new name.
140 // If user hits ENTER or presses OK, then continue with the file loading/saving
141
142 if( FileList )
143 TrashFDlgList( FileList );
144
145 iTopFileShown = iTotalFiles = 0;
146 try
147 {
148 std::vector<ST::string> files = GCM->getAllMaps();
149 for (const ST::string &file : files)
150 {
151 FileList = AddToFDlgList(FileList, file.c_str());
152 ++iTotalFiles;
153 }
154 }
155 catch (...) { /* XXX ignore */ }
156
157 gzFilename = ST::format("{}", g_filename);
158
159 CreateFileDialog(iCurrentAction == ACTION_SAVE_MAP ? "Save Map (*.dat)" : "Load Map (*.dat)");
160
161 if( !iTotalFiles )
162 {
163 gfNoFiles = TRUE;
164 if( iCurrentAction == ACTION_LOAD_MAP )
165 DisableButton( iFileDlgButtons[0] );
166 }
167
168 iLastFileClicked = -1;
169 iLastClickTime = 0;
170
171 }
172
173
174 static void RemoveFileDialog(void);
175 static void RemoveFromFDlgList(FDLG_LIST** head, FDLG_LIST* node);
176 static BOOLEAN ValidFilename(void);
177
178
ProcessLoadSaveScreenMessageBoxResult(void)179 static ScreenID ProcessLoadSaveScreenMessageBoxResult(void)
180 {
181 FDLG_LIST *curr, *temp;
182 gfRenderWorld = TRUE;
183 RemoveMessageBox();
184 if( gfIllegalName )
185 {
186 fEnteringLoadSaveScreen = TRUE;
187 RemoveFileDialog();
188 MarkWorldDirty();
189 return gfMessageBoxResult ? LOADSAVE_SCREEN : EDIT_SCREEN;
190 }
191 if( gfDeleteFile )
192 {
193 if( gfMessageBoxResult )
194 { //delete file
195 INT32 x;
196 curr = FileList;
197 for( x = 0; x < iCurrFileShown && x < iTotalFiles && curr; x++ )
198 {
199 curr = curr->pNext;
200 }
201 if( curr )
202 {
203 FileDelete(gMapFileForRemoval);
204
205 //File is deleted so redo the text fields so they show the
206 //next file in the list.
207 temp = curr->pNext;
208 if( !temp )
209 temp = curr->pPrev;
210 if( !temp )
211 gzFilename = ST::null;
212 else
213 gzFilename = ST::format("{}", temp->filename);
214 if (!ValidFilename()) gzFilename = ST::null;
215 SetInputFieldString(0, gzFilename);
216 RemoveFromFDlgList( &FileList, curr );
217 iTotalFiles--;
218 if( !iTotalFiles )
219 {
220 gfNoFiles = TRUE;
221 if( iCurrentAction == ACTION_LOAD_MAP )
222 DisableButton( iFileDlgButtons[0] );
223 }
224 if( iCurrFileShown >= iTotalFiles )
225 iCurrFileShown--;
226 if( iCurrFileShown < iTopFileShown )
227 iTopFileShown -= 8;
228 if( iTopFileShown < 0 )
229 iTopFileShown = 0;
230 }
231 }
232 MarkWorldDirty();
233 RenderWorld();
234 gfDeleteFile = FALSE;
235 iFDlgState = DIALOG_NONE;
236 return LOADSAVE_SCREEN;
237 }
238 if( gfLoadError )
239 {
240 fEnteringLoadSaveScreen = TRUE;
241 return gfMessageBoxResult ? LOADSAVE_SCREEN : EDIT_SCREEN;
242 }
243 if( gfReadOnly )
244 { //file is readonly. Result will determine if the file dialog stays up.
245 fEnteringLoadSaveScreen = TRUE;
246 RemoveFileDialog();
247 return gfMessageBoxResult ? LOADSAVE_SCREEN : EDIT_SCREEN;
248 }
249 if( gfFileExists )
250 {
251 if( gfMessageBoxResult )
252 { //okay to overwrite file
253 RemoveFileDialog();
254 gbCurrentFileIOStatus = INITIATE_MAP_SAVE;
255 return LOADSAVE_SCREEN;
256 }
257 fEnteringLoadSaveScreen = TRUE;
258 RemoveFileDialog();
259 return EDIT_SCREEN ;
260 }
261 SLOGA("ProcessLoadSaveScreenMessageBoxResult: none of the global flags set");
262 return LOADSAVE_SCREEN;
263 }
264
265
266 static void DrawFileDialog(void);
267 static BOOLEAN ExtractFilenameFromFields(void);
268 static void HandleMainKeyEvents(InputAtom* pEvent);
269 static ScreenID ProcessFileIO(void);
270
271
LoadSaveScreenHandle(void)272 ScreenID LoadSaveScreenHandle(void)
273 {
274 FDLG_LIST *FListNode;
275 INT32 x;
276 InputAtom DialogEvent;
277 ST::string zOrigName;
278
279 if( fEnteringLoadSaveScreen )
280 {
281 LoadSaveScreenEntry();
282 }
283
284 if( gbCurrentFileIOStatus ) //loading or saving map
285 {
286 ScreenID const uiScreen = ProcessFileIO();
287 if( uiScreen == EDIT_SCREEN && gbCurrentFileIOStatus == LOADING_MAP )
288 RemoveProgressBar( 0 );
289 return uiScreen;
290 }
291
292 if( gubMessageBoxStatus )
293 {
294 if( MessageBoxHandled() )
295 return ProcessLoadSaveScreenMessageBoxResult();
296 return LOADSAVE_SCREEN;
297 }
298
299 //handle all key input.
300 while( DequeueEvent(&DialogEvent) )
301 {
302 if( !HandleTextInput(&DialogEvent) && (DialogEvent.usEvent == KEY_DOWN || DialogEvent.usEvent == KEY_REPEAT) )
303 {
304 HandleMainKeyEvents( &DialogEvent );
305 }
306 }
307
308 DrawFileDialog();
309
310 // Skip to first filename to show
311 FListNode = FileList;
312 for(x=0;x<iTopFileShown && x<iTotalFiles && FListNode != NULL;x++)
313 {
314 FListNode = FListNode->pNext;
315 }
316
317 // Show up to 8 filenames in the window
318 SetFont( FONT12POINT1 );
319 if( gfNoFiles )
320 {
321 SetFontForeground( FONT_LTRED );
322 SetFontBackground( 142 );
323 MPrint(226, 126, "NO FILES IN /MAPS DIRECTORY");
324 }
325 else for(x=iTopFileShown;x<(iTopFileShown+8) && x<iTotalFiles && FListNode != NULL; x++)
326 {
327 if( !EditingText() && x == iCurrFileShown )
328 {
329 SetFontForeground( FONT_GRAY2 );
330 SetFontBackground( FONT_METALGRAY );
331 }
332 else
333 {
334 SetFontForeground( FONT_BLACK );
335 SetFontBackground( 142 );
336 }
337 MPrint(186, 73 + (x - iTopFileShown) * 15, ST::format("{}", FListNode->filename));
338 FListNode = FListNode->pNext;
339 }
340
341 RenderAllTextFields();
342
343 InvalidateScreen();
344
345 ExecuteBaseDirtyRectQueue();
346
347 switch( iFDlgState )
348 {
349 case DIALOG_CANCEL:
350 RemoveFileDialog();
351 fEnteringLoadSaveScreen = TRUE;
352 return EDIT_SCREEN;
353
354 case DIALOG_DELETE:
355 {
356 gMapFileForRemoval = GCM->getMapPath(gzFilename);
357 bool readonly = false;
358 if (Fs_getReadOnly(gMapFileForRemoval.c_str(), &readonly))
359 {
360 ST::string str;
361 if (readonly)
362 {
363 str = ST::format(" Delete READ-ONLY file {}? ", gzFilename);
364 }
365 else
366 str = ST::format(" Delete file {}? ", gzFilename);
367 gfDeleteFile = TRUE;
368 CreateMessageBox( str );
369 }
370 return LOADSAVE_SCREEN;
371 }
372
373 case DIALOG_SAVE:
374 {
375 if( !ExtractFilenameFromFields() )
376 {
377 CreateMessageBox( " Illegal filename. Try another filename? " );
378 gfIllegalName = TRUE;
379 iFDlgState = DIALOG_NONE;
380 return LOADSAVE_SCREEN;
381 }
382 ST::string filename(GCM->getMapPath(gzFilename));
383 if ( GCM->doesGameResExists(filename.c_str()) )
384 {
385 gfFileExists = TRUE;
386 gfReadOnly = false;
387 Fs_getReadOnly(filename.c_str(), &gfReadOnly);
388 if( gfReadOnly )
389 CreateMessageBox( " File is read only! Choose a different name? " );
390 else
391 CreateMessageBox( " File exists, Overwrite? " );
392 return( LOADSAVE_SCREEN );
393 }
394 RemoveFileDialog();
395 gbCurrentFileIOStatus = INITIATE_MAP_SAVE;
396 return LOADSAVE_SCREEN ;
397 }
398 case DIALOG_LOAD:
399 if( !ExtractFilenameFromFields() )
400 {
401 CreateMessageBox( " Illegal filename. Try another filename? " );
402 gfIllegalName = TRUE;
403 iFDlgState = DIALOG_NONE;
404 return LOADSAVE_SCREEN;
405 }
406 RemoveFileDialog();
407 CreateProgressBar(0, 118, 183, 404, 19);
408 DefineProgressBarPanel( 0, 65, 79, 94, 100, 155, 540, 235 );
409 zOrigName = ST::format("Loading map: {}", gzFilename);
410 SetProgressBarTitle( 0, zOrigName, BLOCKFONT2, FONT_RED, FONT_NEARBLACK );
411 gbCurrentFileIOStatus = INITIATE_MAP_LOAD;
412 return LOADSAVE_SCREEN ;
413 default:
414 iFDlgState = DIALOG_NONE;
415 }
416 iFDlgState = DIALOG_NONE;
417 return LOADSAVE_SCREEN ;
418 }
419
420
MakeButtonArrow(const char * const gfx,const INT16 y,const GUI_CALLBACK click)421 static GUIButtonRef MakeButtonArrow(const char* const gfx, const INT16 y, const GUI_CALLBACK click)
422 {
423 GUIButtonRef const btn = QuickCreateButtonImg(gfx, -1, 1, 2, 3, 4, 426, y, MSYS_PRIORITY_HIGH, click);
424 btn->SpecifyDisabledStyle(GUI_BUTTON::DISABLED_STYLE_SHADED);
425 return btn;
426 }
427
428
429 static void FDlgCancelCallback(GUI_BUTTON* butn, INT32 reason);
430 static void FDlgDwnCallback(GUI_BUTTON* butn, INT32 reason);
431 static void FDlgNamesCallback(GUI_BUTTON* butn, INT32 reason);
432 static void FDlgOkCallback(GUI_BUTTON* butn, INT32 reason);
433 static void FDlgUpCallback(GUI_BUTTON* butn, INT32 reason);
434 static void FileDialogModeCallback(UINT8 ubID, BOOLEAN fEntering);
435 static void UpdateWorldInfoCallback(GUI_BUTTON* b, INT32 reason);
436
437
CreateFileDialog(const ST::string & zTitle)438 static void CreateFileDialog(const ST::string& zTitle)
439 {
440
441 iFDlgState = DIALOG_NONE;
442
443 DisableEditorTaskbar();
444
445 MSYS_DefineRegion( &BlanketRegion, 0, 0, gsVIEWPORT_END_X, gsVIEWPORT_END_Y, MSYS_PRIORITY_HIGH - 5, 0, 0, 0 );
446
447 //Okay and cancel buttons
448 iFileDlgButtons[0] = CreateTextButton("Okay", FONT12POINT1, FONT_BLACK, FONT_BLACK, 354, 225, 50, 30, MSYS_PRIORITY_HIGH, FDlgOkCallback);
449 iFileDlgButtons[1] = CreateTextButton("Cancel", FONT12POINT1, FONT_BLACK, FONT_BLACK, 406, 225, 50, 30, MSYS_PRIORITY_HIGH, FDlgCancelCallback);
450
451 //Scroll buttons
452 iFileDlgButtons[2] = MakeButtonArrow(EDITORDIR "/uparrow.sti", 92, FDlgUpCallback);
453 iFileDlgButtons[3] = MakeButtonArrow(EDITORDIR "/downarrow.sti", 182, FDlgDwnCallback);
454
455 //File list window
456 iFileDlgButtons[4] = CreateHotSpot(179 + 4, 69 + 3, 179 + 4 + 240, 69 + 120 + 3, MSYS_PRIORITY_HIGH - 1, FDlgNamesCallback);
457 //Title button
458 iFileDlgButtons[5] = CreateLabel(zTitle, HUGEFONT, FONT_LTKHAKI, FONT_DKKHAKI, 179, 39, 281, 30, MSYS_PRIORITY_HIGH - 2);
459
460 if( iCurrentAction == ACTION_SAVE_MAP )
461 { //checkboxes
462 //The update world info checkbox
463 iFileDlgButtons[6] = CreateCheckBoxButton( 183, 229, EDITORDIR "/smcheckbox.sti", MSYS_PRIORITY_HIGH, UpdateWorldInfoCallback );
464 if( gfUpdateSummaryInfo )
465 iFileDlgButtons[6]->uiFlags |= BUTTON_CLICKED_ON;
466 }
467
468 //Add the text input fields
469 InitTextInputModeWithScheme( DEFAULT_SCHEME );
470 //field 1 (filename)
471 AddTextInputField(/*233*/183, 195, 190, 20, MSYS_PRIORITY_HIGH, gzFilename, 30, INPUTTYPE_DOSFILENAME);
472 //field 2 -- user field that allows mouse/key interaction with the filename list
473 AddUserInputField( FileDialogModeCallback );
474 }
475
476
UpdateWorldInfoCallback(GUI_BUTTON * b,INT32 reason)477 static void UpdateWorldInfoCallback(GUI_BUTTON* b, INT32 reason)
478 {
479 if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
480 gfUpdateSummaryInfo = b->Clicked();
481 }
482
483
484 //This is a hook into the text input code. This callback is called whenever the user is currently
485 //editing text, and presses Tab to transfer to the file dialog mode. When this happens, we set the text
486 //field to the currently selected file in the list which is already know.
FileDialogModeCallback(UINT8 ubID,BOOLEAN fEntering)487 static void FileDialogModeCallback(UINT8 ubID, BOOLEAN fEntering)
488 {
489 INT32 x;
490 FDLG_LIST *FListNode;
491 if( fEntering )
492 {
493 // Skip to first filename
494 FListNode = FileList;
495 for(x=0;x<iTopFileShown && x<iTotalFiles && FListNode != NULL;x++)
496 {
497 FListNode = FListNode->pNext;
498 }
499 // Find the already selected filename
500 for(x = iTopFileShown; x < iTopFileShown + 8 && x < iTotalFiles && FListNode != NULL; x++ )
501 {
502 if( iCurrFileShown == (x-iTopFileShown) )
503 {
504 FListNode->filename[30] = '\0';
505 SetInputFieldString(0, FListNode->filename);
506 return;
507 }
508 FListNode = FListNode->pNext;
509 }
510 }
511 }
512
513
RemoveFileDialog(void)514 static void RemoveFileDialog(void)
515 {
516 INT32 x;
517
518 MSYS_RemoveRegion( &BlanketRegion );
519
520 for(x=0; x<6; x++)
521 {
522 RemoveButton(iFileDlgButtons[x]);
523 }
524
525 if (iFileDlgButtons[6]) RemoveButton(iFileDlgButtons[6]);
526
527 TrashFDlgList( FileList );
528 FileList = NULL;
529
530 InvalidateScreen( );
531
532 EnableEditorTaskbar();
533 KillTextInputMode();
534 MarkWorldDirty();
535 RenderWorld();
536 }
537
538
DrawFileDialog(void)539 static void DrawFileDialog(void)
540 {
541 ColorFillVideoSurfaceArea(FRAME_BUFFER, 179, 69, (179+281), 261, Get16BPPColor(FROMRGB(136, 138, 135)) );
542 ColorFillVideoSurfaceArea(FRAME_BUFFER, 180, 70, (179+281), 261, Get16BPPColor(FROMRGB(24, 61, 81)) );
543 ColorFillVideoSurfaceArea(FRAME_BUFFER, 180, 70, (179+280), 260, Get16BPPColor(FROMRGB(65, 79, 94)) );
544
545 ColorFillVideoSurfaceArea(FRAME_BUFFER, (179+4), (69+3), (179+4+240), (69+123), Get16BPPColor(FROMRGB(24, 61, 81)) );
546 ColorFillVideoSurfaceArea(FRAME_BUFFER, (179+5), (69+4), (179+4+240), (69+123), Get16BPPColor(FROMRGB(136, 138, 135)) );
547 ColorFillVideoSurfaceArea(FRAME_BUFFER, (179+5), (69+4), (179+3+240), (69+122), Get16BPPColor(FROMRGB(250, 240, 188)) );
548
549 MarkButtonsDirty();
550 RenderButtons();
551 RenderButtonsFastHelp();
552
553 SetFontAttributes(FONT10ARIAL, FONT_LTKHAKI, FONT_DKKHAKI);
554 MPrint(183, 217, "Filename");
555
556 if (iFileDlgButtons[6]) MPrint(200, 231, "Update world info");
557 }
558
559
560 //The callback calls this function passing the relative y position of where
561 //the user clicked on the hot spot.
SelectFileDialogYPos(UINT16 usRelativeYPos)562 static void SelectFileDialogYPos(UINT16 usRelativeYPos)
563 {
564 INT16 sSelName;
565 INT32 x;
566 FDLG_LIST *FListNode;
567
568 sSelName = usRelativeYPos / 15;
569
570 //This is a field in the text editmode, but clicked via mouse.
571 SetActiveField( 1 );
572
573 // Skip to first filename
574 FListNode = FileList;
575 for(x=0;x<iTopFileShown && x<iTotalFiles && FListNode != NULL;x++)
576 {
577 FListNode = FListNode->pNext;
578 }
579
580 for(x=iTopFileShown;x<(iTopFileShown+8) && x<iTotalFiles && FListNode != NULL; x++)
581 {
582 if( (INT32)sSelName == (x-iTopFileShown) )
583 {
584 INT32 iCurrClickTime;
585 iCurrFileShown = x;
586 FListNode->filename[30] = '\0';
587 gzFilename = ST::format("{}", FListNode->filename);
588 if (!ValidFilename()) gzFilename = ST::null;
589 SetInputFieldString(0, gzFilename);
590
591 RenderInactiveTextField( 0 );
592
593 //Calculate and process any double clicking...
594 iCurrClickTime = GetJA2Clock();
595 if( iCurrClickTime - iLastClickTime < 400 && x == iLastFileClicked )
596 { //Considered a double click, so activate load/save this filename.
597 iFDlgState = iCurrentAction == ACTION_SAVE_MAP ? DIALOG_SAVE : DIALOG_LOAD;
598 }
599 iLastClickTime = iCurrClickTime;
600 iLastFileClicked = x;
601 }
602 FListNode = FListNode->pNext;
603 }
604 }
605
606
AddToFDlgList(FDLG_LIST * const list,char const * const filename)607 FDLG_LIST* AddToFDlgList(FDLG_LIST* const list, char const* const filename)
608 { // Add and sort alphabetically without regard to case
609 FDLG_LIST* prev = 0;
610 FDLG_LIST* i;
611 for (i = list; i; prev = i, i = i->pNext)
612 {
613 if (strcasecmp(i->filename, filename) > 0) break;
614 }
615 FDLG_LIST* const n = new FDLG_LIST{};
616 strlcpy(n->filename, filename, lengthof(n->filename));
617 n->pPrev = prev;
618 n->pNext = i;
619 if (i) i->pPrev = n;
620 if (prev) prev->pNext = n;
621 return prev ? list : n;
622 }
623
624
RemoveFromFDlgList(FDLG_LIST ** const head,FDLG_LIST * const node)625 static void RemoveFromFDlgList(FDLG_LIST** const head, FDLG_LIST* const node)
626 {
627 for (FDLG_LIST* i = *head; i; i = i->pNext)
628 {
629 if (i != node) continue;
630 if (*head == node) *head = (*head)->pNext;
631 FDLG_LIST* const prev = i->pPrev;
632 FDLG_LIST* const next = i->pNext;
633 if (prev) prev->pNext = next;
634 if (next) next->pPrev = prev;
635 delete node;
636 break;
637 }
638 }
639
640
TrashFDlgList(FDLG_LIST * i)641 static void TrashFDlgList(FDLG_LIST* i)
642 {
643 while (i)
644 {
645 FDLG_LIST* const del = i;
646 i = i->pNext;
647 delete del;
648 }
649 }
650
651
SetTopFileToLetter(UINT16 usLetter)652 static void SetTopFileToLetter(UINT16 usLetter)
653 {
654 UINT32 x;
655 FDLG_LIST *curr;
656 FDLG_LIST *prev;
657 UINT16 usNodeLetter;
658
659 // Skip to first filename
660 x = 0;
661 curr = prev = FileList;
662 while( curr )
663 {
664 usNodeLetter = curr->filename[0]; //first letter of filename.
665 if( usNodeLetter < 'a' )
666 usNodeLetter += 32; //convert uppercase to lower case A=65, a=97
667 if( usLetter <= usNodeLetter )
668 break;
669 prev = curr;
670 curr = curr->pNext;
671 x++;
672 }
673 if( FileList )
674 {
675 iCurrFileShown = x;
676 iTopFileShown = x;
677 if( iTopFileShown > iTotalFiles - 7 )
678 iTopFileShown = iTotalFiles - 7;
679 SetInputFieldString(0, prev->filename);
680 }
681 }
682
683
HandleMainKeyEvents(InputAtom * pEvent)684 static void HandleMainKeyEvents(InputAtom* pEvent)
685 {
686 INT32 iPrevFileShown = iCurrFileShown;
687 //Replace Alt-x press with ESC.
688 if( pEvent->usKeyState & ALT_DOWN && pEvent->usParam == 'x' )
689 pEvent->usParam = SDLK_ESCAPE;
690 switch( pEvent->usParam )
691 {
692 case SDLK_RETURN:
693 if( gfNoFiles && iCurrentAction == ACTION_LOAD_MAP )
694 break;
695 iFDlgState = iCurrentAction == ACTION_SAVE_MAP ? DIALOG_SAVE : DIALOG_LOAD;
696 break;
697
698 case SDLK_ESCAPE:
699 iFDlgState = DIALOG_CANCEL;
700 break;
701
702 case SDLK_PAGEUP:
703 if( iTopFileShown > 7 )
704 {
705 iTopFileShown -= 7;
706 iCurrFileShown -= 7;
707 }
708 else
709 {
710 iTopFileShown = 0;
711 iCurrFileShown = 0;
712 }
713 break;
714
715 case SDLK_PAGEDOWN:
716 iTopFileShown += 7;
717 iCurrFileShown += 7;
718 if( iTopFileShown > iTotalFiles-7 )
719 iTopFileShown = iTotalFiles - 7;
720 if( iCurrFileShown >= iTotalFiles )
721 iCurrFileShown = iTotalFiles - 1;
722 break;
723
724 case SDLK_UP:
725 if( iCurrFileShown > 0 )
726 iCurrFileShown--;
727 if( iTopFileShown > iCurrFileShown )
728 iTopFileShown = iCurrFileShown;
729 break;
730
731 case SDLK_DOWN:
732 iCurrFileShown++;
733 if( iCurrFileShown >= iTotalFiles )
734 iCurrFileShown = iTotalFiles - 1;
735 else if( iTopFileShown < iCurrFileShown-7 )
736 iTopFileShown++;
737 break;
738
739 case SDLK_HOME:
740 iTopFileShown = 0;
741 iCurrFileShown = 0;
742 break;
743
744 case SDLK_END:
745 iTopFileShown = iTotalFiles-7;
746 iCurrFileShown = iTotalFiles-1;
747 break;
748
749 case SDLK_DELETE: iFDlgState = DIALOG_DELETE; break;
750
751 default:
752 //This case handles jumping the file list to display the file with the letter pressed.
753 if (pEvent->usParam >= SDLK_a && pEvent->usParam <= SDLK_z)
754 {
755 SetTopFileToLetter( (UINT16)pEvent->usParam );
756 }
757 break;
758 }
759 //Update the text field if the file value has changed.
760 if( iCurrFileShown != iPrevFileShown )
761 {
762 INT32 x;
763 FDLG_LIST *curr;
764 x = 0;
765 curr = FileList;
766 while( curr && x != iCurrFileShown )
767 {
768 curr = curr->pNext;
769 x++;
770 }
771 if( curr )
772 {
773 SetInputFieldString(0, curr->filename);
774 gzFilename = ST::format("{}", curr->filename);
775 }
776 }
777 }
778
779
780 // Editor doesn't care about the z value. It uses its own methods.
SetGlobalSectorValues()781 static void SetGlobalSectorValues()
782 {
783 { const char* f = gzFilename.c_str();
784
785 INT16 y;
786 if ('A' <= f[0] && f[0] <= 'P')
787 {
788 y = f[0] - 'A' + 1;
789 }
790 else if ('a' <= f[0] && f[0] <= 'p')
791 {
792 y = f[0] - 'a' + 1;
793 }
794 else goto invalid;
795 ++f;
796
797 INT16 x;
798 if ('1' <= f[0] && f[0] <= '9' && (f[1] < '0' || '9' < f[1]))
799 { // 1 ... 9
800 x = f[0] - '0';
801 ++f;
802 }
803 else if (f[0] == '1' && '0' <= f[1] && f[1] <= '6')
804 { // 10 ... 16
805 x = (f[0] - '0') * 10 + f[1] - '0';
806 f += 2;
807 }
808 else goto invalid;
809
810 INT8 z = 0;
811 if (f[0] == '_' && f[1] == 'b' && '1' <= f[2] && f[2] <= '3')
812 {
813 z = f[2] - '0';
814 }
815
816 gWorldSectorX = x;
817 gWorldSectorY = y;
818 gbWorldSectorZ = z;
819 return;
820 }
821 invalid:
822 SetWorldSectorInvalid();
823 }
824
825
InitErrorCatchDialog(void)826 static void InitErrorCatchDialog(void)
827 {
828 DoMessageBox(MSG_BOX_BASIC_STYLE, gzErrorCatchString, EDIT_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL);
829 gfErrorCatch = FALSE;
830 }
831
832
833 //Because loading and saving the map takes a few seconds, we want to post a message
834 //on the screen and then update it which requires passing the screen back to the main loop.
835 //When we come back for the next frame, we then actually save or load the map. So this
836 //process takes two full screen cycles.
ProcessFileIO(void)837 static ScreenID ProcessFileIO(void)
838 {
839 INT16 usStartX, usStartY;
840 ST::string ubNewFilename;
841 ST::string zOrigName;
842 switch( gbCurrentFileIOStatus )
843 {
844 case INITIATE_MAP_SAVE: //draw save message
845 SaveFontSettings();
846 SetFontAttributes(HUGEFONT, FONT_LTKHAKI, FONT_DKKHAKI);
847 zOrigName = ST::format("Saving map: {}", gzFilename);
848 usStartX = (SCREEN_WIDTH - StringPixLength(zOrigName, HUGEFONT)) / 2;
849 usStartY = 180 - GetFontHeight(HUGEFONT) / 2;
850 MPrint(usStartX, usStartY, zOrigName);
851
852 InvalidateScreen( );
853 gbCurrentFileIOStatus = SAVING_MAP;
854 return LOADSAVE_SCREEN;
855 case SAVING_MAP: //save map
856 ubNewFilename = ST::format("{}", gzFilename);
857 RaiseWorldLand();
858 if( gfShowPits )
859 RemoveAllPits();
860 OptimizeSchedules();
861 if ( !SaveWorld( ubNewFilename.c_str() ) )
862 {
863 if( gfErrorCatch )
864 {
865 InitErrorCatchDialog();
866 return EDIT_SCREEN;
867 }
868 return ERROR_SCREEN;
869 }
870 if( gfShowPits )
871 AddAllPits();
872
873 SetGlobalSectorValues();
874
875 if( gfGlobalSummaryExists )
876 UpdateSectorSummary( gzFilename, gfUpdateSummaryInfo );
877
878 iCurrentAction = ACTION_NULL;
879 gbCurrentFileIOStatus = IOSTATUS_NONE;
880 gfRenderWorld = TRUE;
881 gfRenderTaskbar = TRUE;
882 fEnteringLoadSaveScreen = TRUE;
883 RestoreFontSettings();
884 if( gfErrorCatch )
885 {
886 InitErrorCatchDialog();
887 return EDIT_SCREEN;
888 }
889 if( gMapInformation.ubMapVersion != gubMinorMapVersion )
890 SLOGE("Map data has just been corrupted!!! What did you just do? KM : 0");
891 return EDIT_SCREEN;
892 case INITIATE_MAP_LOAD: //draw load message
893 SaveFontSettings();
894 gbCurrentFileIOStatus = LOADING_MAP;
895 if( gfEditMode && iCurrentTaskbar == TASK_MERCS )
896 IndicateSelectedMerc( SELECT_NO_MERC );
897 SpecifyItemToEdit( NULL, -1 );
898 return LOADSAVE_SCREEN;
899 case LOADING_MAP: //load map
900 DisableUndo();
901 ubNewFilename = ST::format("{}", gzFilename);
902
903 RemoveMercsInSector( );
904
905 try
906 {
907 UINT32 const start = SDL_GetTicks();
908 LoadWorld(ubNewFilename.c_str());
909 fprintf(stderr, "---> %u\n", SDL_GetTicks() - start);
910 }
911 catch (...)
912 { //Want to override crash, so user can do something else.
913 EnableUndo();
914 SetPendingNewScreen( LOADSAVE_SCREEN );
915 gbCurrentFileIOStatus = IOSTATUS_NONE;
916 gfGlobalError = FALSE;
917 gfLoadError = TRUE;
918 //RemoveButton( iTempButton );
919 CreateMessageBox( " Error loading file. Try another filename?" );
920 return LOADSAVE_SCREEN;
921 }
922 SetGlobalSectorValues();
923
924 RestoreFontSettings();
925
926 //Load successful, update necessary information.
927
928 AddSoldierInitListTeamToWorld(ENEMY_TEAM);
929 AddSoldierInitListTeamToWorld(CREATURE_TEAM);
930 AddSoldierInitListTeamToWorld(MILITIA_TEAM);
931 AddSoldierInitListTeamToWorld(CIV_TEAM);
932 iCurrentAction = ACTION_NULL;
933 gbCurrentFileIOStatus = IOSTATUS_NONE;
934 if( !gfCaves && !gfBasement )
935 {
936 gusLightLevel = 12;
937 if( ubAmbientLightLevel != 4 )
938 {
939 ubAmbientLightLevel = 4;
940 LightSetBaseLevel( ubAmbientLightLevel );
941 }
942 }
943 else
944 gusLightLevel = (UINT16)(EDITOR_LIGHT_MAX - ubAmbientLightLevel );
945 gEditorLightColor = g_light_color;
946 gfRenderWorld = TRUE;
947 gfRenderTaskbar = TRUE;
948 fEnteringLoadSaveScreen = TRUE;
949 InitJA2SelectionWindow();
950 ShowEntryPoints();
951 EnableUndo();
952 RemoveAllFromUndoList();
953 SetEditorSmoothingMode( gMapInformation.ubEditorSmoothingType );
954 if( gMapInformation.ubEditorSmoothingType == SMOOTHING_CAVES )
955 AnalyseCaveMapForStructureInfo();
956
957 AddLockedDoorCursors();
958 gubCurrRoomNumber = gubMaxRoomNumber;
959 UpdateRoofsView();
960 UpdateWallsView();
961 ShowLightPositionHandles();
962 SetMercTeamVisibility( ENEMY_TEAM, gfShowEnemies );
963 SetMercTeamVisibility( CREATURE_TEAM, gfShowCreatures );
964 SetMercTeamVisibility( MILITIA_TEAM, gfShowRebels );
965 SetMercTeamVisibility( CIV_TEAM, gfShowCivilians );
966 BuildItemPoolList();
967 if( gfShowPits )
968 AddAllPits();
969
970 if( iCurrentTaskbar == TASK_MAPINFO )
971 { //We have to temporarily remove the current textinput mode,
972 //update the disabled text field values, then restore the current
973 //text input fields.
974 SaveAndRemoveCurrentTextInputMode();
975 UpdateMapInfoFields();
976 RestoreSavedTextInputMode();
977 }
978 return EDIT_SCREEN;
979 }
980 gbCurrentFileIOStatus = IOSTATUS_NONE;
981 return LOADSAVE_SCREEN;
982 }
983
984
985 //LOADSCREEN
FDlgNamesCallback(GUI_BUTTON * butn,INT32 reason)986 static void FDlgNamesCallback(GUI_BUTTON* butn, INT32 reason)
987 {
988 if( reason & (MSYS_CALLBACK_REASON_LBUTTON_UP) )
989 {
990 SelectFileDialogYPos(butn->RelativeY());
991 }
992 }
993
994
FDlgOkCallback(GUI_BUTTON * butn,INT32 reason)995 static void FDlgOkCallback(GUI_BUTTON* butn, INT32 reason)
996 {
997 if( reason & (MSYS_CALLBACK_REASON_LBUTTON_UP) )
998 {
999 iFDlgState = iCurrentAction == ACTION_SAVE_MAP ? DIALOG_SAVE : DIALOG_LOAD;
1000 }
1001 }
1002
1003
FDlgCancelCallback(GUI_BUTTON * butn,INT32 reason)1004 static void FDlgCancelCallback(GUI_BUTTON* butn, INT32 reason)
1005 {
1006 if( reason & (MSYS_CALLBACK_REASON_LBUTTON_UP) )
1007 {
1008 iFDlgState = DIALOG_CANCEL;
1009 }
1010 }
1011
1012
FDlgUpCallback(GUI_BUTTON * butn,INT32 reason)1013 static void FDlgUpCallback(GUI_BUTTON* butn, INT32 reason)
1014 {
1015 if( reason & (MSYS_CALLBACK_REASON_LBUTTON_UP) )
1016 {
1017 if(iTopFileShown > 0)
1018 iTopFileShown--;
1019 }
1020 }
1021
1022
FDlgDwnCallback(GUI_BUTTON * butn,INT32 reason)1023 static void FDlgDwnCallback(GUI_BUTTON* butn, INT32 reason)
1024 {
1025 if( reason & (MSYS_CALLBACK_REASON_LBUTTON_UP) )
1026 {
1027 if( (iTopFileShown+7) < iTotalFiles )
1028 iTopFileShown++;
1029 }
1030 }
1031
1032
ExtractFilenameFromFields(void)1033 static BOOLEAN ExtractFilenameFromFields(void)
1034 {
1035 gzFilename = GetStringFromField(0);
1036 return ValidFilename();
1037 }
1038
1039
ValidFilename(void)1040 static BOOLEAN ValidFilename(void)
1041 {
1042 if (!gzFilename.empty())
1043 {
1044 auto pos = gzFilename.find(".dat");
1045 if (pos > 0 && gzFilename[pos + 4] == '\0' )
1046 return TRUE;
1047 }
1048 return FALSE;
1049 }
1050
ExternalLoadMap(const ST::string & szFilename)1051 BOOLEAN ExternalLoadMap(const ST::string& szFilename)
1052 {
1053 if (szFilename.empty())
1054 return FALSE;
1055 gzFilename = szFilename;
1056 if( !ValidFilename() )
1057 return FALSE;
1058 gbCurrentFileIOStatus = INITIATE_MAP_LOAD;
1059 ProcessFileIO(); //always returns loadsave_screen and changes iostatus to loading_map.
1060 ExecuteBaseDirtyRectQueue();
1061 EndFrameBufferRender();
1062 RefreshScreen();
1063 if( ProcessFileIO() == EDIT_SCREEN )
1064 return TRUE;
1065 return FALSE;
1066 }
1067
ExternalSaveMap(const ST::string & szFilename)1068 BOOLEAN ExternalSaveMap(const ST::string& szFilename)
1069 {
1070 if (szFilename.empty())
1071 return FALSE;
1072 gzFilename = szFilename;
1073 if( !ValidFilename() )
1074 return FALSE;
1075 gbCurrentFileIOStatus = INITIATE_MAP_SAVE;
1076 if( ProcessFileIO() == ERROR_SCREEN )
1077 return FALSE;
1078 ExecuteBaseDirtyRectQueue();
1079 EndFrameBufferRender();
1080 RefreshScreen();
1081 if( ProcessFileIO() == EDIT_SCREEN )
1082 return TRUE;
1083 return FALSE;
1084 }
1085