Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 14645) +++ apps/plugins/CATEGORIES (working copy) @@ -1,3 +1,4 @@ +language_quizz,apps alpine_cdc,apps battery_bench,apps blackjack,games Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 14645) +++ apps/plugins/viewers.config (working copy) @@ -1,3 +1,4 @@ +jdic,apps/language_quizz,- ch8,viewers/chip8,0 txt,viewers/viewer,1 nfo,viewers/viewer,1 Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (revision 14645) +++ apps/plugins/SOURCES (working copy) @@ -1,4 +1,5 @@ /* plugins common to all models */ +language_quizz.c battery_bench.c chessclock.c credits.c Index: apps/plugins/language_quizz.c =================================================================== --- apps/plugins/language_quizz.c (revision 0) +++ apps/plugins/language_quizz.c (revision 0) @@ -0,0 +1,597 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: helloworld.c,v 1.2 2004/01/08 09:58:58 bagder Exp $ + * + * Copyright (C) 2002 Björn Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "plugin.h" +#include "playback_control.h" +#include "oldmenuapi.h" + +/* This macros must always be included. Should be placed at the top by + convention, although the actual position doesn't matter */ +PLUGIN_HEADER + +/* here is a global api struct pointer. while not strictly necessary, + it's nice not to have to pass the api pointer in all function calls + in the plugin */ +static struct plugin_api* rb; +#define MIN_MEM 120000 +#define MAX_DICT_ENTRIES 69 /*Max number of entries per dictionary file*/ +#define MAX_LEVEL_NBR 3 /*Number of file for which we remember the current level of progression (in learning mode)*/ +#define MAX_FILE_ID_SIZE 12 /*Size of the name of the dictionary (/.rockbox/rocks/jquizz-[MAX_FILE_ID_SIZE characters].jdic*/ +#define MAX_JAP_WORD_SIZE 41 /*Size max of a japanese entry*/ +#define MAX_EN_WORD_SIZE 20 /*Size max of an english entry*/ +#define MAX_WORD_SIZE MAX_JAP_WORD_SIZE + +struct { char jp[MAX_JAP_WORD_SIZE]; char en[MAX_EN_WORD_SIZE]; } dict[MAX_DICT_ENTRIES]; +struct { char name[MAX_FILE_ID_SIZE]; int lvl; } level[MAX_LEVEL_NBR]; +int level_nbr=0; /*Number of file for which we have the level of progression*/ +int current_level=1; /*Level of progression in the current dictionary (current_level * 5 => number of word the user have learned)*/ +int word_count=0; /*Number of entries in the curent dictionary*/ +char dicname[MAX_PATH]; + +struct +{ + enum { R2L, L2R, mixed } mode; + bool learning_mode; /*0 -> select entries in the whole dictionary / 1 -> select entries from the current_level*5 first entries*/ +} config; + +bool load_dict(int param) +{ + int dic; + char in; + bool jp=1; + int off=0; + word_count=0; + char filename[256]; + if (param == 1) + { + rb->snprintf(filename,50,"/.rockbox/rocks/apps/jquizz-%s.jdic",dicname); + }else{ + rb->splash(HZ*2,"Dictionary: %s", dicname); + rb->snprintf(filename,256,"%s", dicname); + } + dic = rb->open(filename,O_RDONLY); + if(dic<0) + { + rb->splash(HZ*1,"Unable to load dictionnarie file!"); + return 0; + } + + rb->lcd_clear_display(); + rb->splash(HZ*1,"Loading dictionnarie..."); + + while(rb->read(dic,&in,1)) + { + if(in=='\n') /*then new word*/ + { + dict[word_count].en[off]='\0'; + off=0; + jp=1; + word_count++; + continue; + } + + if(in=='\t') /*then japanese word finished*/ + { + dict[word_count].jp[off]='\0'; + off=0; + jp=0; + continue; + } + + if(jp) + dict[word_count].jp[off]=in; + else + dict[word_count].en[off]=in; + off++; + } + rb->close(dic); + return 1; +} + +void load_level(void) +{ + /*Set the current level to the one found in the config file*/ + int i; + if(!config.learning_mode) + return; + current_level = 1; + for(i=0;istrcmp(level[i].name,dicname)==0) + { + current_level = level[i].lvl; + return; + } + } +} + +void save_level(void) +{ + /*Save the current level*/ + int i; + if(!config.learning_mode) + return; + for(i=0;istrcmp(level[i].name,dicname)==0) + { + level[i].lvl = current_level; + return; + } + } + /*File id not found -> create an entrie for this file*/ + if(level_nbr == MAX_LEVEL_NBR) + { + rb->splash(HZ*1,"Too much level in config file!"); + return; + } + rb->strcpy(level[level_nbr].name,dicname); + level[level_nbr].lvl = current_level; + level_nbr++; +} + +void quizz(int param) +{ + int word,m; + int known_word; + int answer[3]; + int result=-1; + struct menu_item items[4]; + int nb_answered=0; + int nb_correct=0; + char question[MAX_WORD_SIZE+8]; + int question_in_jap = config.mode; + + items[0].function = NULL; + items[1].function = NULL; + items[2].function = NULL; + items[3].function = NULL; + + if(!load_dict(param)) + return; + + load_level(); + + if(config.learning_mode) + { + nb_answered = 30; + known_word = current_level*5 > word_count ? word_count : current_level*5; + } + else + known_word = word_count; + + while(1) + { + if(config.learning_mode && (rb->rand()%2)) + word = (rb->rand()%5) + known_word -5; + else + word = rb->rand()%known_word; + switch(rb->rand()%3) + { + case 0: + answer[0] = word; + answer[1] = rb->rand()%known_word; + answer[2] = rb->rand()%known_word; + while(answer[1] == word) /*don't select the same word twice!*/ + answer[1] = rb->rand()%known_word; + while(answer[2] == word || answer[2]==answer[1]) /*don't select the same word twice!*/ + answer[2] = rb->rand()%known_word; + break; + case 1: + answer[0] = rb->rand()%known_word; + answer[1] = word; + answer[2] = rb->rand()%known_word; + while(answer[0] == word) /*don't select the same word twice!*/ + answer[0] = rb->rand()%known_word; + while(answer[2] == word || answer[2]==answer[0]) /*don't select the same word twice!*/ + answer[2] = rb->rand()%known_word; + break; + case 2: + answer[0] = rb->rand()%known_word; + answer[1] = rb->rand()%known_word; + answer[2] = word; + while(answer[0] == word) /*don't select the same word twice!*/ + answer[0] = rb->rand()%known_word; + while(answer[1] == word || answer[1]==answer[0]) /*don't select the same word twice!*/ + answer[1] = rb->rand()%known_word; + break; + default: + break; + } + + /* Fill the menu:*/ + if(config.mode == mixed) + question_in_jap = rb->rand()%2; + + if(question_in_jap) + { + rb->snprintf(question,MAX_WORD_SIZE+8,"%s (%d/%d)", dict[word].jp, nb_correct, nb_answered); + items[0].desc = question; + items[1].desc = dict[answer[0]].en; + items[2].desc = dict[answer[1]].en; + items[3].desc = dict[answer[2]].en; + } + else + { + rb->snprintf(question,MAX_WORD_SIZE+8,"%s (%d/%d)", dict[word].en, nb_correct, nb_answered); + items[0].desc = question; + items[1].desc = dict[answer[0]].jp; + items[2].desc = dict[answer[1]].jp; + items[3].desc = dict[answer[2]].jp; + } + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + result=menu_show(m); + menu_exit(m); + + if(result<1 || result>3) + break; + + if(!config.learning_mode) + nb_answered++; + + if(word==answer[result-1]) + { + rb->splash(HZ*1,"Correct!"); + if(config.learning_mode) + { + if(word >= known_word-5) + nb_correct++; + + if(nb_correct == nb_answered) + { + nb_correct = 0; + rb->splash(HZ*1,"Level up!"); + current_level++; + if(current_level*5 > word_count) + { + current_level = 1; + save_level(); + config.learning_mode=0; + known_word = word_count; + rb->splash(HZ*1,"Dictionary learned!"); + } + else + known_word = current_level*5; + } + } + else + nb_correct++; + continue; + } + + if(config.learning_mode) + { + if(nb_correct>0 && word < known_word-5) + nb_correct--; + } + rb->splash(HZ*1,"Wrong!"); + if(question_in_jap) + rb->splash(HZ*1,"%s -> %s",dict[word].jp,dict[word].en); + else + rb->splash(HZ*1,"%s -> %s",dict[word].en,dict[word].jp); + } + save_level(); +} + +void load_config(void) +{ + int cfg_file; + char line[32]; + char* option; + char* value; + int i; + + rb->lcd_clear_display(); + rb->splash(HZ*1,"Loading config..."); + cfg_file = rb->open("/.rockbox/rocks/apps/jquizz.config",O_RDONLY); + + for(i=0;iread_line(cfg_file,line,32)) + { + rb->settings_parseline(line,&option,&value); + if(rb->strcmp(option,"mode")==0) + config.mode = rb->atoi(value); + else + if(rb->strcmp(option,"learning_mode")==0) + config.learning_mode = rb->atoi(value); + else + { + if(level_nbrstrcpy(level[level_nbr].name,option); + level[level_nbr].lvl = rb->atoi(value); + level_nbr++; + } + } + } + rb->close(cfg_file); +} + +void save_config(void) +{ + int cfg_file; + int i; + + rb->lcd_clear_display(); + rb->splash(HZ*1,"Saving config..."); + cfg_file = rb->open("/.rockbox/rocks/apps/jquizz.config",O_WRONLY|O_CREAT); + + if(cfg_file<0) + { + rb->splash(HZ*1,"Unable to save config!"); + return; + } + rb->fdprintf(cfg_file,"mode:%d\n",config.mode); + rb->fdprintf(cfg_file,"learning_mode:%d\n",config.learning_mode); + for(i=0;ifdprintf(cfg_file,"%s:%d\n",level[i].name,level[i].lvl); + rb->close(cfg_file); +} + +void fill_option_menu_two(char* mode,char* learning_mode) +{ + rb->strcpy(mode,"Mode:"); + rb->strcpy(learning_mode,"Learning:"); + switch(config.mode) + { + case R2L: + rb->strcat(mode,"R -> L"); + break; + case L2R: + rb->strcat(mode,"L -> R"); + break; + case mixed: + rb->strcat(mode,"R <-> L"); + break; + default: + break; + } + if(config.learning_mode) + rb->strcat(learning_mode,"yes"); + else + rb->strcat(learning_mode,"no"); +} + +void fill_option_menu(char* mode,char* learning_mode) +{ + rb->strcpy(mode,"Mode:"); + rb->strcpy(learning_mode,"Learning:"); + switch(config.mode) + { + case R2L: + rb->strcat(mode,"en->jp"); + break; + case L2R: + rb->strcat(mode,"jp->en"); + break; + case mixed: + rb->strcat(mode,"en<->jp"); + break; + default: + break; + } + if(config.learning_mode) + rb->strcat(learning_mode,"yes"); + else + rb->strcat(learning_mode,"no"); +} + + +void config_menu(int param_check) +{ + int m,result; + struct menu_item items[3]; + char mode[13]; + char learning_mode[13]; + bool exit=0; + + items[0].desc = mode; + items[0].function = NULL; + items[1].desc = learning_mode; + items[1].function = NULL; + items[2].desc = "Return"; + items[2].function = NULL; + + result = 0; + while(!exit) + { + if(param_check == 1){ + fill_option_menu(mode,learning_mode); + }else{ + fill_option_menu_two(mode,learning_mode); + } + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + result=menu_show(m); + menu_exit(m); + + switch(result) + { + case 0: + config.mode++; + if(config.mode>2) + config.mode = 0; + break; + case 1: + config.learning_mode = !config.learning_mode; + break; + default: + exit=1; + break; + } + } +} + +/* this is the plugin entry point */ +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + /* if you don't use the parameter, you can do like + this to avoid the compiler warning about it */ + /*(void)parameter;*/ + + /* if you are using a global api pointer, don't forget to copy it! + otherwise you will get lovely "I04: IllInstr" errors... :-) */ + rb = api; + + /* now go ahead and have fun! */ + int m; + int result; + bool exit=0; + load_config(); + + if(parameter) + { + enum menu_id + { + QUIT = 0, + QUIZZ, + OPTIONS, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + SHOW_PLAYBACK_MENU, +#endif + }; + + static const struct menu_item items[] = { + [QUIT] = + { "Quit", NULL }, + [QUIZZ] = + { "Quizz", NULL }, + [OPTIONS] = + { "Options", NULL }, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + [SHOW_PLAYBACK_MENU] = + { "Show Playback Menu", NULL }, +#endif + }; + + rb->strcpy(dicname, (char*) parameter); + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + while(!exit) + { + result=menu_show(m); + switch (result) + { + case QUIT: + exit=1; + break; + case QUIZZ: + quizz(2); + break; + case OPTIONS: + config_menu(2); + break; +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + case SHOW_PLAYBACK_MENU: + playback_control(rb); + break; +#endif + default: + exit=1; + break; + } + } + menu_exit(m); + }else{ + enum menu_id + { + QUIT = 0, + KANJI, + HIRAGANA, + KATAKANA, + OPTIONS, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + SHOW_PLAYBACK_MENU, +#endif + }; + + static const struct menu_item items[] = { + [QUIT] = + { "Quit", NULL }, + [KANJI] = + { "Kanji quizz", NULL }, + [HIRAGANA] = + { "Hiragana quizz", NULL }, + [KATAKANA] = + { "Katakana quizz", NULL }, + [OPTIONS] = + { "Options", NULL }, +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + [SHOW_PLAYBACK_MENU] = + { "Show Playback Menu", NULL }, +#endif + }; + + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + + while(!exit) + { + result=menu_show(m); + switch (result) + { + case QUIT: + exit=1; + break; + case KANJI: + rb->strcpy(dicname,"kanji-jlpt4"); + quizz(1); + break; + case HIRAGANA: + rb->strcpy(dicname,"hiragana"); + quizz(1); + break; + case KATAKANA: + rb->strcpy(dicname,"katakana"); + quizz(1); + break; + case OPTIONS: + config_menu(1); + break; +#if PLUGIN_BUFFER_SIZE >= MIN_MEM + case SHOW_PLAYBACK_MENU: + playback_control(rb); + break; +#endif + default: + exit=1; + break; + } + } + menu_exit(m); + +} + /*Save config as well as levels informations*/ + save_config(); + + return PLUGIN_OK; +}