all repos — mgba @ af03ad75be05379bbecbc326e849d26d25237652

mGBA Game Boy Advance Emulator

src/platform/switch/main.c (view raw)

  1/* Copyright (c) 2013-2018 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 "feature/gui/gui-runner.h"
  7#include <mgba/core/core.h>
  8#include <mgba/internal/gba/input.h>
  9#include <mgba-util/gui.h>
 10#include <mgba-util/gui/font.h>
 11
 12#include <switch.h>
 13#include <EGL/egl.h>
 14#include <GLES2/gl2.h>
 15
 16#define AUTO_INPUT 0x4E585031
 17
 18static EGLDisplay s_display;
 19static EGLContext s_context;
 20static EGLSurface s_surface;
 21
 22static const GLfloat _offsets[] = {
 23	0.f, 0.f,
 24	1.f, 0.f,
 25	1.f, 1.f,
 26	0.f, 1.f,
 27};
 28
 29static const GLchar* const _gles2Header =
 30	"#version 100\n"
 31	"precision mediump float;\n";
 32
 33static const char* const _vertexShader =
 34	"attribute vec2 offset;\n"
 35	"uniform vec2 dims;\n"
 36	"uniform vec2 insize;\n"
 37	"varying vec2 texCoord;\n"
 38
 39	"void main() {\n"
 40	"	vec2 ratio = insize / 256.0;\n"
 41	"   vec2 scaledOffset = offset * dims;\n"
 42	"	gl_Position = vec4(scaledOffset.x * 2.0 - dims.x, scaledOffset.y * -2.0 + dims.y, 0.0, 1.0);\n"
 43	"	texCoord = offset * ratio;\n"
 44	"}";
 45
 46static const char* const _fragmentShader =
 47	"varying vec2 texCoord;\n"
 48	"uniform sampler2D tex;\n"
 49	"uniform vec4 color;\n"
 50
 51	"void main() {\n"
 52	"	vec4 texColor = vec4(texture2D(tex, texCoord).rgb, 1.0);\n"
 53	"	texColor *= color;\n"
 54	"	gl_FragColor = texColor;\n"
 55	"}";
 56
 57static GLuint program;
 58static GLuint vbo;
 59static GLuint offsetLocation;
 60static GLuint texLocation;
 61static GLuint dimsLocation;
 62static GLuint insizeLocation;
 63static GLuint colorLocation;
 64static GLuint tex;
 65
 66static color_t frameBuffer[256 * 256];
 67
 68static bool initEgl() {
 69    s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
 70    if (!s_display) {
 71        goto _fail0;
 72    }
 73
 74    eglInitialize(s_display, NULL, NULL);
 75
 76    EGLConfig config;
 77    EGLint numConfigs;
 78    static const EGLint attributeList[] = {
 79        EGL_RED_SIZE, 1,
 80        EGL_GREEN_SIZE, 1,
 81        EGL_BLUE_SIZE, 1,
 82        EGL_NONE
 83    };
 84    eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs);
 85    if (!numConfigs) {
 86        goto _fail1;
 87    }
 88
 89    s_surface = eglCreateWindowSurface(s_display, config, "", NULL);
 90    if (!s_surface) {
 91        goto _fail1;
 92    }
 93
 94    s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL);
 95    if (!s_context) {
 96        goto _fail2;
 97    }
 98
 99    eglMakeCurrent(s_display, s_surface, s_surface, s_context);
100    return true;
101
102_fail2:
103    eglDestroySurface(s_display, s_surface);
104    s_surface = NULL;
105_fail1:
106    eglTerminate(s_display);
107    s_display = NULL;
108_fail0:
109    return false;
110}
111
112static void deinitEgl() {
113    if (s_display) {
114        if (s_context) {
115            eglDestroyContext(s_display, s_context);
116        }
117        if (s_surface) {
118            eglDestroySurface(s_display, s_surface);
119        }
120        eglTerminate(s_display);
121    }
122}
123
124static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {
125	mInputBindKey(map, binding, __builtin_ctz(nativeKey), key);
126}
127
128static void _drawStart(void) {
129	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
130}
131
132static void _drawEnd(void) {
133	eglSwapBuffers(s_display, s_surface);
134}
135
136static uint32_t _pollInput(const struct mInputMap* map) {
137	int keys = 0;
138	hidScanInput();
139	u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO);
140	keys |= mInputMapKeyBits(map, AUTO_INPUT, padkeys, 0);
141	return keys;
142}
143
144static void _setup(struct mGUIRunner* runner) {
145	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_A, GBA_KEY_A);
146	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_B, GBA_KEY_B);
147	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_PLUS, GBA_KEY_START);
148	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_MINUS, GBA_KEY_SELECT);
149	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DUP, GBA_KEY_UP);
150	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DDOWN, GBA_KEY_DOWN);
151	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DLEFT, GBA_KEY_LEFT);
152	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_DRIGHT, GBA_KEY_RIGHT);
153	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_L, GBA_KEY_L);
154	_mapKey(&runner->core->inputMap, AUTO_INPUT, KEY_R, GBA_KEY_R);
155
156	runner->core->setVideoBuffer(runner->core, frameBuffer, 256);
157}
158
159static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded) {
160	glBindBuffer(GL_ARRAY_BUFFER, vbo);
161
162	glEnable(GL_BLEND);
163	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
164
165	glUseProgram(program);
166	float aspectX = width / (float) runner->params.width;
167	float aspectY = height / (float) runner->params.height;
168	float max;
169	if (aspectX > aspectY) {
170		max = floor(1.0 / aspectX);
171	} else {
172		max = floor(1.0 / aspectY);
173	}
174
175	aspectX *= max;
176	aspectY *= max;
177
178	glUniform1i(texLocation, 0);
179	glUniform2f(dimsLocation, aspectX, aspectY);
180	glUniform2f(insizeLocation, width, height);
181	if (!faded) {
182		glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
183	} else {
184		glUniform4f(colorLocation, 0.8f, 0.8f, 0.8f, 0.8f);		
185	}
186
187	glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
188	glEnableVertexAttribArray(offsetLocation);
189
190	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
191
192	glDisableVertexAttribArray(offsetLocation);
193	glBindBuffer(GL_ARRAY_BUFFER, 0);
194	glUseProgram(0);
195}
196
197static void _drawFrame(struct mGUIRunner* runner, bool faded) {
198	glActiveTexture(GL_TEXTURE0);
199	glBindTexture(GL_TEXTURE_2D, tex);
200	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frameBuffer);
201
202	unsigned width, height;
203	runner->core->desiredVideoDimensions(runner->core, &width, &height);
204	_drawTex(runner, width, height, faded);
205}
206
207static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
208	glActiveTexture(GL_TEXTURE0);
209	glBindTexture(GL_TEXTURE_2D, tex);
210	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
211
212	_drawTex(runner, width, height, faded);
213}
214
215static uint16_t _pollGameInput(struct mGUIRunner* runner) {
216	int keys = 0;
217	hidScanInput();
218	u32 padkeys = hidKeysHeld(CONTROLLER_P1_AUTO);
219	keys |= mInputMapKeyBits(&runner->core->inputMap, AUTO_INPUT, padkeys, 0);
220	return keys;
221}
222
223static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
224	UNUSED(runner);
225}	
226
227static bool _running(struct mGUIRunner* runner) {
228	UNUSED(runner);
229	return appletMainLoop();
230}
231
232int main(int argc, char* argv[]) {
233	socketInitializeDefault();
234	nxlinkStdio();
235	initEgl();
236	romfsInit();
237
238	struct GUIFont* font = GUIFontCreate();
239
240	u32 width = 1280;
241	u32 height = 720;
242
243	glViewport(0, 0, width, height);
244	glClearColor(0.f, 0.f, 0.f, 1.f);
245
246	glGenTextures(1, &tex);
247	glActiveTexture(GL_TEXTURE0);
248	glBindTexture(GL_TEXTURE_2D, tex);
249	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
250	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
251	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
252	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
253
254	program = glCreateProgram();
255	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
256	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
257	const GLchar* shaderBuffer[2];
258
259	shaderBuffer[0] = _gles2Header;
260
261	shaderBuffer[1] = _vertexShader;
262	glShaderSource(vertexShader, 2, shaderBuffer, NULL);
263
264	shaderBuffer[1] = _fragmentShader;
265	glShaderSource(fragmentShader, 2, shaderBuffer, NULL);
266
267	glAttachShader(program, vertexShader);
268	glAttachShader(program, fragmentShader);
269
270	glCompileShader(fragmentShader);
271
272	GLint success;
273	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
274	if (!success) {
275		GLchar msg[512];
276		glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg);
277		puts(msg);
278	}
279
280	glCompileShader(vertexShader);
281
282	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
283	if (!success) {
284		GLchar msg[512];
285		glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg);
286		puts(msg);
287	}
288	glLinkProgram(program);
289
290	glDeleteShader(vertexShader);
291	glDeleteShader(fragmentShader);
292
293	texLocation = glGetUniformLocation(program, "tex");
294	colorLocation = glGetUniformLocation(program, "color");
295	dimsLocation = glGetUniformLocation(program, "dims");
296	insizeLocation = glGetUniformLocation(program, "insize");
297	offsetLocation = glGetAttribLocation(program, "offset");
298
299	glGenBuffers(1, &vbo);
300	glBindBuffer(GL_ARRAY_BUFFER, vbo);
301	glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
302	glBindBuffer(GL_ARRAY_BUFFER, 0);
303
304	struct mGUIRunner runner = {
305		.params = {
306			width, height,
307			font, "/",
308			_drawStart, _drawEnd,
309			_pollInput, NULL,
310			NULL,
311			NULL, NULL,
312		},
313		.nConfigExtra = 0,
314		.setup = _setup,
315		.teardown = NULL,
316		.gameLoaded = NULL,
317		.gameUnloaded = NULL,
318		.prepareForFrame = NULL,
319		.drawFrame = _drawFrame,
320		.drawScreenshot = _drawScreenshot,
321		.paused = NULL,
322		.unpaused = NULL,
323		.incrementScreenMode = NULL,
324		.setFrameLimiter = _setFrameLimiter,
325		.pollGameInput = _pollGameInput,
326		.running = _running
327	};
328	mGUIInit(&runner, "switch");
329
330	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_A, GUI_INPUT_SELECT);
331	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_B, GUI_INPUT_BACK);
332	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_X, GUI_INPUT_CANCEL);
333	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DUP, GUI_INPUT_UP);
334	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DDOWN, GUI_INPUT_DOWN);
335	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DLEFT, GUI_INPUT_LEFT);
336	_mapKey(&runner.params.keyMap, AUTO_INPUT, KEY_DRIGHT, GUI_INPUT_RIGHT);
337
338	mGUIRunloop(&runner);
339
340	deinitEgl();
341	socketExit();
342	return 0;
343}