src/gba/gui/gui-runner.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "gui-runner.h"
7
8#include "gba/interface.h"
9#include "gba/serialize.h"
10#include "util/gui/file-select.h"
11#include "util/gui/font.h"
12#include "util/gui/menu.h"
13#include "util/vfs.h"
14
15enum {
16 RUNNER_CONTINUE = 1,
17 RUNNER_EXIT = 2,
18 RUNNER_SAVE_STATE = 3,
19 RUNNER_LOAD_STATE = 4,
20 RUNNER_COMMAND_MASK = 0xFFFF,
21
22 RUNNER_STATE_1 = 0x10000,
23 RUNNER_STATE_2 = 0x20000,
24 RUNNER_STATE_3 = 0x30000,
25 RUNNER_STATE_4 = 0x40000,
26 RUNNER_STATE_5 = 0x50000,
27 RUNNER_STATE_6 = 0x60000,
28 RUNNER_STATE_7 = 0x70000,
29 RUNNER_STATE_8 = 0x80000,
30 RUNNER_STATE_9 = 0x90000,
31};
32
33static void _drawBackground(struct GUIBackground* background) {
34 struct GBAGUIBackground* gbaBackground = (struct GBAGUIBackground*) background;
35 if (gbaBackground->p->drawFrame) {
36 gbaBackground->p->drawFrame(gbaBackground->p, true);
37 }
38}
39
40static void _updateLux(struct GBALuminanceSource* lux) {
41 UNUSED(lux);
42}
43
44static uint8_t _readLux(struct GBALuminanceSource* lux) {
45 struct GBAGUIRunnerLux* runnerLux = (struct GBAGUIRunnerLux*) lux;
46 int value = 0x16;
47 if (runnerLux->luxLevel > 0) {
48 value += GBA_LUX_LEVELS[runnerLux->luxLevel - 1];
49 }
50 return 0xFF - value;
51}
52
53void GBAGUIInit(struct GBAGUIRunner* runner, const char* port) {
54 GUIInit(&runner->params);
55 GBAContextInit(&runner->context, port);
56 runner->luminanceSource.d.readLuminance = _readLux;
57 runner->luminanceSource.d.sample = _updateLux;
58 runner->luminanceSource.luxLevel = 0;
59 runner->context.gba->luminanceSource = &runner->luminanceSource.d;
60 runner->background.d.draw = _drawBackground;
61 runner->background.p = runner;
62 if (runner->setup) {
63 runner->setup(runner);
64 }
65}
66
67void GBAGUIDeinit(struct GBAGUIRunner* runner) {
68 if (runner->teardown) {
69 runner->teardown(runner);
70 }
71 GBAContextDeinit(&runner->context);
72}
73
74void GBAGUIRunloop(struct GBAGUIRunner* runner) {
75 struct GUIMenu pauseMenu = {
76 .title = "Game Paused",
77 .index = 0,
78 .background = &runner->background.d
79 };
80 struct GUIMenu stateSaveMenu = {
81 .title = "Save state",
82 .index = 0,
83 .background = &runner->background.d
84 };
85 struct GUIMenu stateLoadMenu = {
86 .title = "Load state",
87 .index = 0,
88 .background = &runner->background.d
89 };
90 GUIMenuItemListInit(&pauseMenu.items, 0);
91 GUIMenuItemListInit(&stateSaveMenu.items, 9);
92 GUIMenuItemListInit(&stateLoadMenu.items, 9);
93 *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Unpause", .data = (void*) RUNNER_CONTINUE };
94#if !(defined(__POWERPC__) || defined(__PPC__))
95 // PPC doesn't have working savestates yet
96 *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu };
97 *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu };
98
99 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_1) };
100 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_2) };
101 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_3) };
102 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_4) };
103 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_5) };
104 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_6) };
105 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_7) };
106 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_8) };
107 *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_9) };
108
109 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_1) };
110 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_2) };
111 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_3) };
112 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_4) };
113 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_5) };
114 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_6) };
115 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_7) };
116 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_8) };
117 *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_9) };
118#endif
119 *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Exit game", .data = (void*) RUNNER_EXIT };
120
121 while (true) {
122 char path[256];
123 if (!GUISelectFile(&runner->params, path, sizeof(path), GBAIsROM)) {
124 if (runner->params.guiFinish) {
125 runner->params.guiFinish();
126 }
127 GUIMenuItemListDeinit(&pauseMenu.items);
128 return;
129 }
130
131 if (runner->params.guiPrepare) {
132 runner->params.guiPrepare();
133 }
134 // TODO: Message box API
135 runner->params.drawStart();
136 GUIFontPrint(runner->params.font, runner->params.width / 2, (GUIFontHeight(runner->params.font) + runner->params.height) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
137 runner->params.drawEnd();
138 runner->params.drawStart();
139 GUIFontPrint(runner->params.font, runner->params.width / 2, (GUIFontHeight(runner->params.font) + runner->params.height) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Loading...");
140 runner->params.drawEnd();
141
142 if (!GBAContextLoadROM(&runner->context, path, true)) {
143 int i;
144 for (i = 0; i < 300; ++i) {
145 runner->params.drawStart();
146 GUIFontPrint(runner->params.font, runner->params.width / 2, (GUIFontHeight(runner->params.font) + runner->params.height) / 2, GUI_TEXT_CENTER, 0xFFFFFFFF, "Load failed!");
147 runner->params.drawEnd();
148 }
149 }
150 if (runner->params.guiFinish) {
151 runner->params.guiFinish();
152 }
153 GBAContextStart(&runner->context);
154 if (runner->gameLoaded) {
155 runner->gameLoaded(runner);
156 }
157 bool running = true;
158 while (running) {
159 while (true) {
160 uint32_t guiKeys;
161 GUIPollInput(&runner->params, &guiKeys, 0);
162 if (guiKeys & (1 << GUI_INPUT_CANCEL)) {
163 break;
164 }
165 if (guiKeys & (1 << GBA_GUI_INPUT_INCREASE_BRIGHTNESS)) {
166 if (runner->luminanceSource.luxLevel < 10) {
167 ++runner->luminanceSource.luxLevel;
168 }
169 }
170 if (guiKeys & (1 << GBA_GUI_INPUT_DECREASE_BRIGHTNESS)) {
171 if (runner->luminanceSource.luxLevel > 0) {
172 --runner->luminanceSource.luxLevel;
173 }
174 }
175 uint16_t keys = runner->pollGameInput(runner);
176 if (runner->prepareForFrame) {
177 runner->prepareForFrame(runner);
178 }
179 GBAContextFrame(&runner->context, keys);
180 if (runner->drawFrame) {
181 runner->params.drawStart();
182 runner->drawFrame(runner, false);
183 runner->params.drawEnd();
184 }
185 }
186
187 GUIInvalidateKeys(&runner->params);
188 uint32_t keys = 0xFFFFFFFF; // Huge hack to avoid an extra variable!
189 struct GUIMenuItem item;
190 enum GUIMenuExitReason reason = GUIShowMenu(&runner->params, &pauseMenu, &item);
191 if (reason == GUI_MENU_EXIT_ACCEPT) {
192 struct VFile* vf;
193 switch (((int) item.data) & RUNNER_COMMAND_MASK) {
194 case RUNNER_EXIT:
195 running = false;
196 keys = 0;
197 break;
198 case RUNNER_SAVE_STATE:
199 vf = GBAGetState(runner->context.gba, 0, ((int) item.data) >> 16, true);
200 if (vf) {
201 GBASaveStateNamed(runner->context.gba, vf, true);
202 vf->close(vf);
203 }
204 break;
205 case RUNNER_LOAD_STATE:
206 vf = GBAGetState(runner->context.gba, 0, ((int) item.data) >> 16, false);
207 if (vf) {
208 GBALoadStateNamed(runner->context.gba, vf);
209 vf->close(vf);
210 }
211 break;
212 case RUNNER_CONTINUE:
213 break;
214 }
215 }
216 while (keys) {
217 GUIPollInput(&runner->params, 0, &keys);
218 }
219 if (runner->params.guiFinish) {
220 runner->params.guiFinish();
221 }
222 }
223 GBAContextStop(&runner->context);
224 if (runner->gameUnloaded) {
225 runner->gameUnloaded(runner);
226 }
227 GBAContextUnloadROM(&runner->context);
228 }
229 GUIMenuItemListDeinit(&pauseMenu.items);
230 GUIMenuItemListDeinit(&stateSaveMenu.items);
231 GUIMenuItemListDeinit(&stateLoadMenu.items);
232}