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: