1 #include "Directories.h"
2 #include "Font.h"
3 #include "Font_Control.h"
4 #include "Laptop.h"
5 #include "LoadSaveData.h"
6 #include "History.h"
7 #include "Game_Clock.h"
8 #include "Quests.h"
9 #include "Soldier_Control.h"
10 #include "VObject.h"
11 #include "Debug.h"
12 #include "WordWrap.h"
13 #include "Render_Dirty.h"
14 #include "Cursors.h"
15 #include "Soldier_Profile.h"
16 #include "StrategicMap.h"
17 #include "QuestText.h"
18 #include "Text.h"
19 #include "Message.h"
20 #include "LaptopSave.h"
21 #include "Button_System.h"
22 #include "VSurface.h"
23 #include "MemMan.h"
24 #include "FileMan.h"
25 
26 #include "ContentManager.h"
27 #include "GameInstance.h"
28 
29 #include <string_theory/format>
30 #include <string_theory/string>
31 
32 
33 #define HISTORY_QUEST_TEXT_SIZE 80
34 
35 
36 struct HistoryUnit
37 {
38 	UINT8 ubCode; // the code index in the finance code table
39 	UINT8 ubSecondCode; // secondary code
40 	UINT32 uiDate; // time in the world in global time
41 	INT16 sSectorX; // sector X this took place in
42 	INT16 sSectorY; // sector Y this took place in
43 	INT8 bSectorZ;
44 	HistoryUnit* Next; // next unit in the list
45 };
46 
47 
48 #define TOP_X				0+LAPTOP_SCREEN_UL_X
49 #define TOP_Y				LAPTOP_SCREEN_UL_Y
50 #define BOX_HEIGHT			14
51 #define TOP_DIVLINE_Y			(STD_SCREEN_Y + 101)
52 #define TITLE_X				(STD_SCREEN_X + 140)
53 #define TITLE_Y				(STD_SCREEN_Y + 33 )
54 #define PAGE_SIZE			22
55 #define RECORD_Y			TOP_DIVLINE_Y
56 #define RECORD_HISTORY_WIDTH		200
57 #define PAGE_NUMBER_X			TOP_X+20
58 #define PAGE_NUMBER_Y			TOP_Y+33
59 #define HISTORY_DATE_X			PAGE_NUMBER_X+85
60 #define HISTORY_DATE_Y			PAGE_NUMBER_Y
61 #define RECORD_LOCATION_WIDTH		142//95
62 
63 #define HISTORY_HEADER_FONT		FONT14ARIAL
64 #define HISTORY_TEXT_FONT		FONT12ARIAL
65 #define RECORD_DATE_X			TOP_X+10
66 #define RECORD_DATE_WIDTH		31//68
67 #define RECORD_HEADER_Y			(STD_SCREEN_Y + 90)
68 
69 
70 #define NUM_RECORDS_PER_PAGE		PAGE_SIZE
71 #define SIZE_OF_HISTORY_FILE_RECORD	( sizeof( UINT8 ) + sizeof( UINT8 ) + sizeof( UINT32 ) + sizeof( UINT16 ) + sizeof( UINT16 ) + sizeof( UINT8 ) + sizeof( UINT8 ) )
72 
73 // button positions
74 #define NEXT_BTN_X			(STD_SCREEN_X + 577)
75 #define PREV_BTN_X			(STD_SCREEN_X + 553)
76 #define BTN_Y				(STD_SCREEN_Y + 53 )
77 
78 // graphics handles
79 static SGPVObject* guiTITLE;
80 static SGPVObject* guiTOP;
81 static SGPVObject* guiLONGLINE;
82 static SGPVObject* guiSHADELINE;
83 
84 enum{
85 	PREV_PAGE_BUTTON=0,
86 	NEXT_PAGE_BUTTON,
87 };
88 
89 // the page flipping buttons
90 static GUIButtonRef giHistoryButton[2];
91 
92 static MOUSE_REGION g_scroll_region;
93 
94 
95 static BOOLEAN fInHistoryMode = FALSE;
96 // current page displayed
97 static INT32 iCurrentHistoryPage = 1;
98 
99 
100 // the History record list
101 static HistoryUnit* pHistoryListHead = NULL;
102 
103 
104 void ClearHistoryList( void );
105 
106 
107 static void AppendHistoryToEndOfFile(void);
108 static BOOLEAN LoadInHistoryRecords(const UINT32 uiPage);
109 static void ProcessAndEnterAHistoryRecord(UINT8 ubCode, UINT32 uiDate, UINT8 ubSecondCode, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ);
110 
111 
AddHistoryToPlayersLog(const UINT8 ubCode,const UINT8 ubSecondCode,const UINT32 uiDate,const INT16 sSectorX,const INT16 sSectorY)112 void AddHistoryToPlayersLog(const UINT8 ubCode, const UINT8 ubSecondCode, const UINT32 uiDate, const INT16 sSectorX, const INT16 sSectorY)
113 {
114 	ClearHistoryList();
115 
116 	ProcessAndEnterAHistoryRecord(ubCode, uiDate, ubSecondCode, sSectorX, sSectorY, 0);
117 	ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMessageStrings[MSG_HISTORY_UPDATED]);
118 
119 	AppendHistoryToEndOfFile();
120 
121 	// if in history mode, reload current page
122 	if (fInHistoryMode) LoadInHistoryRecords(iCurrentHistoryPage);
123 }
124 
125 
GameInitHistory()126 void GameInitHistory()
127 {
128 	FileDelete(HISTORY_DATA_FILE);
129 }
130 
131 
132 static void CreateHistoryButtons(void);
133 static void LoadHistory(void);
134 static void SetHistoryButtonStates(void);
135 
136 
EnterHistory()137 void EnterHistory()
138 {
139 	// load the graphics
140 	LoadHistory( );
141 
142 	// create History buttons
143 	CreateHistoryButtons( );
144 
145 	// reset current to first page
146 	iCurrentHistoryPage = LaptopSaveInfo.iCurrentHistoryPage;
147 	if (iCurrentHistoryPage <= 0) iCurrentHistoryPage = 1;
148 
149 	LoadInHistoryRecords(iCurrentHistoryPage);
150 
151 	// render hbackground
152 	RenderHistory( );
153 
154 
155 	// set the fact we are in the history viewer
156 	fInHistoryMode=TRUE;
157 
158 	// build Historys list
159 	//OpenAndReadHistoryFile( );
160 
161 	// force redraw of the entire screen
162 	//fReDrawScreenFlag=TRUE;
163 
164 	// set inital states
165 	SetHistoryButtonStates( );
166 }
167 
168 
169 static void DestroyHistoryButtons(void);
170 static void RemoveHistory(void);
171 
172 
ExitHistory()173 void ExitHistory()
174 {
175 	LaptopSaveInfo.iCurrentHistoryPage = iCurrentHistoryPage;
176 
177 	// not in History system anymore
178 	fInHistoryMode=FALSE;
179 
180 	// delete graphics
181 	RemoveHistory( );
182 
183 	// delete buttons
184 	DestroyHistoryButtons( );
185 
186 	ClearHistoryList( );
187 }
188 
189 
190 static void DrawAPageofHistoryRecords(void);
191 static void RenderHistoryBackGround(void);
192 
193 
RenderHistory(void)194 void RenderHistory( void )
195 {
196 	//render the background to the display
197 	RenderHistoryBackGround( );
198 
199 	// render the currentpage of records
200 	DrawAPageofHistoryRecords( );
201 
202 	// title bar icon
203 	BlitTitleBarIcons(  );
204 }
205 
206 
LoadHistory(void)207 static void LoadHistory(void)
208 {
209 	// load History video objects into memory
210 
211 	// title bar
212 	guiTITLE = AddVideoObjectFromFile(LAPTOPDIR "/programtitlebar.sti");
213 
214 	// top portion of the screen background
215 	guiTOP = AddVideoObjectFromFile(LAPTOPDIR "/historywindow.sti");
216 
217 	// shaded line
218 	guiSHADELINE = AddVideoObjectFromFile(LAPTOPDIR "/historylines.sti");
219 
220 	// black divider line - long ( 480 length)
221 	guiLONGLINE = AddVideoObjectFromFile(LAPTOPDIR "/divisionline480.sti");
222 }
223 
224 
RemoveHistory(void)225 static void RemoveHistory(void)
226 {
227 	// delete history video objects from memory
228 	DeleteVideoObject(guiLONGLINE);
229 	DeleteVideoObject(guiTOP);
230 	DeleteVideoObject(guiTITLE);
231 	DeleteVideoObject(guiSHADELINE);
232 }
233 
234 
RenderHistoryBackGround(void)235 static void RenderHistoryBackGround(void)
236 {
237 	// render generic background for history system
238 	BltVideoObject(FRAME_BUFFER, guiTITLE, 0, TOP_X, TOP_Y -  2);
239 	BltVideoObject(FRAME_BUFFER, guiTOP,   0, TOP_X, TOP_Y + 22);
240 }
241 
242 
DrawHistoryTitleText(void)243 static void DrawHistoryTitleText(void)
244 {
245 	// draw the pages title
246 	SetFontAttributes(HISTORY_HEADER_FONT, FONT_WHITE);
247 	MPrint(TITLE_X, TITLE_Y, pHistoryTitle);
248 }
249 
250 
251 static void LoadNextHistoryPage(void);
252 static void LoadPreviousHistoryPage(void);
253 
254 
ScrollRegionCallback(MOUSE_REGION * const,INT32 const reason)255 static void ScrollRegionCallback(MOUSE_REGION* const, INT32 const reason)
256 {
257 	if (reason & MSYS_CALLBACK_REASON_WHEEL_UP)
258 	{
259 		LoadPreviousHistoryPage();
260 	}
261 	else if (reason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
262 	{
263 		LoadNextHistoryPage();
264 	}
265 }
266 
267 
268 static void BtnHistoryDisplayNextPageCallBack(GUI_BUTTON* btn, INT32 reason);
269 static void BtnHistoryDisplayPrevPageCallBack(GUI_BUTTON* btn, INT32 reason);
270 
271 
CreateHistoryButtons(void)272 static void CreateHistoryButtons(void)
273 {
274 	// the prev/next page buttons
275 	giHistoryButton[PREV_PAGE_BUTTON] = QuickCreateButtonImg(LAPTOPDIR "/arrows.sti", 0, 1, PREV_BTN_X, BTN_Y, MSYS_PRIORITY_HIGHEST - 1, BtnHistoryDisplayPrevPageCallBack);
276 	giHistoryButton[NEXT_PAGE_BUTTON] = QuickCreateButtonImg(LAPTOPDIR "/arrows.sti", 6, 7, NEXT_BTN_X, BTN_Y, MSYS_PRIORITY_HIGHEST - 1, BtnHistoryDisplayNextPageCallBack);
277 
278 	// set buttons
279 	giHistoryButton[0]->SetCursor(CURSOR_LAPTOP_SCREEN);
280 	giHistoryButton[1]->SetCursor(CURSOR_LAPTOP_SCREEN);
281 
282 	UINT16 const x = TOP_X +  8;
283 	UINT16 const y = TOP_Y + 53;
284 	UINT16 const w = 482;
285 	UINT16 const h = 354;
286 	MSYS_DefineRegion(&g_scroll_region, x, y, x + w, y + h, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, ScrollRegionCallback);
287 }
288 
289 
DestroyHistoryButtons(void)290 static void DestroyHistoryButtons(void)
291 {
292 	// remove History buttons and images from memory
293 	MSYS_RemoveRegion(&g_scroll_region);
294 	// next page button
295 	RemoveButton(giHistoryButton[1] );
296 	// prev page button
297 	RemoveButton(giHistoryButton[0] );
298 }
299 
300 
BtnHistoryDisplayPrevPageCallBack(GUI_BUTTON * btn,INT32 reason)301 static void BtnHistoryDisplayPrevPageCallBack(GUI_BUTTON* btn, INT32 reason)
302 {
303 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
304 	{
305 		fReDrawScreenFlag = TRUE;
306 	}
307 
308 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
309 	{
310 		LoadPreviousHistoryPage();
311 	}
312 }
313 
314 
BtnHistoryDisplayNextPageCallBack(GUI_BUTTON * btn,INT32 reason)315 static void BtnHistoryDisplayNextPageCallBack(GUI_BUTTON* btn, INT32 reason)
316 {
317 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
318 	{
319 		fReDrawScreenFlag = TRUE;
320 	}
321 
322 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
323 	{
324 		LoadNextHistoryPage();
325 	}
326 }
327 
328 
ProcessAndEnterAHistoryRecord(const UINT8 ubCode,const UINT32 uiDate,const UINT8 ubSecondCode,const INT16 sSectorX,const INT16 sSectorY,const INT8 bSectorZ)329 static void ProcessAndEnterAHistoryRecord(const UINT8 ubCode, const UINT32 uiDate, const UINT8 ubSecondCode, const INT16 sSectorX, const INT16 sSectorY, const INT8 bSectorZ)
330 {
331 	HistoryUnit* const h = new HistoryUnit{};
332 	h->Next         = NULL;
333 	h->ubCode       = ubCode;
334 	h->ubSecondCode = ubSecondCode;
335 	h->uiDate       = uiDate;
336 	h->sSectorX     = sSectorX;
337 	h->sSectorY     = sSectorY;
338 	h->bSectorZ     = bSectorZ;
339 
340 	// Append node to list
341 	HistoryUnit** anchor = &pHistoryListHead;
342 	while (*anchor != NULL) anchor = &(*anchor)->Next;
343 	*anchor = h;
344 }
345 
346 // open and read in data to the History list
OpenAndReadHistoryFile(void)347 static void OpenAndReadHistoryFile(void)
348 {
349 	ClearHistoryList();
350 
351 	AutoSGPFile f(GCM->openGameResForReading(HISTORY_DATA_FILE));
352 
353 	UINT entry_count = FileGetSize(f) / SIZE_OF_HISTORY_FILE_RECORD;
354 	while (entry_count-- > 0)
355 	{
356 		UINT8  ubCode;
357 		UINT8  ubSecondCode;
358 		UINT32 uiDate;
359 		INT16  sSectorX;
360 		INT16  sSectorY;
361 		INT8   bSectorZ;
362 
363 		FileRead(f, &ubCode,       sizeof(UINT8));
364 		FileRead(f, &ubSecondCode, sizeof(UINT8));
365 		FileRead(f, &uiDate,       sizeof(UINT32));
366 		FileRead(f, &sSectorX,     sizeof(INT16));
367 		FileRead(f, &sSectorY,     sizeof(INT16));
368 		FileRead(f, &bSectorZ,     sizeof(INT8));
369 		FileSeek(f, 1, FILE_SEEK_FROM_CURRENT);
370 
371 		ProcessAndEnterAHistoryRecord(ubCode, uiDate, ubSecondCode, sSectorX, sSectorY, bSectorZ);
372 	}
373 }
374 
375 
ClearHistoryList(void)376 void ClearHistoryList(void)
377 {
378 	for (HistoryUnit* h = pHistoryListHead; h != NULL;)
379 	{
380 		HistoryUnit* const next = h->Next;
381 		delete h;
382 		h = next;
383 	}
384 	pHistoryListHead = NULL;
385 }
386 
387 
DisplayHistoryListHeaders(void)388 static void DisplayHistoryListHeaders(void)
389 {
390 	// this procedure will display the headers to each column in History
391 	SetFontAttributes(HISTORY_TEXT_FONT, FONT_BLACK, NO_SHADOW);
392 
393 	INT16 usX;
394 	INT16 usY;
395 
396 	// the date header
397 	FindFontCenterCoordinates(RECORD_DATE_X + 5,0,RECORD_DATE_WIDTH,0, pHistoryHeaders[0], HISTORY_TEXT_FONT,&usX, &usY);
398 	MPrint(usX, RECORD_HEADER_Y, pHistoryHeaders[0]);
399 
400 	// the date header
401 	FindFontCenterCoordinates(RECORD_DATE_X + RECORD_DATE_WIDTH + 5,0,RECORD_LOCATION_WIDTH,0, pHistoryHeaders[ 3 ], HISTORY_TEXT_FONT,&usX, &usY);
402 	MPrint(usX, RECORD_HEADER_Y, pHistoryHeaders[3]);
403 
404 	// event header
405 	FindFontCenterCoordinates(RECORD_DATE_X + RECORD_DATE_WIDTH + RECORD_LOCATION_WIDTH + 5,0,RECORD_LOCATION_WIDTH,0, pHistoryHeaders[ 3 ], HISTORY_TEXT_FONT,&usX, &usY);
406 	MPrint(usX, RECORD_HEADER_Y, pHistoryHeaders[4]);
407 	// reset shadow
408 	SetFontShadow(DEFAULT_SHADOW);
409 }
410 
411 
DisplayHistoryListBackground(void)412 static void DisplayHistoryListBackground(void)
413 {
414 	// this function will display the History list display background
415 	INT32 iCounter=0;
416 
417 	// get shaded line object
418 	for(iCounter=0; iCounter <11; iCounter++)
419 	{
420 		// blt title bar to screen
421 		BltVideoObject(FRAME_BUFFER, guiSHADELINE, 0, TOP_X + 15, TOP_DIVLINE_Y + BOX_HEIGHT * 2 * iCounter);
422 	}
423 
424 	// the long hortizontal line int he records list display region
425 	BltVideoObject(FRAME_BUFFER, guiLONGLINE, 0,TOP_X + 9, TOP_DIVLINE_Y);
426 	BltVideoObject(FRAME_BUFFER, guiLONGLINE, 0,TOP_X + 9, TOP_DIVLINE_Y + BOX_HEIGHT * 2 * 11);
427 }
428 
429 
430 static ST::string ProcessHistoryTransactionString(const HistoryUnit* h);
431 
432 
433 // draw the text of the records
DrawHistoryRecordsText(void)434 static void DrawHistoryRecordsText(void)
435 {
436 	ST::string sString;
437 	INT16   sX;
438 	INT16   sY;
439 
440 	SetFont(HISTORY_TEXT_FONT);
441 	SetFontBackground(FONT_BLACK);
442 	SetFontShadow(NO_SHADOW);
443 
444 	UINT entry_count = 0;
445 	for (const HistoryUnit* h = pHistoryListHead; h != NULL; h = h->Next)
446 	{
447 		const UINT8 colour =
448 			h->ubCode  == HISTORY_CHEAT_ENABLED ||
449 			(h->ubCode == HISTORY_QUEST_STARTED && gubQuest[h->ubSecondCode] == QUESTINPROGRESS) ?
450 				FONT_RED : FONT_BLACK;
451 		SetFontForeground(colour);
452 
453 		const INT32 y = RECORD_Y + entry_count * BOX_HEIGHT + 3;
454 
455 		// get and write the date
456 		sString = ST::format("{}", h->uiDate / (24 * 60));
457 		INT16 usX;
458 		INT16 usY;
459 		FindFontCenterCoordinates(RECORD_DATE_X + 5, 0, RECORD_DATE_WIDTH, 0, sString, HISTORY_TEXT_FONT, &usX, &usY);
460 		MPrint(usX, y, sString);
461 
462 		if (h->sSectorX == -1 || h->sSectorY == -1)
463 		{
464 			// no location
465 			FindFontCenterCoordinates(RECORD_DATE_X + RECORD_DATE_WIDTH, 0, RECORD_LOCATION_WIDTH + 10, 0, pHistoryLocations, HISTORY_TEXT_FONT, &sX, &sY);
466 			MPrint(sX, y, pHistoryLocations);
467 		}
468 		else
469 		{
470 			sString = GetSectorIDString(h->sSectorX, h->sSectorY, h->bSectorZ, TRUE);
471 			FindFontCenterCoordinates(RECORD_DATE_X + RECORD_DATE_WIDTH, 0, RECORD_LOCATION_WIDTH + 10, 0,  sString, HISTORY_TEXT_FONT, &sX, &sY);
472 			sString = ReduceStringLength(sString, RECORD_LOCATION_WIDTH + 10, HISTORY_TEXT_FONT);
473 			MPrint(sX, y, sString);
474 		}
475 
476 		// the actual history text
477 		sString = ProcessHistoryTransactionString(h);
478 		MPrint(RECORD_DATE_X + RECORD_LOCATION_WIDTH + RECORD_DATE_WIDTH + 15, y, sString);
479 
480 		if (++entry_count == NUM_RECORDS_PER_PAGE) break;
481 	}
482 
483 	// restore shadow
484 	SetFontShadow(DEFAULT_SHADOW);
485 }
486 
487 
488 static void DisplayPageNumberAndDateRange(void);
489 
490 
DrawAPageofHistoryRecords(void)491 static void DrawAPageofHistoryRecords(void)
492 {
493 	// this procedure will draw a series of history records to the screen
494 
495 	// (re-)render background
496 
497 	// the title bar text
498 	DrawHistoryTitleText( );
499 
500 	// the actual lists background
501 	DisplayHistoryListBackground( );
502 
503 	// the headers to each column
504 	DisplayHistoryListHeaders( );
505 
506 
507 	// error check
508 	if(iCurrentHistoryPage==-1)
509 	{
510 		iCurrentHistoryPage=0;
511 	}
512 
513 
514 	// current page is found, render  from here
515 	DrawHistoryRecordsText( );
516 
517 	// update page numbers, and date ranges
518 	DisplayPageNumberAndDateRange( );
519 }
520 
521 
522 static INT32 GetNumberOfHistoryPages(void);
523 
524 
525 /* go through the list of 'histories' starting at current until end or
526  * NUM_RECORDS_PER_PAGE and get the date range and the page number */
DisplayPageNumberAndDateRange(void)527 static void DisplayPageNumberAndDateRange(void)
528 {
529 	UINT current_page;
530 	UINT count_pages;
531 	UINT first_date;
532 	UINT last_date;
533 	const HistoryUnit* h = pHistoryListHead;
534 	if (h == NULL)
535 	{
536 		current_page = 1;
537 		count_pages  = 1;
538 		first_date   = 1;
539 		last_date    = 1;
540 	}
541 	else
542 	{
543 		current_page     = iCurrentHistoryPage;
544 		count_pages      = GetNumberOfHistoryPages();
545 		first_date       = h->uiDate / (24 * 60);
546 
547 		UINT entry_count = NUM_RECORDS_PER_PAGE;
548 		while (--entry_count != 0 && h->Next != NULL) h = h->Next;
549 
550 		last_date        = h->uiDate / (24 * 60);
551 	}
552 
553 	SetFontAttributes(HISTORY_TEXT_FONT, FONT_BLACK, NO_SHADOW);
554 	MPrint(PAGE_NUMBER_X,  PAGE_NUMBER_Y, ST::format("{}  {} / {}", pHistoryHeaders[1], current_page, count_pages));
555 	MPrint(HISTORY_DATE_X, HISTORY_DATE_Y, ST::format("{} {} - {}",  pHistoryHeaders[2], first_date, last_date));
556 	SetFontShadow(DEFAULT_SHADOW);
557 }
558 
559 
560 static ST::string GetQuestEndedString(UINT8 ubQuestValue);
561 static ST::string GetQuestStartedString(UINT8 ubQuestValue);
562 
563 
ProcessHistoryTransactionString(const HistoryUnit * h)564 static ST::string ProcessHistoryTransactionString(const HistoryUnit* h)
565 {
566 	const UINT8 code = h->ubCode;
567 	switch (code)
568 	{
569 		case HISTORY_QUEST_STARTED:
570 			return GetQuestStartedString(h->ubSecondCode);
571 
572 		case HISTORY_QUEST_FINISHED:
573 			return GetQuestEndedString(h->ubSecondCode);
574 
575 		case HISTORY_LIBERATED_TOWN:
576 		case HISTORY_MINE_RAN_OUT:
577 		case HISTORY_MINE_REOPENED:
578 		case HISTORY_MINE_RUNNING_OUT:
579 		case HISTORY_MINE_SHUTDOWN:
580 		case HISTORY_TALKED_TO_MINER:
581 			return st_format_printf(pHistoryStrings[code], GCM->getTownName(h->ubSecondCode));
582 
583 		case HISTORY_MERC_KILLED:
584 			if (h->ubSecondCode == NO_PROFILE)
585 			{
586 				break;
587 			}
588 			return st_format_printf(pHistoryStrings[code], GetProfile(h->ubSecondCode).zName);
589 
590 		case HISTORY_HIRED_MERC_FROM_AIM:
591 		case HISTORY_HIRED_MERC_FROM_MERC:
592 		case HISTORY_MERC_CONTRACT_EXPIRED:
593 		case HISTORY_RPC_JOINED_TEAM:
594 			return st_format_printf(pHistoryStrings[code], GetProfile(h->ubSecondCode).zName);
595 
596 		case HISTORY_CANCELLED_INSURANCE:
597 		case HISTORY_DISQUALIFIED_BOXING:
598 		case HISTORY_EXTENDED_CONTRACT_1_DAY:
599 		case HISTORY_EXTENDED_CONTRACT_1_WEEK:
600 		case HISTORY_EXTENDED_CONTRACT_2_WEEK:
601 		case HISTORY_INSURANCE_CLAIM_PAYOUT:
602 		case HISTORY_LOST_BOXING:
603 		case HISTORY_MERC_FIRED:
604 		case HISTORY_MERC_KILLED_CHARACTER:
605 		case HISTORY_MERC_MARRIED_OFF:
606 		case HISTORY_MERC_QUIT:
607 		case HISTORY_NPC_KILLED:
608 		case HISTORY_PURCHASED_INSURANCE:
609 		case HISTORY_WON_BOXING:
610 			return st_format_printf(pHistoryStrings[code], GetProfile(h->ubSecondCode).zNickname);
611 
612 		// all simple history log msgs, no params
613 		case HISTORY_ACCEPTED_ASSIGNMENT_FROM_ENRICO:
614 		case HISTORY_ARNOLD:
615 		case HISTORY_ASSASSIN:
616 		case HISTORY_BOXING_MATCHES:
617 		case HISTORY_BUM_KEYCARD:
618 		case HISTORY_CHARACTER_GENERATED:
619 		case HISTORY_CHEAT_ENABLED:
620 		case HISTORY_CREATURESATTACKED:
621 		case HISTORY_DAVE:
622 		case HISTORY_DEFENDEDTOWNSECTOR:
623 		case HISTORY_DEIDRANNA_DEAD_BODIES:
624 		case HISTORY_DEVIN:
625 		case HISTORY_DISCOVERED_ORTA:
626 		case HISTORY_DISCOVERED_TIXA:
627 		case HISTORY_ENRICO_COMPLAINED:
628 		case HISTORY_ENTERED_HISTORY_MODE:
629 		case HISTORY_FATALAMBUSH:
630 		case HISTORY_FOUND_MONEY:
631 		case HISTORY_FRANZ:
632 		case HISTORY_FREDO:
633 		case HISTORY_GABBY:
634 		case HISTORY_GAVE_CARMEN_HEAD:
635 		case HISTORY_GOT_ROCKET_RIFLES:
636 		case HISTORY_HOWARD:
637 		case HISTORY_HOWARD_CYANIDE:
638 		case HISTORY_JAKE:
639 		case HISTORY_KEITH:
640 		case HISTORY_KEITH_OUT_OF_BUSINESS:
641 		case HISTORY_KILLEDBYBLOODCATS:
642 		case HISTORY_KINGPIN_MONEY:
643 		case HISTORY_KROTT:
644 		case HISTORY_KYLE:
645 		case HISTORY_LOSTBATTLE:
646 		case HISTORY_LOSTTOWNSECTOR:
647 		case HISTORY_MADLAB:
648 		case HISTORY_MIKE:
649 		case HISTORY_PABLO:
650 		case HISTORY_PERKO:
651 		case HISTORY_RICHGUY_BALIME:
652 		case HISTORY_SAM:
653 		case HISTORY_SETTLED_ACCOUNTS_AT_MERC:
654 		case HISTORY_SLAUGHTEREDBLOODCATS:
655 		case HISTORY_SLAY_MYSTERIOUSLY_LEFT:
656 		case HISTORY_SOMETHING_IN_MINES:
657 		case HISTORY_SUCCESSFULATTACK:
658 		case HISTORY_TALKED_TO_FATHER_WALKER:
659 		case HISTORY_TONY:
660 		case HISTORY_UNSUCCESSFULATTACK:
661 		case HISTORY_WALTER:
662 		case HISTORY_WIPEDOUTENEMYAMBUSH:
663 		case HISTORY_WONBATTLE:
664 			return pHistoryStrings[code];
665 	}
666 	return ST::null;
667 }
668 
669 
670 // look at what page we are viewing, enable and disable buttons as needed
SetHistoryButtonStates(void)671 static void SetHistoryButtonStates(void)
672 {
673 	EnableButton(giHistoryButton[PREV_PAGE_BUTTON], iCurrentHistoryPage != 1);
674 	EnableButton(giHistoryButton[NEXT_PAGE_BUTTON], iCurrentHistoryPage < GetNumberOfHistoryPages());
675 }
676 
677 
678 // loads in records belogning, to page uiPage
LoadInHistoryRecords(const UINT32 uiPage)679 static BOOLEAN LoadInHistoryRecords(const UINT32 uiPage)
680 try
681 {
682 	ClearHistoryList();
683 
684 	// check if bad page
685 	if (uiPage == 0) return FALSE;
686 
687 	AutoSGPFile f(GCM->openGameResForReading(HISTORY_DATA_FILE));
688 
689 	UINT       entry_count = FileGetSize(f) / SIZE_OF_HISTORY_FILE_RECORD;
690 	UINT const skip        = (uiPage - 1) * NUM_RECORDS_PER_PAGE;
691 	if (entry_count <= skip) return FALSE;
692 
693 	FileSeek(f, skip * SIZE_OF_HISTORY_FILE_RECORD, FILE_SEEK_FROM_START);
694 	entry_count -= skip;
695 
696 	if (entry_count > NUM_RECORDS_PER_PAGE) entry_count = NUM_RECORDS_PER_PAGE;
697 
698 	while (entry_count-- > 0)
699 	{
700 		UINT8  ubCode;
701 		UINT8  ubSecondCode;
702 		UINT32 uiDate;
703 		INT16  sSectorX;
704 		INT16  sSectorY;
705 		INT8   bSectorZ;
706 
707 		FileRead(f, &ubCode,       sizeof(UINT8));
708 		FileRead(f, &ubSecondCode, sizeof(UINT8));
709 		FileRead(f, &uiDate,       sizeof(UINT32));
710 		FileRead(f, &sSectorX,     sizeof(INT16));
711 		FileRead(f, &sSectorY,     sizeof(INT16));
712 		FileRead(f, &bSectorZ,     sizeof(INT8));
713 		FileSeek(f, 1, FILE_SEEK_FROM_CURRENT);
714 
715 		ProcessAndEnterAHistoryRecord(ubCode, uiDate,  ubSecondCode, sSectorX, sSectorY, bSectorZ);
716 	}
717 
718 	return TRUE;
719 }
720 catch (...) { return FALSE; }
721 
722 
723 // clear out old list of records, and load in next page worth of records
LoadNextHistoryPage(void)724 static void LoadNextHistoryPage(void)
725 {
726 	// now load in previous page's records, if we can
727 	if ( LoadInHistoryRecords( iCurrentHistoryPage + 1 ) )
728 	{
729 		iCurrentHistoryPage++;
730 	}
731 	else
732 	{
733 		LoadInHistoryRecords( iCurrentHistoryPage );
734 	}
735 	SetHistoryButtonStates();
736 	fReDrawScreenFlag = TRUE;
737 }
738 
739 
740 // clear out old list of records and load in previous page worth of records
LoadPreviousHistoryPage(void)741 static void LoadPreviousHistoryPage(void)
742 {
743 	if (iCurrentHistoryPage <= 1) return;
744 	LoadInHistoryRecords(--iCurrentHistoryPage);
745 	SetHistoryButtonStates();
746 	fReDrawScreenFlag = TRUE;
747 }
748 
749 
AppendHistoryToEndOfFile(void)750 static void AppendHistoryToEndOfFile(void)
751 {
752 	AutoSGPFile f(FileMan::openForAppend(HISTORY_DATA_FILE));
753 
754 	const HistoryUnit* const h = pHistoryListHead;
755 
756 	BYTE  data[12];
757 	DataWriter d{data};
758 	INJ_U8(d, h->ubCode)
759 	INJ_U8(d, h->ubSecondCode)
760 	INJ_U32(d, h->uiDate)
761 	INJ_I16(d, h->sSectorX)
762 	INJ_I16(d, h->sSectorY)
763 	INJ_I8(d, h->bSectorZ)
764 	INJ_SKIP(d, 1)
765 	Assert(d.getConsumed() == lengthof(data));
766 
767 	FileWrite(f, data, sizeof(data));
768 }
769 
770 
GetTimeQuestWasStarted(const UINT8 ubCode)771 UINT32 GetTimeQuestWasStarted(const UINT8 ubCode)
772 {
773 	iCurrentHistoryPage = 0;
774 	OpenAndReadHistoryFile();
775 
776 	UINT32 uiTime = 0;
777 	for (const HistoryUnit* h = pHistoryListHead; h != NULL; h = h->Next)
778 	{
779 		if (h->ubSecondCode == ubCode && h->ubCode == HISTORY_QUEST_STARTED)
780 		{
781 			uiTime = h->uiDate;
782 			break;
783 		}
784 	}
785 
786 	if (fInHistoryMode) LoadInHistoryRecords(iCurrentHistoryPage);
787 
788 	return uiTime;
789 }
790 
791 
GetQuestStartedString(const UINT8 ubQuestValue)792 static ST::string GetQuestStartedString(const UINT8 ubQuestValue)
793 {
794 	// open the file and copy the string
795 	return GCM->loadEncryptedString(BINARYDATADIR "/quests.edt", HISTORY_QUEST_TEXT_SIZE * ubQuestValue * 2, HISTORY_QUEST_TEXT_SIZE);
796 }
797 
798 
GetQuestEndedString(const UINT8 ubQuestValue)799 static ST::string GetQuestEndedString(const UINT8 ubQuestValue)
800 {
801 	// open the file and copy the string
802 	return GCM->loadEncryptedString(BINARYDATADIR "/quests.edt", HISTORY_QUEST_TEXT_SIZE * (ubQuestValue * 2 + 1), HISTORY_QUEST_TEXT_SIZE);
803 }
804 
GetNumberOfHistoryPages(void)805 static INT32 GetNumberOfHistoryPages(void)
806 {
807 	AutoSGPFile f(GCM->openGameResForReading(HISTORY_DATA_FILE));
808 
809 	const UINT32 uiFileSize = FileGetSize(f);
810 
811 	if (uiFileSize == 0) return 1;
812 
813 	return (uiFileSize / SIZE_OF_HISTORY_FILE_RECORD + NUM_RECORDS_PER_PAGE - 1) / NUM_RECORDS_PER_PAGE;
814 }
815