482 lines
11 KiB
C
482 lines
11 KiB
C
//阅读器应用
|
||
|
||
#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);
|
||
}
|