HanjaDS v03
From KBase
Contents |
[edit] Introduction
올해 우리 딸래미 승원이는 7살이 되었다. 그동안 NDS 에 손을 놓은 지도 오래 되었고 한자 관련된 것을 만들고 싶었긴 했는데 바쁘기도 하고 기회가 잘 안되었었다. 그런데 승원이가 즐겨 보는 만화 채널에서 태극천자문 이라는 것을 하고 있었다. 만화와 캐릭터 상품이 상업적으로 연계되는 것은 자주 있는 일이긴 하지만, 팽이나 로보트 보다는 한자 카드를 사 주는 것이 살아가는 데 더 도움되는 것이기를 기대하기도 한다.
Anti-aliasing 폰트 엔진 을 만든 이후 한자 출력을 하려고 보니, 한자 공부를 위해서는 글자를 크게 찍어야 한다는 것이었다. 그런데 내가 만든 폰트 엔진은 픽셀당 4비트나 사용하기 때문에 글자가 조금만 커져도 폰트 파일의 크기는 엄청나게 늘어난다. 그래서 RLE 압축을 하기로 한다. 이 기능은 NDS Homebrew Application Framework#Font 에 반영되어 자세히 설명되어 있다.
[edit] License
누구나 자유롭게 소스를 가져다 쓸 수 있습니다. 다만 원작자를 표시해 주는 센스~
[edit] Game Play
- 아래 화면에 나오는 버튼에는 키가 표시되어 있어서 터치스크린으로 눌러도 되고 해당 키를 눌러도 동작한다
- 이전/다음 글자, 이전/다음 페이지 는 좌우상하 키패드를 이용할 수 있다.
- 글자 보기 화면에서는 위 화면에 글자가 표시되고 약 4초가 지나면 뜻과 음이 나타난다. 나타나기 전에 뜻과 음을 먼저 말하면 되며 ^_^ 모르는 글자는 기다렸다가 확인하고 다음 글자로 넘어간다
- 승원이는 화살표 눌러가면서 모르는 글자가 몇 개인지 세는 방식으로 게임을 즐긴다.
- 종류별로 정리가 되어 있다 (v0.3 에서 어떤 항목은 아직 안됨)
[edit] Used Library
GyonG's NDS Homebrew Application Framework 를 사용한다.
[edit] Scene Description
[edit] InitScene
|
현재 메뉴는 다음과 같이 되어 있다
위 메뉴 중 첫번째만 구현을 진행하고 있고 나머지는 About 으로 연결되도록 되어 있다. 이 화면이 나올 때마다 배경색이 약간씩 무작위로 변경되도록 했다. |
[edit] ViewScene
|
화면 구성은 다음과 같다
글자의 배경색도 아주 조금씩 바뀐다. 아래 화면의 버튼들은 모두 버튼으로도 입력이 가능하다. 버튼 이름이 안 써 있는 이전/다음 페이지와 이전/다음 글자는 십자 버튼을 이용하여 조작한다. |
[edit] AboutScene
|
현재 버전 정보가 나온다. #InitScene 이나 #ViewScene 어느 곳에서 오더라도 아래 화면이 어두워지며 정보가 나타나며 아무 키나 누르면 이전 화면으로 되돌아간다. |
[edit] Font Set
HanjaDS v0.3 에서 사용하는 FontSet 은 다음과 같다.
FontSetEntry g_aFontSet[] =
{
{ FONTINDEX_DEF_ENGLISH, FONTINDEX_NOTHING, LucidaConsole_10pt_8x13_gfn },
{ FONTINDEX_DEF_KOREAN, FONTINDEX_DEF_ENGLISH, malgun_10pt_12x13_gfn },
{ FONTINDEX_DEF_HANJA, FONTINDEX_NOTHING, haeseo_262_24pt_30x32_gfn },
{ FONTINDEX_BIG_HANJA, FONTINDEX_NOTHING, haeseo_262_96pt_128x128_gfn },
{ FONTINDEX_TITLE_HANJA, FONTINDEX_NOTHING, haeseo_68pt_84x92_gfn },
{ FONTINDEX_SMALLEST_ENGLISH, FONTINDEX_NOTHING, LucidaConsole_5pt_5x7_gfn },
{ FONTINDEX_NOTHING, FONTINDEX_NOTHING, NULL }
};
위 정보는 다음과 같이 해석된다.
- 기본으로 사용하는 한글은 맑은고딕 10pt (12x13), 영어는 Lucida Console 10pt (8x13) 이다
- 상단 화면의 좌우 2개씩 나오는 한자는 한양해서 24pt (30x32) 이고 262 자만 포함되어 있다.
- 상단 화면의 큰 한자는 한양해서 96pt (128x128) 이고 역시 262 자 이다.
- #InitScene 의 "천자문" 부분은 한양해서 68pt (84x92) 를 사용하였다. "천", "자", "문" 세 글자만 들어 있다.
각 폰트 파일의 크기는 다음과 같다.
2.8K LucidaConsole_10pt_8x13.gfn 1.6K LucidaConsole_5pt_5x7.gfn 52K haeseo_262_24pt_30x32.gfn 270K haeseo_262_96pt_128x128.gfn 1.7K haeseo_68pt_84x92.gfn 139K malgun_10pt_12x13.gfn
[edit] Hanja Data
[edit] HanjaChar
글자 하나에 대한 정보를 기록한다. 뜻과 음은 2개 이상이 될 수도 있다고 가정했지만 현재 하나만 사용하고 있다.
struct HanjaChar {
const char *ch;
const char *data[3];
unsigned short wCh, reserved;
};
각 글자에 대한 실제 데이터는 다음과 같이 기록하며, sort 되어 있다.
static HanjaChar hanjaChars[] = {
{ "家", { "집 가", "", "" } },
{ "覺", { "깨달을 각", "", "" } },
{ "間", { "사이 간", "", "" } },
(중략)
{ "回", { "돌아올 회", "", "" } },
{ "凶", { "흉할 흉", "", "" } },
{ "吸", { "숨들이쉴 흡", "", "" } },
{ "", { "", "", "" } }
};
[edit] Page Entry
Page 를 나타내기 위해 다음 정보를 저장한다. 이 문자열에서 지정한 순서대로 나타나게 된다.
const char szCh1[] = "山川日月火水木金土石草花果林牛馬魚鳥犬羊竹貝春夏秋冬立年江天地風雨雲雪朝夕晝夜";
(중략)
#define CHARS_COUNT(x) x, sizeof (x) / 2
static struct PageEntry {
const char *title;
const char *chars;
const int count;
} g_aPages[] = {
{ "자연", CHARS_COUNT(szCh1) },
{ "인체,가정", CHARS_COUNT(szCh2) },
{ "수", CHARS_COUNT(szCh3) },
{ "천자카드3", CHARS_COUNT(szTCC3) },
{ "태극천자문1", CHARS_COUNT(szTC1) },
{ "태극천자문2", CHARS_COUNT(szTC2) },
{ "태극천자문3", CHARS_COUNT(szTC3) },
{ "태극천자문4", CHARS_COUNT(szTC4) },
{ NULL, NULL, 0 }
};
String literal 의 size 는 null char 를 포함하기 때문에 정확히는 (sizeof(x) - 1) / 2 라고 해야 맞지만 모두 한자이기 때문에 그냥 2로 나누어도 같은 값이 나온다.
위 각 분류(페이지)의 의미는 다음과 같다
- 자연/인체,가정/수
- 승원이한테 작년에 사 준 어린이 한자책에 있는 분류
- 천자카드3
- 승원이에게 어린이날 선물로 사 준 천자카드세트3권 의 글자들
- 태극천자문1
- Daum 키즈짱 태극천자문 에서 가져온 글자들. TV판 1회~10 회에 나온 글자들. 급수별, 회차별로 정리되어 있다.
- 태극천자문2,3,4
- 각각 TV판 11회~20회, 21~30회, 31~39회에 나온 글자들. 정리를 못 해서 가나다순이다.
[edit] class HanjaData
HanjaData 는 위의 모든 데이터를 얻기 위한 API 이다. global instance 하나만을 가지며 다음과 같은 method 를 갖는다.
class HanjaData
{
public:
void concat(char *str, int index);
int getData(const char *ch, char *data, int data_index = 0);
void getChar(int index, char *str);
int getCharCount();
int getPageCount();
void setPage(int page);
int getPage();
const char *getPageTitle();
HanjaData();
};
page 를 설정하고 index 를 넘겨 주면 해당하는 글자를 얻을 수 있고 (getChar) 글자를 얻은 후 얻은 글자로 getData 를 하면 뜻과 음을 얻을 수 있다. getData 는 sort 되어 있는 HanaChar array 에서 Binary Search 를 이용하여 글자를 찾는다. Binary Search 시 비교를 빨리 하기 위해 String compare 를 하지 않고 미리 ASCII 두 글자의 자리를 바꾸어 wCh 에 저장해 둔다.
#define SWAPBYTE(w) (((w) << 8) & 0xFF00) | (((w) >> 8) & 0x00FF)
HanjaData::HanjaData()
{
for (HanjaChar *p = hanjaChars; *p->ch; p++) {
p->wCh = SWAPBYTE(*(unsigned short *)p->ch);
}
}
글자를 찾을 때도 ch 를 바꾸어서 찾아야 한다. 아래는 Binary Search 를 하는 코드이다.
int HanjaData::getData(const char *ch, char *data, int data_index)
{
const int SIZEOF_HANJA = sizeof (hanjaChars) / sizeof (hanjaChars [0]) - 1;
int min = 0, max = SIZEOF_HANJA - 1;
unsigned short theCh = *(unsigned short *)ch;
theCh = SWAPBYTE(theCh);
while (min <= max) {
int mid = (min + max) / 2;
unsigned short mid_ch = hanjaChars[mid].wCh;
if (mid_ch == theCh) {
sprintf(data, "%d", mid);
strcpy(data, hanjaChars[mid].data[data_index]);
return 1;
}
if (mid_ch < theCh) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return 0;
}
[edit] Screen Shots & Downloads
[edit] hanjaDS
- 2008.05.13
- v0.3
- Page 이동이 단순히 10글자씩이던 것을 수정하여 분류별로 나오도록 수정함.
- Font 에서 FontSet 분리. 폰트 추가나 변경시 Font.cpp 를 건드리지 않도록 함.
- 폰트는 다시 한양해서 로 돌아옴. 옛체는 좀 알아보기 어려운 글자들이 있는 듯.
- 좌우에 표시되는 한자의 수를 3 에서 2 로 줄임.
- Media:HanjaDS.nds.080513.zip - HanjaDS.nds 만
- Media:HanjaDS.080513.zip - 모든 소스
[edit] Old Versions
Old HanjaDS - 이전 버전들
[edit] GFNMaker
기존 버전과는 호환되지 않는다.
- 2008.05.09
- v1.1
- Media:GFN.080509.zip - 실행 파일 및 소스. exe 도 포함되어 있는데, Debug 로 빌드한 버전이다. Release 로 빌드하니 웬일인지 죽어버린다.
[edit] Links
[edit] Other stuffs
- NDS Homebrew Tutorial
- Homebrew 와 관련된 정보들을 모아 놓은 홈
- Biological Warfare DS
- 세균전 for NDS
[edit] Guestbook
GyonG 방명록 에 글도 써 주삼~






