Alchemy
Loading...
Searching...
No Matches

save.c

Go to the documentation of this file.
1/// @file
2/// Save files and the save file menu.
3/// Part of battle_command.c, the menu object, compiled only through it.
4
6
7// The three save files. Each is two 4 KB records of backup memory: record
8// `file` holds the first half of the 8 KB save buffer and record
9// `file + SAVE_FILE_COUNT` the second.
10#define SAVE_FILE_COUNT 3
11#define SAVE_RECORD_BYTES 0x1000
12#define SAVE_NO_FREE_FILE 999
13
14// What the save functions return when they fail, after showing the message.
15#define SAVE_ERROR_READ -2 ///< a record could not be read
16/// Writing failed. "Failed to save." (or "Failed to erase.")
17#define SAVE_ERROR_WRITE -3
18#define SAVE_ERROR_ERASE -4 ///< erasing failed. "Failed to erase."
19#define SAVE_ERROR_NO_FILE -5 ///< no empty file to copy into
20/// No backup memory. "Unable to detect backup memory."
21#define SAVE_ERROR_NO_MEMORY -9
22
23/// Writes the play time `frames` (at 60 frames a second) into `out` as hours
24/// and minutes, "hhh:mm" with the hours right-aligned in three characters, and
25/// returns `out`. The time stops at 999:59.
26void *Text_FormatPlayTime(s32 frames, u8 *out) {
27 u8 digits[64];
28 u32 totalMinutes;
29 u32 hours;
30 s32 minutes;
31 u8 *digitsCursor;
32 u8 *textCursor;
33
34 totalMinutes = (u32)frames / (60 * 60);
35 if (totalMinutes > 999 * 60 + 59)
36 totalMinutes = 999 * 60 + 59;
37
38 hours = totalMinutes / 60;
39 minutes = totalMinutes % 60;
40
41 digitsCursor = UiText_FormatNumber(digits, hours, 3);
42 *out = *digitsCursor;
43 digitsCursor++;
44 textCursor = out + 1;
45 *textCursor = *digitsCursor;
46 textCursor++;
47 *textCursor = digitsCursor[1];
48 // Adding 100 gives the minutes a leading zero in their last two digits.
49 minutes += 100;
50 textCursor++;
51 *textCursor = ':';
52
53 digitsCursor = UiText_FormatNumber(digits, minutes, 2);
54 textCursor++;
55 *textCursor = digitsCursor[0];
56 textCursor++;
57 *textCursor = digitsCursor[1];
58 textCursor[1] = 0;
59
60 return out;
61}
62
63/// Returns the first save file whose summary shows it empty (level 0), or
64/// SAVE_NO_FREE_FILE when all three are in use. The summaries must be loaded.
66 u32 file;
67 struct SaveSummary *summary;
68
69 summary = gSaveWorkspace->summary;
70 for (file = 0; file < SAVE_FILE_COUNT; file++, summary++) {
71 if (summary->level == 0)
72 return file;
73 }
74 return SAVE_NO_FREE_FILE;
75}
76
77/// Returns how many save files there are, leaving out those whose summary has
78/// its send flag set when `excludeSent` is nonzero, or SAVE_ERROR_NO_MEMORY
79/// when the backup memory cannot be read.
81 s32 file;
82 s32 count;
83 struct SaveSummary *summary;
84
87 } else {
89 if (excludeSent != 0) {
90 summary = gSaveWorkspace->summary;
91 for (file = 0; file < SAVE_FILE_COUNT; file++) {
92 if ((s8)summary[file].sendFlag != 0)
93 count--;
94 }
95 }
96 }
98 return count;
99}
100
101/// The distance in bytes from one field of a save summary to another.
102#define SUMMARY_DISTANCE(from, to) \
103 ((u32)&((struct SaveSummary *)0)->to - (u32)&((struct SaveSummary *)0)->from)
104
105/// Loads the save file summaries for the title menu and returns how many files
106/// there are: plus 100 when every file has its send flag set, or
107/// SAVE_ERROR_NO_MEMORY when the backup memory cannot be read. Sets
108/// gTitleExtraOptionEnabled when a file has any of game flags 48-127 set, and
109/// gTitleSendOptionEnabled when a file has its send flag set and R and Left are
110/// held.
112 /* FAKEMATCH: the signed cursor from the party tail keeps the native literal
113 * base 0x1070 and flag offsets 1 and 2; as summary[i].sendFlag and
114 * .flagCount, 4 instructions differ in tbs-en. */
115 s32 error;
116 s32 sentCount;
117 s32 fileCount;
118
120 sentCount = 0;
121 fileCount = SAVE_ERROR_NO_MEMORY;
122 if (error == 0) {
123 s32 i;
124 s8 *flags;
125
126 fileCount = SaveState_LoadSummaryRecords();
127 flags = (s8 *)&gSaveWorkspace->summary[0].party[4];
129 (*(s16 *)&gTitleExtraOptionEnabled) = 0;
130 for (i = 0; i < SAVE_FILE_COUNT; i++) {
131 if (flags[i * sizeof(struct SaveSummary) +
132 SUMMARY_DISTANCE(party[4], sendFlag)] != 0) {
134 sentCount++;
135 }
136 if (flags[i * sizeof(struct SaveSummary) +
137 SUMMARY_DISTANCE(party[4], flagCount)] != 0) {
138 (*(s16 *)&gTitleExtraOptionEnabled) = 1;
139 }
140 }
141
142 if ((gKeysHeld & (KEY_R | KEY_LEFT)) != (KEY_R | KEY_LEFT)) {
144 }
145 }
147 if (fileCount != 0 && sentCount == fileCount) {
148 return fileCount + 100;
149 }
150 return fileCount;
151}
152
153/// Prepares the save buffer for writing: stamps the game state with the time
154/// and options, then fills the file summary shown on the file menus (the
155/// leader's name, level and class, the play time, area, coins, Djinn of each
156/// element, the party, the send flag and how many of game flags 48-127 are
157/// set) and its checksum, the sum of the 968 words from gGameFlagBytes on.
158/// Returns the checksum.
160 u16 members[14];
161 struct SaveSummary *summary;
162 struct BattleUnit *leader;
163 u8 *source;
164 u8 *destination;
165 u32 *word;
166 s32 i;
167 u32 sum = 0;
168
169 // The summary's 16-byte header comes before the save buffer.
170 summary = (struct SaveSummary *)((u32)gSaveBuffer - 16);
173 {
174 u32 *copy = (u32 *)gSceneState;
175 copy[64] = gPlayTimeFrames;
176 }
178
180 destination = summary->name;
181 source = leader->name;
182 for (i = 11; i >= 0; i--)
183 *destination++ = *source++;
184 summary->level = leader->level;
185 summary->playTime = gGameState.playTime;
186 summary->area =
188 summary->classId = leader->classIndex;
189 summary->coins = gGameState.coins;
190 summary->djinn[0] = Party_SumDjinnCountsFar(0);
191 summary->djinn[1] = Party_SumDjinnCountsFar(1);
192 summary->djinn[2] = Party_SumDjinnCountsFar(2);
193 summary->djinn[3] = Party_SumDjinnCountsFar(3);
194 // Up to four party members, ended by -1.
196 for (i = 0; i <= 3 && members[i] != 255; i++)
197 summary->party[i] = members[i];
198 summary->party[i] = -1;
199 summary->paletteGlow = gGameState.paletteGlow[0];
201 summary->sendFlag = gGameState.unk20f;
202 summary->flagCount = 0;
203 for (i = 48; i <= 127; i++) {
204 if (GameFlag_TestFar(i))
205 summary->flagCount++;
206 }
208 {
209 s32 n;
210 u32 *frame = &gGameState.buildStamp;
211 u32 frames;
212
213 n = 242;
214 /* FAKEMATCH(one-pass): a never-taken null arm's one-pass block ends CSE
215 * with the bound constant ahead of the frame load; with no arm, 2 of 174
216 * instructions differ in tbs-en. */
217 if (!frame) {
218 do {
219 n = 242;
220 } while (0);
221 } else {
222 n = 242;
223 frames = *frame;
224 }
225 frames = *frame;
226 word = (u32 *)gGameFlagBytes;
227 i = 0;
228 n <<= 2;
229 summary->frames = frames;
230 // Entered at its test: as a while loop it gains a second test (4 bytes).
231 goto test;
232 do {
233 sum += *word++;
234 i++;
235 test:;
236 } while (i < n);
237 }
238 summary->checksum = sum;
239 return sum;
240}
241
242/// Saves the game to the current file (gSaveSlot): builds the summary and
243/// writes both halves of the save buffer. Returns 0, gSaveSlot itself (-1)
244/// when no file is chosen, or a SAVE_ERROR_* after showing its message.
246 s16 file;
247 s16 result;
248 s32 error;
249
250 result = 0;
251 file = gSaveSlot;
252 if (file != -1) {
254 if (error != 0) {
255 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
256 result = SAVE_ERROR_NO_MEMORY;
257 } else {
259 {
260 void *half = &gSaveBuffer;
261 s32 current;
262
263 error = SaveState_WriteRecord(gSaveSlot, half);
264 current = gSaveSlot;
265 half = (char *)half + SAVE_RECORD_BYTES;
266 error |= SaveState_WriteRecord(current + SAVE_FILE_COUNT, half);
267 if (error != 0) {
268 UiText_ShowPositionedMessageAndWait(MsgSaveFailed, 1);
269 result = SAVE_ERROR_WRITE;
270 }
271 }
272 }
274 file = result;
275 }
276 return file;
277}
278
279/// Writes both halves of the save buffer, as they are, to save file `file`.
280/// Returns 0, or a SAVE_ERROR_* after showing its message.
282 s32 error;
283 s16 result = 0;
284
286 if (error != 0) {
287 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
288 result = SAVE_ERROR_NO_MEMORY;
289 } else {
290 void *half = &gSaveBuffer;
291
292 error = SaveState_WriteRecord(file, half);
293 half = (char *)half + SAVE_RECORD_BYTES;
294 error |= SaveState_WriteRecord(file + SAVE_FILE_COUNT, half);
295 if (error != 0) {
296 UiText_ShowPositionedMessageAndWait(MsgSaveFailed, 1);
297 result = SAVE_ERROR_WRITE;
298 }
299 }
301 return result;
302}
303
304/// Writes the 64-byte save stamp (gSaveStamp, which lies in the first half of
305/// the save buffer) into the current file's first record, leaving the rest of
306/// the record as it was saved. Returns 0, -1 when no file is chosen, or a
307/// SAVE_ERROR_* after showing its message.
309 void *record;
310 s32 result;
311 s32 error;
312 u8 *stamp;
313
315 result = 0;
316 if (gSaveSlot == -1)
317 return -1;
319 if (error != 0) {
320 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
321 result = SAVE_ERROR_NO_MEMORY;
322 } else {
323 error = SaveState_ReadRecordPayload(gSaveSlot, record);
324 if (error != 0) {
325 UiText_ShowPositionedMessageAndWait(MsgSaveFailed, 1);
326 result = SAVE_ERROR_READ;
327 }
328 // The stamp's place in the record is its place in the save buffer.
329 stamp = (u8 *)record + (s32)gSaveStamp;
330 stamp -= (s32)gSaveBuffer;
331 Iwram_CopyWords(stamp, gSaveStamp, 16);
332 error = SaveState_WriteRecord(gSaveSlot, record);
333 if (error != 0) {
334 UiText_ShowPositionedMessageAndWait(MsgSaveFailed, 1);
335 result = SAVE_ERROR_WRITE;
336 }
337 }
339 MemFree(record);
340 return result;
341}
342
343/// Asks whether to save over the current file and, on Yes, writes the save
344/// stamp into it (SaveState_ProcessSelectedSlot) and says so. "OK to save over
345/// this file?" and "Your game was saved." Returns that result.
346/// Nothing calls it.
348 s32 result;
349
350 // UB: on No, result is returned without ever being set.
351#ifdef UBFIX
352 result = 0;
353#endif
354
355 UiText_OpenMessageWindow(MsgOverwriteConfirm, 8, 12, 2);
356 while (UiWork_IsComplete() == 0) {
357 WaitFrames(1);
358 }
359 if (Menu_RunConfirmSelection(1, 0, 0, 1)) {
361 } else {
365 if (result >= 0) {
367 }
368 }
369 return result;
370}
371
372/// The file menu's Copy: lets the player choose a file, reads both of its
373/// records into the save buffer and writes them to the first empty file.
374/// Returns 0, -1 when cancelled, or a SAVE_ERROR_* after showing its message
375/// (the game says "Failed to erase." both when no file is empty and when the
376/// write fails).
378 u32 error;
379 s32 file;
380 s32 result;
381
382 result = 0;
384 if (error != 0) {
385 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
386 result = SAVE_ERROR_NO_MEMORY;
387 } else {
389 file = SaveMenu_SelectSlot(0, 2);
390 if (file == -1) {
391 result = file;
392 } else {
393 void *lower = &gSaveBuffer;
394 void *upper;
395
396 error = SaveState_ReadRecordPayload(file, lower);
397 upper = (u8 *)lower + SAVE_RECORD_BYTES;
398 error |= SaveState_ReadRecordPayload(file + SAVE_FILE_COUNT, upper);
399 if (error != 0) {
400 UiText_ShowPositionedMessageAndWait(MsgLoadFailed, 1);
401 result = SAVE_ERROR_READ;
402 } else {
404 if (file == SAVE_NO_FREE_FILE) {
405 UiText_ShowPositionedMessageAndWait(MsgEraseFailed, 1);
406 result = SAVE_ERROR_NO_FILE;
407 } else {
408 error = SaveState_WriteRecord(file, lower);
409 error |= SaveState_WriteRecord(file + SAVE_FILE_COUNT, upper);
410 if (error != 0) {
411 UiText_ShowPositionedMessageAndWait(MsgEraseFailed, 1);
412 result = SAVE_ERROR_WRITE;
413 } else {
414 UiText_ShowPositionedMessageAndWait(MsgFileCopied, 1);
415 }
416 }
417 }
418 }
419 }
420
422 return result;
423}
424
425/// The file menu's Erase: lets the player choose a file, asks whether to erase
426/// it and on Yes deletes both of its records. "OK to erase this?" Returns 0
427/// (also when the player answers No), -1 when cancelled, or a SAVE_ERROR_*
428/// after showing its message.
430 s32 error;
431 s32 file;
432 s32 result = 0;
433
435 if (error != 0) {
436 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
437 result = SAVE_ERROR_NO_MEMORY;
438 } else {
440 file = SaveMenu_SelectSlot(0, 3);
441 if (file == -1) {
442 result = file;
443 } else {
444 UiText_OpenMessageWindow(MsgEraseConfirm, 8, 1, 2);
445 while (UiWork_IsComplete() == 0) {
446 WaitFrames(1);
447 }
448 if (Menu_RunConfirmSelection(1, 0, 3, 1) != 0) {
450 } else {
452 error = SaveState_DeleteRecord(file);
454 if (error != 0) {
455 UiText_ShowPositionedMessageAndWait(MsgEraseFailed, 1);
456 result = SAVE_ERROR_ERASE;
457 } else {
458 UiText_ShowPositionedMessageAndWait(MsgFileErased, 1);
459 }
460 }
461 }
462 }
464 return result;
465}
466
467#if LANGUAGE != LANGUAGE_JAPANESE
468
469/// A scheduler callback that makes colours 8-11 of BG palette 14 glow: each
470/// follows a sine wave of the frame counter, eight frames behind the one
471/// before. With s the sine from -1 to 1, the colour is red 22 + 2s, green
472/// 16 + s and blue 20 + s (of 31).
474 s32 *tick;
475 volatile u16 *palette;
476 s32 colour;
477 s32 sine;
478 s32 i;
479
480 tick = (s32 *)&gFrameTick;
481 palette = &BG_PLTT_COLOR(14, 8);
482 i = 0;
483 do {
484#ifdef UBFIX
485 // Wrap the frame phase before Trig_Sin takes its low sixteen bits.
486 sine = Trig_Sin((((u32)*tick + i * 8U) * 0x300U) & 0xffffU);
487#else
488 sine = Trig_Sin((*tick + i * 8) * 0x300);
489#endif
490 colour = sine / 16384;
491 {
492#ifdef UBFIX
493 // A negative sine must be shifted as unsigned bits.
494 u32 red = (u32)colour << 1;
495#else
496 u32 red = colour << 1;
497#endif
498 u32 green = colour;
499
500 red += 22;
501 green += 16;
502 colour += 20;
503 /* FAKEMATCH(one-pass): the blue shift in a block of its own stays after
504 * the three adds, in source order; as a plain shift, the red add moves
505 * below the shifts and 6 of 33 instructions differ in tbs-en. */
506 do {
507 colour <<= 10;
508 } while (0);
509 green <<= 5;
510 colour |= green;
511 colour |= red;
512 }
513 /* FAKEMATCH(one-pass): the store in a block of its own stays ahead of the
514 * loop index's increment; as a plain store, 3 of 33 instructions differ
515 * in tbs-en. */
516 do {
517 *palette++ = colour;
518 } while (0);
519 } while (++i <= 3);
520}
521
522#endif
523
524#if TBS_PATCH_1
525
526/// Starts the palette glow (PaletteGlow_UpdateSine) in the scheduler.
529}
530
533}
534
535/// Fills the `width` x `height` tile rectangle at (x, y) inside `window`,
536/// clipped to the screen as UiWindow_SetTileAttributeBitRect clips, with the
537/// blank tile entry 0xe006 of the UI tilemap.
538void UiWindow_FillTilemapRect(struct UiWindow *window, s32 x, s32 y, s32 width,
539 s32 height) {
540 u16 *tilemap = (u16 *)gWindowWork[0];
541 s32 sum;
542
543 sum = x + window->x;
544 x = sum + 1;
545 sum = y + (window)->y;
546 y = sum + 1;
547
548 if (x < 0) {
549 width += x;
550 x = 0;
551 }
552 if (x + width > 29)
553 width = 30 - x;
554
555 if (y < 0) {
556 height += y;
557 y = 0;
558 }
559 if (y + height > 29)
560 height = 20 - y;
561
562 if (width > 0 && height > 0) {
563 // Byte offsets: 32 entries of 2 bytes a row.
564 s32 yOffset = y << 6;
565 s32 xOffset = x << 1;
566 s32 offset = yOffset + xOffset;
567 do {
568 s32 remaining = width;
569 u16 *cell = (u16 *)(offset + (s32)tilemap);
570 if (remaining != 0) {
571 do {
572 remaining--;
573 *cell++ = 0xE006;
574 } while (remaining != 0);
575 }
576 height--;
577 offset += 64;
578 } while (height != 0);
579 ((struct UiRenderWork *)tilemap)->dirty = 1;
580 }
581}
582
583#endif
584
585/// The save file menu shows the chosen file's party as walking sprites in a
586/// row, and four Djinn sprites, one for each element, over the file's Djinn
587/// counts. Each sprite stands three tiles (24 pixels) from the one before.
588#define SAVE_MENU_SPRITE_SPACING 3
589/// Scheduler priority of the callbacks that place the sprites every frame.
590#define SAVE_MENU_SPRITE_PRIORITY (200 << 4)
591
592/// Shows the party of save file `summary` (up to four members) as sprites in
593/// a row from tile (x, y) inside `window`, and starts placing them every
594/// frame (SaveMenu_PlacePartySprites).
595void SaveMenu_ShowPartySprites(struct UiWindow *window, s32 x, s32 y,
596 struct SaveSummary *summary) {
597 struct ShopRuntime *menu = gMenuWork;
598 s32 i;
599 s32 priority;
600
601 for (i = 0; i < 4 && summary->party[i] != -1; i++) {
602 struct AnimationObject *sprite =
604 summary->party[i], (s8)summary->hasFlag20));
605
606 if (sprite != NULL) {
608 sprite->flags = 0;
609 sprite->image.priority = 0;
610 }
611
612 menu->partyMemberIcons[i] = sprite;
613 menu->partyMemberX[i] =
614 (x + window->x + i * SAVE_MENU_SPRITE_SPACING) * 8 + 16;
615 menu->partyMemberY[i] = (y + window->y) * 8 + 16;
616 menu->partyMemberScale[i] = 0x10000;
617 }
618
619 priority = 200;
620 priority <<= 4;
622}
623
624/// Stops placing the party sprites of the save file menu and releases them.
626 struct ShopRuntime *menu = gMenuWork;
627 s32 i;
628
630 for (i = 0; i < 4; i++) {
631 if (menu->partyMemberIcons[i] != NULL) {
633 menu->partyMemberIcons[i] = NULL;
634 }
635 }
636}
637
638/// A scheduler callback: draws each party sprite of the save file menu at its
639/// place and scale for this frame.
641 struct ShopRuntime *menu = gMenuWork;
642 struct SpriteScale scale;
643 struct SpritePlacement position;
644 s32 index = 0;
645 struct AnimationObject *sprite;
646
647 while (1) {
648 sprite = menu->partyMemberIcons[index];
649 if (sprite != 0) {
650 // Offset height and y equally to keep the visible row fixed.
651 scale.x = menu->partyMemberScale[index];
652 scale.y = menu->partyMemberScale[index];
653 position.x = menu->partyMemberX[index] << 16;
654 position.height = 500 << 16;
655 position.y = (menu->partyMemberY[index] << 16) + (500 << 16);
656 position.groundHeight = 0;
657 Object_ApplyProjectedPlacementFar(sprite, &position, &scale, 0x4000);
658 }
659 index += 1;
660 if (index > 3) {
661 break;
662 }
663 }
664}
665
666/// Shows the four Djinn sprites, one for each element, in a row from tile
667/// (x, y) inside `window`, and starts placing them every frame
668/// (SaveMenu_PlaceDjinnSprites). Does nothing when `window` is NULL.
669void SaveMenu_ShowDjinnSprites(struct UiWindow *window, s32 x, s32 y) {
670 struct FourObjectMotionState *menu = gMenuWork;
671 s32 i;
672
673 if (window != NULL) {
674 for (i = 0; i < 4; i++) {
675 struct AnimationObject *sprite =
677
678 if (sprite != NULL) {
680 sprite->flags = 0;
681 sprite->image.priority = 0;
682 }
683
684 menu->objects[i] = sprite;
685 menu->positionsX[i] =
686 (window->x + x + i * SAVE_MENU_SPRITE_SPACING) * 8 + 16;
687 menu->positionsY[i] = (window->y + y) * 8 + 16;
688 }
689
692 }
693}
694
695/// Stops placing the Djinn sprites of the save file menu and releases them.
697 struct FourObjectMotionState *menu = gMenuWork;
698 s32 i;
699
701 for (i = 0; i < 4; i++) {
702 if (menu->objects[i] != NULL) {
704 menu->objects[i] = NULL;
705 }
706 }
707}
708
709/// A scheduler callback: draws each Djinn sprite of the save file menu at its
710/// place, at scale 1.0, for this frame.
712 struct FourObjectMotionState *menu = gMenuWork;
713 struct SpriteScale scale;
714 struct SpritePlacement position;
715 s32 index;
716
717 index = 0;
718 do {
719 struct AnimationObject *sprite = menu->objects[index];
720
721 if (sprite != NULL) {
722 s32 one = 0x10000;
723 s32 height = 500 << 16;
724
725 scale.x = one;
726 scale.y = one;
727 position.x = menu->positionsX[index] << 16;
728 position.height = height;
729 position.y = (menu->positionsY[index] << 16) + height;
730 position.groundHeight = 0;
731 Object_ApplyProjectedPlacementFar(sprite, &position, &scale, 0x4000);
732 }
733 index++;
734 } while (index <= 3);
735}
736
737/// Draws the Djinn counts of save file `summary`, one for each element, two
738/// digits each, 24 pixels apart on row 16 of `window`, under the Djinn
739/// sprites. Does nothing when `window` is NULL.
741 struct SaveSummary *summary) {
742 s32 i;
743
744 if (window != NULL) {
745 for (i = 0; i < 4; i++)
746 UiText_DrawNumberAtOffset((s8)summary->djinn[i], 2, window,
747 i * SAVE_MENU_SPRITE_SPACING * 8, 16);
748 }
749}
750
751// Where each edition draws the "Lv" label and the coin label, in pixels.
752#if (LANGUAGE == LANGUAGE_SPANISH)
753#define SUMMARY_LEVEL_X 64
754#define SUMMARY_COINS_X 56
755#define SUMMARY_DRAW_LEVEL UiText_DrawStringAtOffset
756#elif (LANGUAGE == LANGUAGE_FRENCH)
757#define SUMMARY_LEVEL_X 59
758#define SUMMARY_COINS_X 56
759#define SUMMARY_DRAW_LEVEL UiText_DrawStringAtOffset
760#else
761#define SUMMARY_LEVEL_X 72
762#define SUMMARY_COINS_X 48
763#define SUMMARY_DRAW_LEVEL UiText_DrawStringInWindow
764#endif
765
766/// Draws the summary of save file `summary` into `window`: the leader's name,
767/// level and class, the play time and the coins. Does nothing when `window`
768/// is 0.
770 struct SaveSummary *summary) {
771 u32 playTime[4]; // the play time text
772 s32 y;
773
774 if (window != 0) {
776 UiWindow_DrawDividerLine(window, 0, 4, 13, 4);
777 UiText_DrawStringAtOffset(summary->name, window, 0, 0);
778 y = 0;
780 0);
781 UiText_DrawNumberAtOffset(summary->level, 2, window, 80, y);
782 UiText_DrawCharacterAtOffset(summary->classId + MsgClassName, window, 0,
783 16);
784#if !TBS_PATCH_1
785 // Coins on the third row, then "Play Time:" and the time.
786 y = 32;
787 UiText_DrawNumberInWindow(summary->coins, 6, window, 24, y);
788 UiText_DrawCharacterAtOffset(MsgCoins, window, 72, 32);
789 UiText_DrawCharacterAtOffset(MsgStatusLabel, window, 0, 48);
791 Text_FormatPlayTime(summary->playTime, (u8 *)playTime), window, 48, 48);
792#else
793 // "Play Time:" with the time under it, then the coins.
794 UiText_DrawCharacterAtOffset(MsgStatusLabel, window, 0, 32);
796 Text_FormatPlayTime(summary->playTime, (u8 *)playTime), window, 48, 40);
797 y = 48;
798 UiText_DrawNumberInWindow(summary->coins, 6, window, 0, y);
799#if (LANGUAGE == LANGUAGE_FRENCH)
800 // French takes the singular "Pièce" for one coin or none.
801 if (summary->coins > 1)
802 UiText_DrawCharacterAtOffset(MsgCoins, window, SUMMARY_COINS_X, 48);
803 else
804 UiText_DrawCharacterAtOffset(MsgPiece, window, SUMMARY_COINS_X, 48);
805#else
806 UiText_DrawCharacterAtOffset(MsgCoins, window, SUMMARY_COINS_X, 48);
807#endif
808#endif
809 }
810}
811
812/// What the save file menu is choosing a file for, which decides the files
813/// the cursor may stop on.
815 SAVE_MENU_SAVE, ///< saving: any file, empty ones too
816 SAVE_MENU_CONTINUE, ///< continuing: files not yet cleared
817 SAVE_MENU_COPY, ///< copying: any file with data
818 SAVE_MENU_ERASE, ///< erasing: any file with data
819 SAVE_MENU_EXTRA, ///< files with any of game flags 48-127 set
820 SAVE_MENU_CLEARED ///< cleared files only
821};
822
823/// SaveMenu_SelectSlot's result when no file can be chosen in its mode.
824#define SAVE_MENU_NO_FILE -2
825
826// The save file menu: lists the three files with each one's leader and area
827// (or why it cannot be chosen) and, for the file under the cursor, its
828// summary, its party and its Djinn, with the file's own palette glow. The
829// cursor starts on `file` and moves up and down over the files `mode` allows
830// (see SaveMenuMode). Returns the file chosen with A, -1 when cancelled with
831// B, or SAVE_MENU_NO_FILE when no file is allowed.
832#if TBS_PATCH_1 || LANGUAGE == LANGUAGE_JAPANESE
833
834s32 SaveMenu_SelectSlot(s32 selectedFile, s32 menuMode) {
835 struct UiWindow *fileList;
836#if TBS_PATCH_1
837 struct SaveWorkspace *save;
838 struct UiWindow *summaryWindow;
839 struct UiWindow *partyWindow;
840 struct UiWindow *djinnWindow;
841 struct SaveSummary *summary;
842 struct RenderOutput *modePicture;
843 s32 summaryDirty;
844 struct UiRenderWork *render;
845 u32 buildStamp;
846 struct ShopRuntime *spriteWork;
847 s32 result;
848 s32 i;
849 s32 y;
850#else
851 struct UiWindow *summaryWindow;
852 struct SaveWorkspace *save;
853 struct UiWindow *partyWindow;
854 struct UiWindow *djinnWindow;
855 struct SaveSummary *summary;
856 struct RenderOutput *modePicture;
857 s32 cursorStyle;
858 s32 summaryDirty;
859 struct UiRenderWork *render;
860 u32 buildStamp;
861 struct ShopRuntime *spriteWork;
862 struct ShopCursor fileCursor;
863 s32 result;
864 s32 i;
865 s32 x;
866 s32 y;
867 s32 vramSlot;
868 struct RenderOutput *cursorSprite;
869#endif
870
871 spriteWork = Runtime_AllocateHeapBlock(HEAP_SLOT_MENU, sizeof(*spriteWork));
872 save = *(gWorkSlot + HEAP_SLOT_SAVE);
873 summaryWindow = NULL;
874 partyWindow = NULL;
875 djinnWindow = NULL;
876 summaryDirty = 1;
877 render = *(gWorkSlot + HEAP_SLOT_WINDOW);
878 buildStamp = Runtime_GetBuildStampTimeFar();
879 // Start on the first file the mode allows.
880 if (selectedFile < 0)
881 selectedFile = 0;
882#if TBS_PATCH_1
883 if (menuMode == SAVE_MENU_CONTINUE) {
884 for (i = 0;
885 i < SAVE_FILE_COUNT && (save->summary[selectedFile].level == 0 ||
886 (s8)save->summary[selectedFile].sendFlag != 0);
887 i++) {
888 selectedFile++;
889 if (selectedFile == SAVE_FILE_COUNT)
890 selectedFile = 0;
891 }
892 if (i == SAVE_FILE_COUNT) {
893 result = SAVE_MENU_NO_FILE;
894 goto done;
895 }
896 } else if (menuMode == SAVE_MENU_EXTRA) {
897 for (i = 0; i < SAVE_FILE_COUNT &&
898 (save->summary[selectedFile].level == 0 ||
899 (s8)save->summary[selectedFile].flagCount == 0);
900 i++) {
901 selectedFile++;
902 if (selectedFile == SAVE_FILE_COUNT)
903 selectedFile = 0;
904 }
905 if (i == SAVE_FILE_COUNT) {
906 result = SAVE_MENU_NO_FILE;
907 goto done;
908 }
909 } else if (menuMode == SAVE_MENU_CLEARED) {
910 for (i = 0;
911 i < SAVE_FILE_COUNT && (save->summary[selectedFile].level == 0 ||
912 (s8)save->summary[selectedFile].sendFlag == 0);
913 i++) {
914 selectedFile++;
915 if (selectedFile == SAVE_FILE_COUNT)
916 selectedFile = 0;
917 }
918 if (i == SAVE_FILE_COUNT) {
919 result = SAVE_MENU_NO_FILE;
920 goto done;
921 }
922 } else if (menuMode != SAVE_MENU_SAVE) {
923 for (i = 0; i < SAVE_FILE_COUNT && (save->summary[selectedFile].level == 0);
924 i++) {
925 selectedFile++;
926 if (selectedFile == SAVE_FILE_COUNT)
927 selectedFile = 0;
928 }
929 if (i == SAVE_FILE_COUNT) {
930 result = SAVE_MENU_NO_FILE;
931 goto done;
932 }
933 }
934
935 // The file list, and the mode's sprite above it.
936 Dma_Fill32(0, spriteWork, sizeof(*spriteWork));
938 fileList = UiWindow_Create(1, 2, 28, 7, 2);
939 summary = save->summary;
940 y = 0;
941 for (i = 0; i < SAVE_FILE_COUNT; i++, y += 16, summary++) {
942 if (summary->level == 0) {
943 UiText_DrawResource(MsgSaveNoData, fileList, 10, y);
944 } else if (summary->frames < buildStamp) {
945 // Saved by an older build.
946 UiText_DrawResource(MsgSaveContinue, fileList, 10, y);
947 } else if (summary->playTime != *(u32 *)summary->unk38) {
948 UiText_DrawResource(MsgSaveCorrupted, fileList, 10, y);
949 } else if (menuMode == SAVE_MENU_CLEARED && (s8)summary->sendFlag == 0) {
950 UiText_DrawResource(MsgSaveNotCleared, fileList, 10, y);
951 } else {
952 UiText_DrawString(summary->name, fileList, SAVE_SLOT_NAME_X, y);
953 UiText_DrawResource(summary->area + MsgDebugEntryName, fileList, 62, y);
954 render->dirty = 1;
955 }
956 }
957 UiWindow_DrawDividerLine(fileList, 0, 2, 27, 2);
958 UiWindow_DrawDividerLine(fileList, 0, 4, 27, 4);
959#else
960 if (menuMode != SAVE_MENU_SAVE) {
961 for (i = 0; i < SAVE_FILE_COUNT && (save->summary[selectedFile].level == 0);
962 i++) {
963 selectedFile++;
964 if (selectedFile == SAVE_FILE_COUNT)
965 selectedFile = 0;
966 }
967 if (i == SAVE_FILE_COUNT) {
968 result = SAVE_MENU_NO_FILE;
969 goto done;
970 }
971 }
972 if (menuMode == SAVE_MENU_CONTINUE) {
973 for (i = 0;
974 i < SAVE_FILE_COUNT && ((s8)save->summary[selectedFile].sendFlag != 0);
975 i++) {
976 selectedFile++;
977 if (selectedFile == SAVE_FILE_COUNT)
978 selectedFile = 0;
979 }
980 if (i == SAVE_FILE_COUNT) {
981 result = SAVE_MENU_NO_FILE;
982 goto done;
983 }
984 }
985 if (menuMode == SAVE_MENU_EXTRA) {
986 for (i = 0; i < SAVE_FILE_COUNT &&
987 ((s8)save->summary[selectedFile].flagCount == 0);
988 i++) {
989 selectedFile++;
990 if (selectedFile == SAVE_FILE_COUNT)
991 selectedFile = 0;
992 }
993 if (i == SAVE_FILE_COUNT) {
994 result = SAVE_MENU_NO_FILE;
995 goto done;
996 }
997 }
998 if (menuMode == SAVE_MENU_CLEARED) {
999 for (i = 0;
1000 i < SAVE_FILE_COUNT && ((s8)save->summary[selectedFile].sendFlag == 0);
1001 i++) {
1002 selectedFile++;
1003 if (selectedFile == SAVE_FILE_COUNT)
1004 selectedFile = 0;
1005 }
1006 if (i == SAVE_FILE_COUNT) {
1007 result = SAVE_MENU_NO_FILE;
1008 goto done;
1009 }
1010 }
1011
1012 // The file list, and the mode's sprite above it.
1013 Dma_Fill32(0, spriteWork, sizeof(*spriteWork));
1014 fileList = UiWindow_Create(1, 2, 28, 7, 2);
1015
1016 for (i = 0, summary = save->summary; i < SAVE_FILE_COUNT; i++, summary++) {
1017 if (save->summary[i].level == 0) {
1018 UiText_DrawResource(MsgSaveNoData, fileList, 10, i * 16);
1019 } else if (save->summary[i].frames < buildStamp) {
1020 // Saved by an older build.
1021 UiText_DrawResource(MsgSaveContinue, fileList, 10, i * 16);
1022 } else if (save->summary[i].playTime != *(u32 *)save->summary[i].unk38) {
1023 UiText_DrawResource(MsgSaveCorrupted, fileList, 10, i * 16);
1024 } else if (menuMode == SAVE_MENU_CLEARED &&
1025 (s8)save->summary[i].sendFlag == 0) {
1026 UiText_DrawResource(MsgSaveNotCleared, fileList, 10, i * 16);
1027 } else {
1028 y = i * 16;
1029 UiText_DrawString(summary->name, fileList, SAVE_SLOT_NAME_X, y);
1030 UiText_DrawResource(save->summary[i].area + MsgDebugEntryName, fileList,
1031 72, y);
1032 render->dirty = 1;
1033 }
1034 }
1035 UiWindow_DrawDividerLine(fileList, 0, 2, 27, 2);
1036 UiWindow_DrawDividerLine(fileList, 0, 4, 27, 4);
1037 vramSlot = Resource_FindFreeEntry();
1038 if (vramSlot < VRAM_CACHE_ENTRY_COUNT) {
1040 x = -12;
1041 y = selectedFile * 16 + 4;
1042 cursorSprite =
1043 RenderOutput_Create(vramSlot, OUTPUT_SIZE_16X16, fileList, x, y);
1044 cursorSprite->active = 1;
1045 fileCursor.anchor = cursorSprite;
1046 }
1047 cursorStyle = 1;
1048#endif
1049 modePicture = RenderResource_CreatePair(menuMode, fileList, 72, -24);
1050
1051 while (1) {
1052 // Show the file under the cursor after it moves.
1053 if (summaryDirty != 0) {
1054 summaryDirty = 0;
1055 if (save->summary[selectedFile].level != 0) {
1056 PaletteGlow_Update(save->summary[selectedFile].paletteGlow,
1057 save->summary[selectedFile].paletteGlowSub);
1058 if (summaryWindow == NULL)
1059 summaryWindow = UiWindow_Create(1, 10, 14, 9, 2);
1060 SaveMenu_DrawFileSummary(summaryWindow, &save->summary[selectedFile]);
1061 WaitFrames(1);
1062 if (partyWindow == NULL)
1063 partyWindow = UiWindow_Create(16, 10, 13, 3, 2);
1065 SaveMenu_ShowPartySprites(partyWindow, 0, 0,
1066 &save->summary[selectedFile]);
1067 WaitFrames(1);
1068 if ((s8)save->summary[selectedFile].djinn[0] +
1069 (s8)save->summary[selectedFile].djinn[1] +
1070 (s8)save->summary[selectedFile].djinn[2] +
1071 (s8)save->summary[selectedFile].djinn[3] !=
1072 0) {
1073 if (djinnWindow == NULL)
1074 djinnWindow = UiWindow_Create(16, 14, 13, 5, 2);
1075 SaveMenu_DrawDjinnCounts(djinnWindow, &save->summary[selectedFile]);
1077 SaveMenu_ShowDjinnSprites(djinnWindow, 0, 0);
1078 } else {
1080 UiWork_Finalize(djinnWindow, 2);
1081 djinnWindow = NULL;
1082 }
1083 } else {
1088 UiWork_Finalize(djinnWindow, 2);
1089 UiWork_Finalize(partyWindow, 2);
1090 UiWork_Finalize(summaryWindow, 2);
1091 djinnWindow = NULL;
1092 partyWindow = NULL;
1093 summaryWindow = NULL;
1094 }
1095#if TBS_PATCH_1
1097 UiWindow_DrawDividerLine(fileList, 0, 2, 27, 2);
1098 UiWindow_DrawDividerLine(fileList, 0, 4, 27, 4);
1099 UiWindow_FillTilemapRect(fileList, 0, selectedFile * 2, 26, 1);
1100 }
1101#else
1102 x = fileList->x * 8 - 4;
1103 y = (fileList->y << 3) + selectedFile * 16 + 12;
1104 Shop_SetCursorFar(&fileCursor, x, y, cursorStyle);
1105 cursorStyle = 3;
1106 }
1107 ShopCursor_AdvanceFar(&fileCursor);
1108#endif
1109 RenderOutput_BobPair(modePicture);
1110 WaitFrames(1);
1111 // Up and down move to the previous or next file the mode allows.
1112 if (gKeysRepeat & KEY_UP) {
1114 summaryDirty = 1;
1115 do {
1116 selectedFile = (selectedFile + SAVE_FILE_COUNT - 1) % SAVE_FILE_COUNT;
1117 if (menuMode == SAVE_MENU_SAVE)
1118 break;
1119 } while ((save->summary[selectedFile].level == 0 ||
1120 (menuMode == SAVE_MENU_CONTINUE &&
1121 (s8)save->summary[selectedFile].sendFlag != 0) ||
1122 (menuMode == SAVE_MENU_EXTRA &&
1123 (s8)save->summary[selectedFile].flagCount == 0) ||
1124 (menuMode == SAVE_MENU_CLEARED &&
1125 (s8)save->summary[selectedFile].sendFlag == 0)));
1126 } else if (gKeysRepeat & KEY_DOWN) {
1128 summaryDirty = 1;
1129 do {
1130 selectedFile = (selectedFile + SAVE_FILE_COUNT + 1) % SAVE_FILE_COUNT;
1131 if (menuMode == SAVE_MENU_SAVE)
1132 break;
1133 } while ((save->summary[selectedFile].level == 0 ||
1134 (menuMode == SAVE_MENU_CONTINUE &&
1135 (s8)save->summary[selectedFile].sendFlag != 0) ||
1136 (menuMode == SAVE_MENU_EXTRA &&
1137 (s8)save->summary[selectedFile].flagCount == 0) ||
1138 (menuMode == SAVE_MENU_CLEARED &&
1139 (s8)save->summary[selectedFile].sendFlag == 0)));
1140 } else if (gKeysPressed & KEY_B) {
1142 result = -1;
1143 break;
1144 } else if (gKeysPressed & KEY_A) {
1146 result = selectedFile;
1147 break;
1148 }
1149 }
1150 // Close the menu and return to the current game's palette glow.
1153 UiWork_Finalize(djinnWindow, 2);
1154 UiWork_Finalize(partyWindow, 2);
1155 UiWork_Finalize(summaryWindow, 2);
1156 UiWork_Finalize(fileList, 2);
1157#if TBS_PATCH_1
1159#else
1160#endif
1163 WaitFrames(1);
1164done:
1165 return result;
1166}
1167#endif
1168
1169/// The Save command: lets the player choose a file (the current one first),
1170/// asks whether to save over it when it has data, then saves the game to it,
1171/// object table included, and makes it the current file. The question is "OK
1172/// to save over this file?" Returns 0 (also when the player answers No), -1
1173/// when cancelled, or a SAVE_ERROR_* after showing its message.
1175 s32 result;
1176 s32 file;
1177 s32 error;
1178 struct SaveWorkspace *save;
1179
1180 result = 0;
1182 if (error != 0) {
1183 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
1184 result = SAVE_ERROR_NO_MEMORY;
1185 } else {
1187 save = gSaveWorkspace;
1189 if (file == -1) {
1190 result = file;
1191 } else {
1192 if (save->summary[file].level != 0) {
1193 UiText_ShowPositionedMessageAndWait(MsgOverwriteConfirm, 13);
1194 while (UiWork_IsComplete() == 0) {
1195 WaitFrames(1);
1196 }
1197 if (Menu_RunConfirmSelection(1, 0, 0, 1) != 0) {
1199 goto done;
1200 }
1202 }
1203 gSaveSlot = file;
1206 while (UiWork_IsComplete() == 0) {
1207 WaitFrames(1);
1208 }
1211 error = SaveState_WriteRecord(file, gSaveBuffer);
1212 error |= SaveState_WriteRecord(file + SAVE_FILE_COUNT,
1215 if (error != 0) {
1216 UiText_ShowPositionedMessageAndWait(MsgSaveFailed, 1);
1217 result = SAVE_ERROR_WRITE;
1218 } else {
1219 UiText_ShowPositionedMessageAndWait(MsgGameSaved, 9);
1220 }
1221 }
1222 }
1223done:
1225 return result;
1226}
1227
1228/// Lets the player choose a file in save menu `mode` (see SaveMenuMode) and
1229/// loads it into the save buffer as the current file, restoring its play time
1230/// and options. Returns 0, -1 when cancelled, or a SAVE_ERROR_* after showing
1231/// its message.
1233 s32 result = 0;
1235
1236 if (error != 0) {
1237 UiText_ShowPositionedMessageAndWait(MsgNoBackupMemory, 1);
1238 result = SAVE_ERROR_NO_MEMORY;
1239 } else {
1240 s32 file;
1241
1243 file = SaveMenu_SelectSlot(gSaveSlot, mode);
1244 if (file == -1) {
1245 result = file;
1246 } else {
1247 void *half = &gSaveBuffer;
1248
1249 error = SaveState_ReadRecordPayload(file, half);
1250 half = (char *)half + SAVE_RECORD_BYTES;
1251 error |= SaveState_ReadRecordPayload(file + SAVE_FILE_COUNT, half);
1252 if (error != 0) {
1253 UiText_ShowPositionedMessageAndWait(MsgLoadFailed, 1);
1254 result = SAVE_ERROR_READ;
1255 } else {
1258 gIdleFrames = 0;
1259 gSaveSlot = file;
1260 }
1261 }
1262 }
1264 return result;
1265}
1266
1267/// Loads the pointing hand cursor's tiles (gResource_FixedBlockBTiles, 128
1268/// bytes) into a free VRAM slot and returns the slot.
1270 s32 slot;
1271
1274 return slot;
1275}
What every part of the menu object shares: its includes, edition values, macros and the link round st...
#define OUTPUT_SIZE_16X16
OAM attribute bits a sprite output is created with: the size field of attribute 1 (bits 30-31 of the ...
#define Dma_Fill32(value, destination, size)
Fills size bytes at destination with the word value by DMA3.
Definition dma.h:30
s32 AnimationObjects_SelectAnimationFar(struct AnimationObject *state, s32 flags)
struct AnimationObject * ResourceObject_CreateFar(s32 id)
void Object_ApplyProjectedPlacementFar(struct AnimationObject *sprite, const struct SpritePlacement *position, const struct SpriteScale *scale, s32 facing)
Far call to Render_ApplyProjectedPlacement.
void ResourceObject_ReleaseFar(struct AnimationObject *sprite)
struct BattleUnit * UnitGetFar(u32 owner)
s32 Party_ListActiveOwnersFar(s16 *owners)
s32 GameFlag_TestFar(s32 flag)
u32 Runtime_GetBuildStampTimeFar()
s32 Party_SumDjinnCountsFar(s32 index)
s32 Scene_GetAreaNameIndexFar(s32 scene, s32 entrance)
void SaveState_CaptureObjectTableFar(void)
Far call to ObjectTable_Snapshot.
s32 Party_RemapCharacterIdIfEnabledFar(s32 arg0, s32 arg1)
void Shop_SetCursorFar(struct ShopCursor *cursor, s32 targetX, s32 targetY, s32 kind)
void ShopCursor_AdvanceFar(struct ShopCursor *cursor)
void AudioCommand_PlayFar(s32 id)
u8 gSceneState[]
s32 Trig_Sin(s32 angle)
#define FLAG_CHILDHOOD
the prologue: Isaac and Garet are children
Definition flags.h:10
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
u32 gFrameTick
Counted up by every VBlank.
u32 gPlayTimeFrames
The play time clock, in frames.
volatile u16 gIdleFrames
Frames since a key was last held.
volatile u8 gIdleSleepEnabled
Sleep after three idle minutes.
void UiText_ShowPositionedMessageAndWait(s32 message, s32 flags)
Shows a message in a window centred on the screen, waits until it is typed out and closes it.
Definition frames.c:447
struct UiWindow * UiText_OpenMessageWindow(s32 message, s32 x, s32 y, u32 packed)
Opens a window sized to a message near tile (x, y) and starts typing the message into it.
Definition frames.c:396
s32 UiWork_IsComplete(void)
Returns 1 when every message channel has finished its text (or is idle), and 0 while one is still typ...
Definition frames.c:210
u8 gSaveStamp[]
u8 gGameFlagBytes[]
struct GameState gGameState
void * gMenuWork
u8 * UiText_FormatNumber(u8 *buffer, s32 input, s32 width)
Writes value as decimal text into buffer (14 bytes) and returns where the text starts.
Definition glyphs.c:470
u8 gResource_FixedBlockBTiles[]
void * Runtime_BumpAllocateAlternatePool(s32 size)
As MemAlloc, but tries EWRAM first.
Definition heap.c:140
void * Runtime_AllocateHeapBlock(s32 kind, s32 size)
Returns heap slot kind's block, first allocating size bytes for it (rounded up to words),...
Definition heap.c:47
@ HEAP_SLOT_MENU
Definition heap_state.h:55
@ HEAP_SLOT_SAVE
Definition heap_state.h:50
@ HEAP_SLOT_WINDOW
Definition heap_state.h:39
s32 Menu_RunConfirmSelection(s32 bottom, s32 message, s32 labelWidth, s32 selection)
Selects Yes/No (icons 5/6); canceling returns 1 (No).
Definition icon_menu.c:795
@ KEY_UP
Definition io_reg.h:249
@ KEY_B
Definition io_reg.h:244
@ KEY_LEFT
Definition io_reg.h:248
@ KEY_R
Definition io_reg.h:251
@ KEY_A
Definition io_reg.h:243
@ KEY_DOWN
Definition io_reg.h:250
#define BG_PLTT_COLOR(palette, color)
Definition io_reg.h:158
static s32 Iwram_CopyWords(volatile void *destination, const volatile void *source, s32 size)
Definition iwram_call.h:85
volatile u32 gKeysHeld
Keys down now.
volatile u32 gKeysPressed
Keys that went down this frame.
volatile u32 gKeysRepeat
Pressed keys, repeating while held: after 19 frames, then every 6.
void Runtime_ReleaseHeapBlock(s32 slot)
void MemFree(void *buffer)
u8 gStatusMenu_LevelLetterString[]
const s32 gMenu_PartySpriteResourceIds[4]
u8 gTitleExtraOptionEnabled[]
void PaletteGlow_Update(s32 phase, s32 brightness)
Sets the window colours (BG palette 15, colours 4 to 10) for a window hue (0 to 23) and brightness (0...
Definition options.c:152
struct RenderOutput * RenderOutput_Create(s32 slot, s32 oamFlags, struct UiWindow *window, s32 offsetX, s32 offsetY)
Creates a sprite output for the VRAM cache slot slot and attaches it to window, placed at (offsetX,...
Definition outputs.c:13
struct RenderOutput * RenderResource_CreatePair(s32 picture, struct UiWindow *window, s32 x, s32 y)
Shows picture picture of gRenderResource_PairSourceTable as two 32x32 sprites side by side at (x,...
Definition pictures.c:28
void RenderOutput_BobPair(struct RenderOutput *obj)
Bobs the two sprites of a pair (see RenderResource_CreatePair) up and down by gUi_PairBobOffsets,...
Definition pictures.c:50
u32 SaveState_ReadRecordPayload(s32 recordId, void *destination)
Loads the current copy of record recordId into destination.
Definition save.c:236
s32 SaveState_LoadSummaryRecords(void)
Reads the summary at the start of each of the three save files into summary for the file select scree...
Definition save.c:376
u32 SaveState_ReleaseWorkspace(void)
Removes the flash timer handler and frees the save workspace's heap slot.
Definition save.c:409
u32 SaveState_DeleteRecord(s32 recordId)
Invalidates the current copy of record recordId.
Definition save.c:253
s32 SaveState_WriteRecord(s32 recordId, void *source)
Saves 0xff0 bytes from source as record recordId: writes the new copy with the next sequence number,...
Definition save.c:181
s32 SaveState_InitializeWorkspace(void)
Allocates the save workspace (heap slot HEAP_SLOT_SAVE), installs the flash library's timer 2 handler...
Definition save.c:31
struct SaveWorkspace * gSaveWorkspace
s16 gTitleSendOptionEnabled
s32 Scheduler_AddOrUpdateCallback(SchedulerCallback callback, s32 order)
Schedules callback with order, or gives a task already running it the new order, and re-sorts the tab...
Definition scheduler.c:102
s32 Scheduler_RemoveCallback(SchedulerCallback callback)
Unschedules the task running callback: clears its routine and gives it order 0x7fff,...
Definition scheduler.c:146
#define SE_MENU_CANCEL
Definition songs.h:106
#define SE_MENU_CURSOR_MOVE
Definition songs.h:104
#define MUS_085
Definition songs.h:84
#define SE_MENU_CONFIRM
Definition songs.h:105
#define NULL
Definition bilibin.h:6
void * gWorkSlot[]
u8 gWindowWork[]
An animated sprite: its image and shadow, scale, rotation, size and up to four animation layers.
Definition animspr.h:108
struct AnimationSpritePart image
Definition animspr.h:109
u8 flags
bits: 1 outlined in ObjectSystemWork's edge and fill colours | 0 shadow drawn (set when created)
Definition animspr.h:128
u8 height
pixels
Definition animspr.h:120
s32 scale
16.16 fixed point, 0x10000 full size
Definition animspr.h:111
u16 priority
2 when created
Definition animspr.h:100
A unit in battle: a party member's or an enemy's stats, Psynergy, items, Djinn and ailments.
u8 name[0x0f]
The name, one byte a character, ending in 0.
u8 classIndex
The character's class, worked out from its Djinn (Unit_UpdateClass); 0 marks an enemy,...
u8 level
1 to 99 for a character
The menu work as the row of four figures reads it: each figure's object, screen position and motion p...
struct AnimationObject * objects[4]
s16 positionsX[4]
screen pixels
s16 positionsY[4]
screen pixels
u8 paletteGlow[2]
window hue (0 to 24), brightness
Definition game_state.h:62
u32 playTime
frames, copied from gPlayTimeFrames when saving
Definition game_state.h:18
u32 buildStamp
Runtime_GetBuildStampTime's value when the game was last saved; its low half goes into the file's sum...
Definition game_state.h:17
u8 unk20f
Set on saving at the end of the game; copied to the file's summary as sendFlag.
Definition game_state.h:70
u8 idleSleepEnabled
the option, saved from gIdleSleepEnabled
Definition game_state.h:91
s16 entrance
Definition game_state.h:37
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
A render output: a sprite in a window's output list (a character, an icon or a portrait),...
u16 y
screen pixels
s8 active
its UI_SPRITE_ motion; 0 while free
u16 x
screen pixels
A save file's summary for the file screen: the leader's name, level and class, the area,...
Definition save_state.h:29
u16 paletteGlow
bits: 15..8 paletteGlowSub | 7..0 paletteGlow
Definition save_state.h:46
u8 classId
the leader's
Definition save_state.h:33
u8 name[12]
the leader's
Definition save_state.h:31
u8 unk38[4]
Filled on reading: the play time from the file's second record.
Definition save_state.h:54
u8 hasFlag20
FLAG_CHILDHOOD is set; remaps the party's sprites.
Definition save_state.h:43
u16 frames
Low half of GameState.buildStamp; a file below the current stamp is listed as from an older build.
Definition save_state.h:51
u32 checksum
the sum of the 968 words from gGameFlagBytes
Definition save_state.h:55
s8 sendFlag
Copied from GameState.unk20f, which saving at the end of the game sets; the file menu for cleared dat...
Definition save_state.h:41
u8 djinn[4]
the party's Djinn of each element
Definition save_state.h:37
u32 playTime
frames
Definition save_state.h:35
u8 level
the leader's; 0 for an empty file
Definition save_state.h:32
s8 flagCount
how many of game flags 48 to 127 are set
Definition save_state.h:42
u16 paletteGlowSub
GameState.paletteGlow[1].
Definition save_state.h:47
u16 area
area name number (Scene_GetAreaNameIndex)
Definition save_state.h:34
s8 party[5]
up to four party members, ended by -1
Definition save_state.h:38
The save work: which of the 16 flash sectors hold a record, their record ids and sequence numbers,...
Definition save_state.h:61
struct SaveSummary summary[3]
each file's, read from its first record
Definition save_state.h:72
union SaveWorkspace::@331263107057156204162071305274032234046256201132 slot
one sector, read from or written to flash
Cursor that approaches its target, shared by shop, name entry and settings.
Definition cursor.h:13
u16 x
screen pixels: where the current move started
Definition cursor.h:15
u16 y
screen pixels: where the current move started
Definition cursor.h:16
struct RenderOutput * anchor
the cursor's sprite, moved with its OAM
Definition cursor.h:14
A shop's work: its windows, the party members' icons, the stock, the cursor and its icons,...
Definition shop.h:18
s32 partyMemberScale[8]
16.16 fixed point, 0x10000 full size
Definition shop.h:27
struct AnimationObject * partyMemberIcons[8]
the party row's sprites
Definition shop.h:24
s16 partyMemberY[8]
pixels
Definition shop.h:26
s16 partyMemberX[8]
pixels
Definition shop.h:25
A sprite's screen position and its two vertical offsets, in 16.16 pixels.
s32 x
screen x of the sprite's centre
s32 y
screen y at the ground; sets the depth slot
s32 groundHeight
how far the shadow is raised above y
s32 height
how far the image is raised above y
Horizontal and vertical sprite scale; 0x10000 is the original size.
The window system's work: the windows' tilemap, the windows, the three text channels,...
Definition window.h:19
u16 tilemap[640]
the windows' BG tilemap, 32 by 20 tiles
Definition window.h:20
u8 dirty
The tilemap's 4-row bands to upload: bit 0 for all, bit n + 1 for band n.
Definition window.h:32
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
u16 y
tiles
Definition uiwindow.h:16
u16 x
tiles
Definition uiwindow.h:15
s16 gSaveSlot
u8 gSaveBuffer[]
#define SAVE_SLOT_NAME_X
void UiText_DrawStringInWindow(const u8 *text, struct UiWindow *window, u32 x, u32 y)
Draws a byte string into a window's tilemap at pixel (x, y) of its interior, rounded down to tiles.
Definition text_tiles.c:646
void UiText_DrawResource(s32 message, struct UiWindow *window, s32 x, s32 y)
Decodes a message and draws it into a text canvas window at pixel (x, y).
Definition text_tiles.c:538
void UiText_DrawString(const u8 *text, struct UiWindow *window, s32 x, s32 y)
Draws a byte string into a text canvas window at pixel (x, y).
Definition text_tiles.c:599
void UiWindow_DrawDividerLine(struct UiWindow *win, u32 x1, u32 y1, u32 x2, u32 y2)
Draws a straight divider line inside a window from tile (x1, y1) to (x2, y2), horizontal or vertical:...
Definition text_tiles.c:365
void UiText_DrawStringAtOffset(const u8 *text, const struct UiWindow *work, s32 offsetX, s32 offsetY)
Draws a byte string as tiles in window work at pixel (offsetX, offsetY) of its interior.
Definition text_tiles.c:614
void UiText_DrawNumberInWindow(s32 value, s32 width, struct UiWindow *window, s32 x, s32 y)
Draws a number (width as UiText_FormatNumber) into a window's tilemap at pixel (x,...
Definition text_tiles.c:678
void UiText_DrawNumberAtOffset(s32 value, s32 width, const struct UiWindow *window, s32 x, s32 y)
Draws a number (width as UiText_FormatNumber) as tiles in a window at pixel (x, y) of its interior.
Definition text_tiles.c:668
void UiText_DrawCharacterAtOffset(s32 message, const struct UiWindow *window, u32 offsetX, u32 offsetY)
Decodes a message and draws it as tiles in a window at pixel (offsetX, offsetY) of its interior.
Definition text_tiles.c:552
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
#define UNUSED
Definition types.h:22
unsigned char u8
Definition types.h:11
s32 Resource_FindFreeEntry(void)
Returns the number of the first free cache entry, or VRAM_CACHE_ENTRY_COUNT when all are taken.
Definition vram_cache.c:211
s32 VramCache_Load(u32 no, u32 size, const void *src)
Gives cache entry no size bytes of sprite VRAM and copies src there.
Definition vram_cache.c:143
@ VRAM_CACHE_ENTRY_COUNT
Definition vram_tab.h:12
#define SAVE_ERROR_READ
a record could not be read
Definition save.c:15
s32 Save_WriteSelectedSlot(void)
The Save command: lets the player choose a file (the current one first), asks whether to save over it...
Definition save.c:1174
#define SAVE_MENU_NO_FILE
SaveMenu_SelectSlot's result when no file can be chosen in its mode.
Definition save.c:824
#define SAVE_RECORD_BYTES
Definition save.c:11
s32 SaveState_DeleteSelectedSlot(void)
The file menu's Erase: lets the player choose a file, asks whether to erase it and on Yes deletes bot...
Definition save.c:429
#define SUMMARY_LEVEL_X
Definition save.c:761
#define SUMMARY_DRAW_LEVEL
Definition save.c:763
void UiWindow_FillTilemapRect(struct UiWindow *window, s32 x, s32 y, s32 width, s32 height)
Fills the width x height tile rectangle at (x, y) inside window, clipped to the screen as UiWindow_Se...
Definition save.c:538
s32 SaveState_WriteSlotPair(s32 file)
Writes both halves of the save buffer, as they are, to save file file.
Definition save.c:281
void SaveMenu_ShowDjinnSprites(struct UiWindow *window, s32 x, s32 y)
Shows the four Djinn sprites, one for each element, in a row from tile (x, y) inside window,...
Definition save.c:669
#define SAVE_ERROR_NO_FILE
no empty file to copy into
Definition save.c:19
#define SAVE_FILE_COUNT
Definition save.c:10
u32 SaveState_BuildSummaryHeader(void)
Prepares the save buffer for writing: stamps the game state with the time and options,...
Definition save.c:159
s32 SaveMenu_SelectSlot(s32 selectedFile, s32 menuMode)
Definition save.c:834
#define SAVE_MENU_SPRITE_SPACING
The save file menu shows the chosen file's party as walking sprites in a row, and four Djinn sprites,...
Definition save.c:588
void SaveMenu_HidePartySprites(void)
Stops placing the party sprites of the save file menu and releases them.
Definition save.c:625
UNUSED s32 SaveState_ConfirmAndProcessSelectedSlot(void)
Asks whether to save over the current file and, on Yes, writes the save stamp into it (SaveState_Proc...
Definition save.c:347
s32 SaveState_CopySlotPair(void)
The file menu's Copy: lets the player choose a file, reads both of its records into the save buffer a...
Definition save.c:377
void SaveMenu_DrawFileSummary(struct UiWindow *window, struct SaveSummary *summary)
Draws the summary of save file summary into window: the leader's name, level and class,...
Definition save.c:769
void PaletteGlow_UpdateSine(void)
A scheduler callback that makes colours 8-11 of BG palette 14 glow: each follows a sine wave of the f...
Definition save.c:473
#define SAVE_MENU_SPRITE_PRIORITY
Scheduler priority of the callbacks that place the sprites every frame.
Definition save.c:590
void PaletteGlow_Start(void)
Starts the palette glow (PaletteGlow_UpdateSine) in the scheduler.
Definition save.c:527
u32 SaveState_FindFreeSummarySlot(void)
Returns the first save file whose summary shows it empty (level 0), or SAVE_NO_FREE_FILE when all thr...
Definition save.c:65
void SaveMenu_DrawDjinnCounts(struct UiWindow *window, struct SaveSummary *summary)
Draws the Djinn counts of save file summary, one for each element, two digits each,...
Definition save.c:740
void SaveMenu_HideDjinnSprites(void)
Stops placing the Djinn sprites of the save file menu and releases them.
Definition save.c:696
s32 Resource_LoadFixedBlockBIntoFreeSlot(void)
Loads the pointing hand cursor's tiles (gResource_FixedBlockBTiles, 128 bytes) into a free VRAM slot ...
Definition save.c:1269
void PaletteGlow_Stop(void)
Definition save.c:531
s16 SaveState_WriteCurrentSlotPair(void)
Saves the game to the current file (gSaveSlot): builds the summary and writes both halves of the save...
Definition save.c:245
#define SAVE_NO_FREE_FILE
Definition save.c:12
#define SUMMARY_COINS_X
Definition save.c:762
s32 SaveState_CountRecordsExcludingFlagged(s32 excludeSent)
Returns how many save files there are, leaving out those whose summary has its send flag set when exc...
Definition save.c:80
s32 SaveState_LoadRecordIntoWork(s32 mode)
Lets the player choose a file in save menu mode (see SaveMenuMode) and loads it into the save buffer ...
Definition save.c:1232
s32 SaveState_ProcessSelectedSlot(void)
Writes the 64-byte save stamp (gSaveStamp, which lies in the first half of the save buffer) into the ...
Definition save.c:308
s32 SaveState_ScanRecordFlags(void)
Loads the save file summaries for the title menu and returns how many files there are: plus 100 when ...
Definition save.c:111
SaveMenuMode
What the save file menu is choosing a file for, which decides the files the cursor may stop on.
Definition save.c:814
@ SAVE_MENU_CLEARED
cleared files only
Definition save.c:820
@ SAVE_MENU_SAVE
saving: any file, empty ones too
Definition save.c:815
@ SAVE_MENU_CONTINUE
continuing: files not yet cleared
Definition save.c:816
@ SAVE_MENU_COPY
copying: any file with data
Definition save.c:817
@ SAVE_MENU_EXTRA
files with any of game flags 48-127 set
Definition save.c:819
@ SAVE_MENU_ERASE
erasing: any file with data
Definition save.c:818
void SaveMenu_PlaceDjinnSprites(void)
A scheduler callback: draws each Djinn sprite of the save file menu at its place, at scale 1....
Definition save.c:711
#define SAVE_ERROR_ERASE
erasing failed. "Failed to erase."
Definition save.c:18
void * Text_FormatPlayTime(s32 frames, u8 *out)
Writes the play time frames (at 60 frames a second) into out as hours and minutes,...
Definition save.c:26
#define SAVE_ERROR_NO_MEMORY
No backup memory. "Unable to detect backup memory.".
Definition save.c:21
void SaveMenu_PlacePartySprites(void)
A scheduler callback: draws each party sprite of the save file menu at its place and scale for this f...
Definition save.c:640
#define SUMMARY_DISTANCE(from, to)
The distance in bytes from one field of a save summary to another.
Definition save.c:102
void SaveMenu_ShowPartySprites(struct UiWindow *window, s32 x, s32 y, struct SaveSummary *summary)
Shows the party of save file summary (up to four members) as sprites in a row from tile (x,...
Definition save.c:595
#define SAVE_ERROR_WRITE
Writing failed. "Failed to save." (or "Failed to erase.").
Definition save.c:17
void UiWork_FinalizePendingCore(void)
Closes each message channel's window whose text has ended, then waits ten frames.
Definition messages.c:302
void RenderOutput_PrepareForRedraw(struct UiWindow *window)
Clears a window's contents: redraws its empty frame and releases the render outputs it carries.
Definition windows.c:392
void UiWork_Finalize(struct UiWindow *window, s32 instant)
Closes a window: clears its contents and remembers its last rectangle.
Definition windows.c:349
void RenderOutput_RedrawSavedRect(struct UiWindow *window)
Redraws a window's empty frame at its current rectangle.
Definition windows.c:400
struct UiWindow * UiWindow_Create(s32 x, s32 y, s32 width, s32 height, s32 attrs)
Opens a window at tile (x, y) of width by height tiles in the first free window slot,...
Definition windows.c:279