1: /*
2: * Guitar-ZyX(tm)::MasterControlProgram - portable guitar F/X controller
3: * Copyright (C) 2009 Douglas McClendon
4: *
5: * This program is free software: you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation, version 3 of the License.
8: *
9: * This program is distributed in the hope that it will be useful,
10: * but WITHOUT ANY WARRANTY; without even the implied warranty of
11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: * GNU General Public License for more details.
13: *
14: * You should have received a copy of the GNU General Public License
15: * along with this program. If not, see <http://www.gnu.org/licenses/>.
16: */
17: /*
18: #############################################################################
19: #############################################################################
20: ##
21: ## gzmcp::libmcp: the mcp's meta-ds library (how I wish libnds would look)
22: ##
23: #############################################################################
24: ##
25: ## Copyright 2008-2009 Douglas McClendon <dmc AT filteredperception DOT org>
26: ##
27: #############################################################################
28: #############################################################################
29: #
30: */
31:
32:
33:
34:
35: #include <nds.h>
36:
37: #include <nds/arm9/console.h>
38:
39: #include <string.h>
40:
41: #include <stdlib.h>
42:
43: #include <stdio.h>
44:
45: #include <sys/iosupport.h>
46:
47: #include "debug.h"
48:
49: #include "mcp.h"
50:
51:
52:
53:
54: extern bool bgIsTextLut[8];
55: extern bool checkIfText(int id);
56: extern ssize_t con_write(struct _reent *,
57: int,
58: const char *,
59: size_t);
60: extern PrintConsole *currentConsole;
61: extern PrintConsole defaultConsole;
62: extern void consoleLoadFont(PrintConsole *);
63: extern void consolePrintChar(char);
64:
65:
66:
67:
68:
69: int mcp_blend_level_main = MCP_MAX_BLEND_LEVEL;
70: int mcp_blend_level_sub = MCP_MAX_BLEND_LEVEL;
71:
72: static const devoptab_t dotab_stdout = {
73: "con",
74: 0,
75: NULL,
76: NULL,
77: con_write,
78: NULL,
79: NULL,
80: NULL,
81: };
82:
83: u16 fadestate[8];
84: void (*fadestate_fire_func[8])(void);
85:
86:
87:
88:
89:
90:
91:
92: void mcp_basename(char *dest,
93: char *path) {
94:
95: char *new_result;
96: char *last_result;
97:
98:
99: // look for the last '/'
100: last_result = path;
101: while ((new_result = strstr(last_result, "/")) != NULL) {
102: last_result = new_result + 1;
103: }
104:
105: // copy to dest, everything after the last '/'
106: strncpy(dest, last_result, PATH_MAXLEN);
107:
108: // guarantee null terminated destination string
109: dest[PATH_MAXLEN] = 0;
110:
111: return;
112: }
113:
114:
115:
116: void mcp_dirname(char *dest,
117: char *path) {
118:
119: char *new_result;
120: char *last_result;
121: int dirname_length;
122:
123: char *trimmed_path;
124:
125:
126: // first check for dirname('/') = '/'
127: if (strncmp(path, "/", 2) == 0) {
128: strncpy(dest, path, 2);
129: return;
130: }
131:
132: if ((trimmed_path = malloc(PATH_MAXLEN)) == NULL) die();
133:
134: strncpy(trimmed_path, path, PATH_MAXLEN);
135: if (trimmed_path[strlen(trimmed_path) - 1] == '/') {
136:
137: trimmed_path[strlen(trimmed_path) - 1] = '\0';
138: }
139:
140: // look for the last '/'
141: last_result = trimmed_path;
142: while ((new_result = strstr(last_result, "/")) != NULL) {
143: last_result = new_result + 1;
144: }
145:
146: // if no / found, dirname is .
147: if (last_result == trimmed_path) {
148: // edunote: normally I go with n of strlen(string), but this from the
149: // strncpy manpage worries me-
150: // "If there is no terminating null byte in the first n characters of
151: // src, strncpy() produces an unterminated string in dest."
152: strncpy(dest, ".", 2);
153: free(trimmed_path);
154: return;
155: }
156:
157: // calculdate the length of the result we want
158: dirname_length = strlen(trimmed_path) - strlen(last_result) - 1;
159:
160: if (dirname_length > PATH_MAXLEN) {
161: // die("dirname() result greater than PATH_MAXLEN - %d\n",
162: // PATH_MAXLEN);
163: die();
164: }
165:
166: // special case of dirname("/XYZ") == "/" instead of ""
167: if (!dirname_length) {
168: strncpy(dest, "/", 2);
169: } else {
170:
171: // copy to dest, everything up to last '/'
172: strncpy(dest,
173: trimmed_path,
174: dirname_length);
175:
176: // guarantee null terminated destination string
177: dest[dirname_length] = 0;
178: dest[PATH_MAXLEN] = 0;
179:
180: }
181:
182: free(trimmed_path);
183: return;
184:
185: }
186:
187:
188:
189: void mcp_set_blend(int main,
190: int level) {
191: if (main) {
192: mcp_blend_level_main = level;
193: REG_BLDY = mcp_blend_level_main;
194: } else {
195: mcp_blend_level_sub = level;
196: REG_BLDY_SUB = mcp_blend_level_sub;
197: }
198: }
199: int mcp_get_blend(int main) {
200: if (main) {
201: return mcp_blend_level_main;
202: } else {
203: return mcp_blend_level_sub;
204: }
205: }
206:
207:
208:
209: int mcp_bg_init(int main,
210: int layer,
211: int hidden,
212: BgType bg_type,
213: BgSize bg_size,
214: int map_base,
215: int tile_base) {
216:
217: int layer_id;
218:
219: vu16 *bg_control_reg;
220:
221:
222: // convert layer and main/sub to compact layer_id encoding
223: layer_id = MCP_LAYER_ID(main, layer);
224:
225: // get a pointer to the appropriate control register
226: bg_control_reg = (main ? &(BGCTRL[layer]) : &(BGCTRL_SUB[layer]));
227:
228: // will this compile?? (not)
229: *bg_control_reg = (BG_MAP_BASE(map_base) |
230: BG_TILE_BASE(tile_base) |
231: bg_size |
232: ((bg_type == BgType_Text8bpp) ? BG_COLOR_256 : 0));
233:
234: // initialize global state structure array
235: memset(&bgState[layer_id],
236: 0,
237: sizeof(BgState));
238:
239: // initialize rotate and scale if a bitmap
240: if ((bg_type != BgType_Text4bpp) &&
241: (bg_type != BgType_Text8bpp)) {
242: bgSetScale(layer_id,
243: intToFixed(1, 8),
244: intToFixed(1, 8));
245: bgRotate(layer_id, 0);
246: }
247:
248: // update global state structure array
249: bgState[layer_id].type = bg_type;
250: bgState[layer_id].size = bg_size;
251:
252: // *DON'T* show(unhide) layer automatically
253: // if (main) {
254: // videoBgEnable(layer);
255: // } else {
256: // videoBgEnableSub(layer);
257: // }
258:
259: // update more global state
260: bgIsTextLut[layer_id] = checkIfText(layer_id);
261:
262: // set layer state to dirty
263: bgState[layer_id].dirty = true;
264:
265: // things are set up, let bgUpdate process the new state
266: bgUpdate();
267:
268: // be seeing you...
269: return layer_id;
270:
271: }
272:
273:
274:
275: void mcp_bg_show(int main,
276: int layer) {
277: bgShow(MCP_LAYER_ID(main, layer));
278: }
279:
280: void mcp_bg_hide(int main,
281: int layer) {
282: bgHide(MCP_LAYER_ID(main, layer));
283: }
284:
285:
286:
287: void mcp_console_init(PrintConsole *pconsole,
288: int main,
289: int layer,
290: int hidden,
291: int loadfont,
292: BgType bg_type,
293: BgSize bg_size,
294: int map_base,
295: int tile_base) {
296:
297: static int do_first_init = 1;
298: int i;
299:
300:
301: if (do_first_init) {
302: devoptab_list[STD_OUT] = &dotab_stdout;
303: setvbuf(stdout,
304: NULL,
305: _IONBF,
306: 0);
307: do_first_init = 0;
308: }
309:
310: currentConsole = pconsole;
311:
312: // initialize the console struct
313: // note: that the contents of console prior thus do not matter
314: *currentConsole = defaultConsole;
315:
316: // initialize the background layer
317: pconsole->bgId = MCP_LAYER_ID(main, layer);
318: mcp_bg_init(main,
319: layer,
320: 1,
321: bg_type,
322: bg_size,
323: map_base,
324: tile_base);
325:
326: // set console gfx pointers
327: pconsole->fontBgMap = (u16*)bgGetMapPtr(pconsole->bgId);
328: pconsole->fontBgGfx = (u16*)bgGetGfxPtr(pconsole->bgId);
329:
330: pconsole->consoleInitialised = 1;
331:
332: // clear the console
333: pconsole->cursorX = 0;
334: pconsole->cursorY = 0;
335: for (i = 0; i < pconsole->windowHeight * pconsole->windowWidth; i++) {
336: consolePrintChar(' ');
337: }
338:
339: // load the font if requested
340: if (loadfont) consoleLoadFont(pconsole);
341:
342: // show the background layer if requested (that it not be hidden)
343: if (!hidden) mcp_bg_show(main, layer);
344:
345: }
346:
347:
348:
349: void mcp_delay(int frames) {
350: int i;
351: for (i = 0 ; i < frames ; i++) swiWaitForVBlank();
352: }
353:
354:
355: void mcp_fade_set_enabled(int screen,
356: int layer,
357: int fading_enable) {
358: // todo? a ^= more readable logic equivalent?
359: fadestate[MCP_LAYER_ID(screen, layer)] &= ~(1 << 13);
360: fadestate[MCP_LAYER_ID(screen, layer)] |= (fading_enable << 13);
361: }
362:
363:
364: int mcp_fade_get_enabled(int screen,
365: int layer) {
366: return ((fadestate[MCP_LAYER_ID(screen, layer)] & 0x2000) >> 13);
367: }
368:
369:
370: void mcp_fade_set_level(int screen,
371: int layer,
372: int level) {
373: fadestate[MCP_LAYER_ID(screen, layer)] &= ~((u16)63 << 0);
374: fadestate[MCP_LAYER_ID(screen, layer)] |= (level << 0);
375: }
376:
377:
378: int mcp_fade_get_level(int screen,
379: int layer) {
380: return ((fadestate[MCP_LAYER_ID(screen, layer)] & 0x003F) >> 0);
381: }
382:
383:
384: void mcp_fade_set_rate(int screen,
385: int layer,
386: int rate) {
387: fadestate[MCP_LAYER_ID(screen, layer)] &= ~((u16)63 << 6);
388: fadestate[MCP_LAYER_ID(screen, layer)] |= (rate << 6);
389: }
390:
391:
392: int mcp_fade_get_rate(int screen,
393: int layer) {
394: return ((fadestate[MCP_LAYER_ID(screen, layer)] & 0x0FC0) >> 6);
395: }
396:
397:
398: void mcp_fade_set_fwd(int screen,
399: int layer,
400: int fwd) {
401: fadestate[MCP_LAYER_ID(screen, layer)] &= ~(1 << 12);
402: fadestate[MCP_LAYER_ID(screen, layer)] |= (fwd << 12);
403: }
404:
405:
406: int mcp_fade_get_fwd(int screen,
407: int layer) {
408: return ((fadestate[MCP_LAYER_ID(screen, layer)] & 0x1000) >> 12);
409: }
410:
411:
412: void mcp_fade_set_func(int screen,
413: int layer,
414: void (*func)(void)) {
415: fadestate_fire_func[MCP_LAYER_ID(screen, layer)] = func;
416: }
417:
418:
419: void mcp_fade_fire_func(int screen,
420: int layer) {
421:
422: void (*tfunc)(void);
423:
424:
425: // get a pointer to the function to fire off
426: tfunc = fadestate_fire_func[MCP_LAYER_ID(screen, layer)];
427: // reinitialize func to NULL (so perhaps tfunc can set it anew)
428: mcp_fade_set_func(screen, layer, NULL);
429: // fire off the function
430: if (tfunc != NULL) (*tfunc)();
431:
432: }
433:
434:
435: void mcp_fade_init(void) {
436:
437: int screen;
438: int layer;
439:
440:
441: for (screen = MCP_SUB_SCREEN ; screen <= MCP_MAIN_SCREEN ; screen++) {
442: for (layer = 0; layer < 4 ; layer++) {
443: mcp_fade_set_enabled(screen, layer, 0);
444: mcp_fade_set_fwd(screen, layer, 0);
445: mcp_fade_set_rate(screen, layer, 0);
446: mcp_fade_set_level(screen, layer, 0);
447: mcp_fade_set_func(screen, layer, NULL);
448: }
449: }
450:
451: }
452:
453:
454: void mcp_fade_start(int screen,
455: int layer,
456: int fwd,
457: int duration_ms,
458: int update_period_hz,
459: void (*func)(void)) {
460:
461: mcp_fade_set_enabled(screen, layer, 1);
462: mcp_fade_set_fwd(screen, layer, fwd);
463: mcp_fade_set_func(screen, layer, func);
464: mcp_fade_set_rate(screen, layer,
465: (MCP_FADE_MAXLEVEL /
466: (duration_ms /
467: (1000 / (update_period_hz / MCP_FADE_THROTTLE)))));
468: }
469:
470:
471: void mcp_fade_update(void) {
472:
473: int screen;
474: int layer;
475:
476: static int fade_throttle = 0;
477:
478:
479: fade_throttle++;
480: if (fade_throttle % MCP_FADE_THROTTLE) {
481: return;
482: }
483:
484: for (screen = MCP_SUB_SCREEN ; screen <= MCP_MAIN_SCREEN ; screen++) {
485: for (layer = 0; layer < 4 ; layer++) {
486: if (mcp_fade_get_enabled(screen, layer)) {
487: if (mcp_fade_get_fwd(screen, layer)) {
488: // fading in
489: if (mcp_fade_get_level(screen, layer) == MCP_FADE_MAXLEVEL) {
490: // disable first, in case fired func wants to start a new fade
491: mcp_fade_set_enabled(screen, layer, 0);
492: mcp_fade_fire_func(screen, layer);
493: } else if ((mcp_fade_get_level(screen, layer) + 1 +
494: mcp_fade_get_rate(screen, layer)) >=
495: MCP_FADE_MAXLEVEL) {
496: // finished fading in
497: mcp_fade_set_level(screen, layer,
498: MCP_FADE_MAXLEVEL);
499: } else {
500: // fade in progress
501: // note: +1 ensures that a level of 0 is still progressing
502: mcp_fade_set_level(screen, layer,
503: (mcp_fade_get_level(screen, layer) + 1 +
504: mcp_fade_get_rate(screen, layer)));
505: }
506: } else {
507: // fading out
508: if (mcp_fade_get_level(screen, layer) == 0) {
509: // disable first, in case fired func wants to start a new fade
510: mcp_fade_set_enabled(screen, layer, 0);
511: mcp_fade_fire_func(screen, layer);
512: } else if ((mcp_fade_get_level(screen, layer) - 1
513: - mcp_fade_get_rate(screen, layer)) <=
514: 0) {
515: // finished fading in
516: mcp_fade_set_level(screen, layer, 0);
517: } else {
518: // in progress
519: mcp_fade_set_level(screen, layer,
520: (mcp_fade_get_level(screen, layer) - 1 -
521: mcp_fade_get_rate(screen, layer)));
522: }
523: } // end if fading in/else/out
524: } // end if fading
525: } // end layer iteration
526: } // end screen iteration
527:
528: }
529:
530:
531:
532:
533: