//阅读器应用 #include "stdio.h" #include "sys.h" #include "systick.h" #include "key.h" #include "lcd.h" #include "GameEngine.h" #include "SD.h" #include "APP_Reader.h" /**************************************** 私有定义 ****************************************/ typedef struct { FSIZE_t page_ptr; uint32_t page_size; } Page; //页结构定义 typedef struct { uint8_t font_size; uint32_t page_num; uint32_t page_amount; } Save; //存档结构定义 /*****************************************************************************************/ /**************************************** 全局变量 ****************************************/ FIL file; //文件 char filename[32]; //文件名 char filepath[32]; //文件路径 FIL page_file; //分页文件 char page_filepath[32]; //分页文件名 char page_filename[32]; //分页文件路径 FIL save_file; //存档文件 char save_filepath[32]; //存档文件名 char save_filename[32]; //存档文件路径 uint32_t page_amount = 1; //页数 uint8_t current_font_size = FONT_24; //当前字体设置 uint32_t current_page = 1; //当前页码 /*****************************************************************************************/ /**************************************** 私有函数 ****************************************/ uint8_t APP_Reader_ReadPage(uint8_t font_size, uint32_t page_num); uint8_t APP_Reader_SplitPages(uint8_t font_size); uint8_t APP_Reader_Menu(void); uint8_t APP_Reader_SaveWrite(void); uint8_t APP_Reader_SaveRead(void); void APP_Reader_Msg(uint8_t *head, uint8_t *content); void APP_Reader_Msg_NoBlock(uint8_t *head, uint8_t *content); /*****************************************************************************************/ /** * @brief 启动阅读器 */ void APP_Reader_Launcher(void) { //GUI 选取文件 uint8_t ret; if ((ret = SD_SelectFile(filename, "txt")) != SD_OK) { if (ret == SD_ERROR) APP_Reader_Msg("警告", "选取文件出错!\n\n请按任意键返回"); return; } SD_GetPath(filepath, filename); //打开文件 if (f_open(&file, filepath, FA_OPEN_EXISTING | FA_READ) != FR_OK) { APP_Reader_Msg("警告", "打开文件出错!\n\n请按任意键返回"); return; } //读存档 uint8_t is_saved = 0; if (APP_Reader_SaveRead() == 0) is_saved = 1; uint32_t save_page_amount = page_amount; //分页 if (APP_Reader_SplitPages(current_font_size) != 0) { APP_Reader_Msg("警告", "分页出错!\n\n请按任意键返回"); return; } if (is_saved == 0 || save_page_amount != page_amount) current_page = 1; //开始阅读 f_open(&page_file, page_filepath, FA_OPEN_EXISTING | FA_READ); //打开分页文件 while (1) { GE_Draw_ClrAll(WHITE); if (APP_Reader_ReadPage(current_font_size, current_page) != 0) { GE_Draw_ClrAll(WHITE); APP_Reader_Msg("警告", "发生错误!\n\n请按任意键返回"); return; } switch (KEY_GetKeyWait()) { case JOY_L_UP: //设置菜单 { if (APP_Reader_Menu() != 0) return; } break; case JOY_D_DOWN: //向后翻页 { if (current_page == page_amount) APP_Reader_Msg("提示", "已到最后一页!"); else current_page++; } break; case JOY_U_DOWN: //向前翻页 { if (current_page == 1) APP_Reader_Msg("提示", "已到第一页!"); else current_page--; } } } } /** * @brief 显示某一页 * @param font_size: 字体大小 * @param page_num: 页码,从 1 开始 * @retval 成功返回 0,失败返回 1 */ uint8_t APP_Reader_ReadPage(uint8_t font_size, uint32_t page_num) { FRESULT f_res; uint32_t br; uint32_t page_br; Page page; //读分页信息 f_res = f_lseek(&page_file, (page_num - 1) * sizeof(Page)); f_res = f_read(&page_file, &page, sizeof(Page), &page_br); //读取文件 uint8_t buffer[1500]; f_res = f_lseek(&file, page.page_ptr); f_res = f_read(&file, buffer, page.page_size, &br); if (f_res != FR_OK) return 1; *((uint8_t *)buffer + page.page_size) = '\0'; GE_Font_Print(0, 0, BORDER_MAX, BORDER_MAX, font_size, BLACK, WHITE, TRUE, buffer); GE_Font_Print( 1, 223, BORDER_MAX, BORDER_MAX, FONT_16, BLUE, WHITE, TRUE, "页数:%d/%d %.1f%%", page_num, page_amount, (float)page_num / (float)page_amount * 100.0); GE_Draw_Disp(); return 0; } /** * @brief 对文本文件分页 * @param font_size: 字体大小 * @retval 成功返回 0,失败返回 1 */ uint8_t APP_Reader_SplitPages(uint8_t font_size) { APP_Reader_Msg_NoBlock("提示", "正在分页中...\n\n请稍等"); uint32_t br; FRESULT f_res; char ch; uint8_t x, y; Page page; FSIZE_t fptr = 0; uint32_t page_bw; uint8_t x_max, y_max; if (font_size == FONT_16) { x_max = 40; y_max = 13; } else { x_max = 26; y_max = 9; } strcpy(page_filename, filename); strcat(page_filename, ".page"); SD_GetPath(page_filepath, page_filename); f_res = f_open(&page_file, page_filepath, FA_CREATE_ALWAYS | FA_WRITE); page_amount = 1; f_lseek(&file, 0); page.page_size = 0; while (1) { x = 0; y = 0; while (1) { if (f_res != FR_OK) return 1; f_res = f_read(&file, &ch, 1, &br); //读一个字符 page.page_size++; if (br == 0) //分页完毕 { page.page_ptr = fptr; f_res = f_write(&page_file, &page, sizeof(Page), &page_bw); f_res = f_close(&page_file); if (f_res != FR_OK) return 1; else return 0; } if (ch <= 0x80) //ASCII 字符 { if (ch == '\r' || ch == '\0') //忽略的字符 continue; if ((x + 1 > x_max) || (ch == '\n')) { x = 0; y++; if (y + 1 > y_max) { if (ch != '\n') f_res = f_lseek(&file, file.fptr - 1); page.page_size--; break; } } if (ch != '\n') x++; } else //GBK 字符 { f_res = f_lseek(&file, file.fptr + 1); page.page_size++; if (x + 2 > x_max) { x = 0; y++; if (y + 1 > y_max) { f_res = f_lseek(&file, file.fptr - 2); page.page_size -= 2; break; } } x += 2; } } page_amount++; //页数增加 page.page_ptr = fptr; //本页起始指针 f_res = f_write(&page_file, &page, sizeof(Page), &page_bw); //写入分页文件 page.page_size = 0; //页大小归零 fptr = file.fptr; //下页起始指针 } } /** * @brief 设置菜单 * @retval 返回阅读器返回 0,结束阅读返回 1 */ uint8_t APP_Reader_Menu(void) { uint8_t content[3][GE_GUI_MENUBOX_CONTENT_LEN] = {"字体设置", "退出阅读器", "返回"}; GE_Draw_Fill(50, 50, 220, 140, WHITE); switch (GE_GUI_MenuBox(50, 50, 220, 140, "菜单", 3, content, NULL)) { case 0: //返回 { KEY_ClearKey(); } break; case 1: //字体设置 { GE_Draw_Fill(50, 50, 220, 140, WHITE); uint8_t content[2][GE_GUI_MENUBOX_CONTENT_LEN] = {"大号字体", "小号字体"}; if (GE_GUI_MenuBox(50, 50, 220, 140, "字体设置", 2, content, NULL) == 1) { if (current_font_size == FONT_24) { APP_Reader_Msg("提示", "您正在使用大号字体!"); current_page--; } else { float read_pos = (float)current_page / (float)page_amount; current_font_size = FONT_24; f_close(&page_file); APP_Reader_SplitPages(current_font_size); f_open(&page_file, page_filepath, FA_OPEN_EXISTING | FA_READ); current_page = (float)page_amount * read_pos; } } else { if (current_font_size == FONT_16) { APP_Reader_Msg("提示", "您正在使用小号字体!"); current_page--; } else { float read_pos = (float)current_page / (float)page_amount; current_font_size = FONT_16; f_close(&page_file); APP_Reader_SplitPages(current_font_size); f_open(&page_file, page_filepath, FA_OPEN_EXISTING | FA_READ); current_page = (float)page_amount * read_pos; } } } break; case 2: //退出 { //关闭文件 f_close(&page_file); f_close(&file); APP_Reader_SaveWrite(); return 1; } break; case 3: //返回 { } } return 0; } /** * @brief 读存档 * @retval 有存档返回 0,无存档返回 1 */ uint8_t APP_Reader_SaveRead(void) { uint32_t save_br; Save save; strcpy(save_filename, filename); strcat(save_filename, ".save"); SD_GetPath(save_filepath, save_filename); if (f_open(&save_file, save_filepath, FA_OPEN_EXISTING | FA_READ) != FR_OK || f_read(&save_file, &save, sizeof(Save), &save_br) != FR_OK) { f_close(&save_file); return 1; } f_close(&save_file); if (save.font_size == FONT_16) current_font_size = FONT_16; else if (save.font_size == FONT_24) current_font_size = FONT_24; else return 1; current_page = save.page_num; page_amount = save.page_amount; return 0; } /** * @brief 写存档 * @retval 成功返回 0,失败返回 1 */ uint8_t APP_Reader_SaveWrite(void) { uint32_t save_bw; Save save; save.font_size = current_font_size; save.page_num = current_page; save.page_amount = page_amount; strcpy(save_filename, filename); strcat(save_filename, ".save"); SD_GetPath(save_filepath, save_filename); f_open(&save_file, save_filepath, FA_CREATE_ALWAYS | FA_WRITE); f_write(&save_file, &save, sizeof(Save), &save_bw); f_close(&save_file); return 0; } /** * @brief 消息框,任意键按下后退出 * @param head: 标题 * @param content: 内容 */ void APP_Reader_Msg(uint8_t *head, uint8_t *content) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, head, content, NULL); KEY_WaitKey(JOY_L); } /** * @brief 消息框,任意键按下后退出 * @param head: 标题 * @param content: 内容 */ void APP_Reader_Msg_NoBlock(uint8_t *head, uint8_t *content) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, head, content, NULL); }