漢王手寫輸入的系統部分(Palm平臺) (20千字)

看雪資料發表於2003-04-25

漢王手寫輸入的系統部分

工具: IDA Pro 4.x
平臺: Palm OS 3.5, Motorola 68000 指令集
下載: http://www.hwpen.net/download.htm

年初, 下了一個漢王試了一下, 覺得它的浮動選單做的比較好, 因為它在任何App下都可以冒出來. 要知道Palm是單任務的, 在一個時候只能有一個APP在執行. 當時剛剛接觸Palm, 如果要讓我實現這些功能, 還真的不知道怎麼下手. 於是想到了用IDA試試在非Windows平臺的反彙編. 最後得到的這些程式碼完全說明了它是怎樣實現這些功能的. 對於Palm程式設計師是有參考價值的.


另外, 有幾點需要特別說明:

1. 對於它的識別引擎, 我沒有做逆向, 因為我對它後面的高深理論不瞭解也沒有興趣.
2. 因為是為了學習, 而不是破解這套軟體, 我沒有去逆向它的註冊碼演算法部分. 從支援國產軟體方面考慮, 我也不會這樣做.(言下之意:漢王你可別找我麻煩 :) )
3. 這裡公佈的只是任何Palm書籍上都有的程式設計的最基礎部分, 應該不會對漢王在技術上構成什麼危害.
4. 這些可以編譯的程式碼只用於學習目的, 請勿做它用.
5. 用SetTrap的方法掛接HW_SysHandleEvent()而沒有其它措施是不安全的,因為系統在特定的時候會移動程式碼資源, 這肯定會讓系統垮掉, 應該使用DmDatabaseProtect()對這段程式碼資源加以保護. 希望漢王以後會這樣做.

// #include <windows.h>
#include <PalmOS.h>
#include <DLServer.h>
#include <PalmUtils.h>

#define version350                0x3503000
#define HW_MainForm                1000
#define RomIncompatibleAlert     1001
#define HWDefinedCmd            0xddbb

#define HW_COMMONCCHAR            0x0001
#define HW_SUBCOMMONCCHAR        0x0002
#define HW_TRA2SIMPLIFIED        0x0008
#define HW_NUMERIC                0x0010
#define HW_ENGLISH                0x0060
#define HW_SYMBOL                0x0180
#define HW_UPPER2LOWER            0x2000
#define HW_LOWER2UPPER            0x4000
#define HW_HALF2FULL            0x8000

// 選單模式
#define FOLDED    0    // 收成"V"
#define LOWER    1    // 在螢幕的下半部
#define UPPER    2    // 在螢幕的上半部
#define HIDDEN    3    // 隱藏

// 手寫系統階段
#define READY    0    // 就緒
#define ENTER    1    // 進入視窗或Form
#define LEAVE    2    // 離開視窗或Form
#define CHGFLD    3    // 切換到其它欄位

typedef struct tagHW_PREF {
    Boolean bInstallHW;        // 是否安裝漢王手寫輸入系統. 0
    Int8 al_b1;                // 未被使用 1
    UInt16 flags;            // 標誌位 2
    UInt16 RecogRange;        // 識別範圍 4
    Int8 RecogWaitTime;        // 識別等待時間 6
    Int8 PenThickness;        // 筆跡粗細 7
    Int8 PenColor;            // 筆的顏色 8
    Int8 al_b9;                // 未被使用 9
    Int16 ShotcutPenDownX;    // 快捷方式的起點螢幕座標 X a
    Int16 ShotcutPenDownY;    // 快捷方式的起點螢幕座標 Y c
    Int16 ShotcutPenUpX;    // 快捷方式的終點螢幕座標 X e
    Int16 ShotcutPenUpY;    // 快捷方式的終點螢幕座標 Y 10
    Boolean bSetting;        // 是否正在設定快捷方式 12
    Int8 bUsingShortcut;    // 是否正在使用快捷方式 13
    Boolean bSplitScreen;    // 全屏分割 14
    Boolean bHalfToFull;    // 半形字元轉成全形字元 15
    Boolean bUpperToLower;    // 大寫字母轉成小寫字母 16
    Boolean bLowerToUpper;    // 小寫字母轉成大寫字母 17
    Boolean bMouse;            // 是否作為滑鼠使用 18
    Boolean bReged;            // 是否是註冊使用者: 0xff 是, 0 否. 19
    Int8 MenuMode;            // 0: FOLDED, 1: LOWER, 2: UPPER, 3: HIDDEN 1a
    Boolean bMenuFolded;    // 選單是否被收成"V" 1b
    Boolean bOpenHW;        // 是否啟動漢王手寫輸入 1c
    Boolean bHalfScrnInput;    // 是否在半屏手寫模式 1d
    Boolean bRedrawMenu;    // 選單是否需要重畫 1e
    Int8 Phase;                // 手寫系統階段 0: READY, 1: ENTER, 2: LEAVE, 3: CHGFLD 1f
} HW_PREF;

typedef struct tagHW_PENS {
    UInt16 Cands[20][10];    // 候選字Buffer 0
    UInt8 index;            // 候選字在候選字Buffer中的下標索引 0x190/400
    UInt8 b191;                // not used
    UInt16 InsPtPos;        // 插入點偏移, 192h, 402
    UInt16 CandiDispOfs;    // 候選字顯示偏移 194h 404
    char* AssocBuffer;        // 聯想字buf 196h 406
    UInt16 AssocNum;        // 聯想字個數 19a 410
    UInt8 AssocOfs;            // 聯想字偏移 19c 412
    UInt8 AssocDispOfs;        // 聯想字顯示偏移 19d 413
} HW_PENS;

typedef struct tagHW_TRACE {
    PointType points[2048]; // 筆跡取樣點陣列 0
    UInt16    PtCount;        // 筆跡取樣存放位置 2000
    UInt32 dw2002; // 2002
    UInt8 b2006; // 2006
    UInt8 b2007; // 2007
    UInt8 b2008; // 2008
} HW_TRACE;



int HW_Install(void);
void HW_Uninstall(void);

UInt16 GetRecogRange(HW_PREF* pPref)
{
    UInt16 flags;

    if (pPref->RecogRange)
        flags = pPref->RecogRange;
    else
        flags = pPref->flags;

    if (pPref->bHalfToFull)
        flags |= 0x8000;

    if (pPref->bUpperToLower)
        flags |= 0x2000;

    if (pPref->bLowerToUpper)
        flags |= 0x4000;

    return flags;
}

Int8 GetMenuMode(void)
{
    Int16 x, y;

    InsPtGetLocation(&x, &y);

    if (y > 109)
        return UPPER;
    else
        return LOWER;
}

char V[] = "V";

void ClearMenu(Int8 MenuMode)
{
    RectangleType rc;

    if (MenuMode == HIDDEN) return;

    if (MenuMode == FOLDED) {
        WinEraseChars(V, 1, 141, 141);
        return;
    }

    if (MenuMode == LOWER)
        RctSetRectangle(&rc, 0, 118, 160, 30);
    else if (MenuMode == UPPER)
        RctSetRectangle(&rc, 0, 78, 160, 30);

    WinEraseRectangle(&rc, 0/*cornerDiam, 0 for square corners*/);
}

void RedrawCurForm(void)
{
    FormPtr frm = FrmGetActiveForm();
    WindowType* winHandle = FrmGetWindowHandle(frm);

    if (winGetActiveWindow() == winHandle && FrmValidatePtr(frm))
        FrmDrawForm(frm);
}

void DrawMenu(HW_PREF* pPrefs, HW_PENS* pHWPens) // a3 = pPrefs, a2 = pHWPens
{
    int ofs, x, d5;
    RectangleType rc;
    UInt16* ptr;

    if (pHWPens->index == 0)
        x = 0;
    else
        x = pHWPens->index - 1;

    if (pPrefs->MenuMode == UPPER) {
        RctSetRectangle(&rc, 2, 80, 155, 25);
        WinDrawRectangleFrame(dialogFrame, &rc);

        // This routine does not draw in the gray color; it draws with
        // alternating foreground and background pixels. That is, it uses
        // the grayPattern pattern type.
        WinDrawGrayLine(74, 93, 74, 104);
        WinDrawGragLine(2, 92, 156, 92);
        WinDrawChars("→", 2, 63, 94);

        WinDrawChars("英", 2, 76, 94);
        if (pPrefs->RecogRange == HW_ENGLISH) {
            RctSetRectangle(&rc, 75, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("數", 2, 88, 94);
        if (pPrefs->RecogRange == HW_NUMERIC) {
            RctSetRectangle(&rc, 87, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("符", 2, 100, 94);
        if (pPrefs->RecogRange == HW_SYMBOL) {
            RctSetRectangle(&rc, 99, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("全", 2, 112, 94);
        if (pPrefs->bHalfToFull) {
            RctSetRectangle(&rc, 111, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("窗", 2, 124, 94);

        WinDrawChars("鼠", 2, 136, 94);
        if (pPrefs->bMouse) {
            RctSetRectangle(&rc, 135, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }
        WinDrawChars("^", 1, 148, 94);
        WinDrawChars("←", 2, 132, 81);
        WinDrawChars("→", 2, 145, 81);

        d5 = pHWPens->CandiDispOfs * 2;
        ptr = pHWPens->Cands[x];

        // 顯示5個候選字
        if (ptr[d5] || ptr[d5 + 1]) {
            for (ofs = d5; ofs < d5 + 10; ofs += 2) {
                if (ptr[ofs]) {
                    WinDrawChars(&ptr[ofs], 2, x, 94);
                    x += 12;
                }
                else {
                    if (ptr[d5 + 1] <= ' ')
                        break;

                    WinDrawChars(&ptr[d5 + 1], 1, x, 94);
                    x += 12;
                }
            }
        }

        // 顯示10個聯想字
        if (pHWPens->AssocNum) {
            x = 3;
            for (ofs = pHWPens->AssocDispOfs; ofs < pHWPens->AssocOfs + 20; ofs += 2) {
                if (ofs - pHWPens->AssocOfs < pHWPens->AssocNum)
                    break;

                WinDrawChars(&pHWPens->AssocBuffer[ofs], 2, x, 81);
                x += 13;
            }
        }
    }
    else if (pPrefs->MenuMode == LOWER) {
        RctSetRectangle(&rc, 2, 120, 155, 25);
        WinDrawRectangleFrame(dialogFrame, &rc);

        // This routine does not draw in the gray color; it draws with
        // alternating foreground and background pixels. That is, it uses
        // the grayPattern pattern type.
        WinDrawGrayLine(74, 133, 74, 144);
        WinDrawGragLine(2, 132, 156, 312);
        WinDrawChars("→", 2, 63, 134);

        WinDrawChars("英", 2, 76, 134);
        if (pPrefs->RecogRange == HW_ENGLISH) {
            RctSetRectangle(&rc, 75, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("數", 2, 88, 134);
        if (pPrefs->RecogRange == HW_NUMERIC) {
            RctSetRectangle(&rc, 87, 93, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("符", 2, 100, 134);
        if (pPrefs->RecogRange == HW_SYMBOL) {
            RctSetRectangle(&rc, 99, 133, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("全", 2, 112, 134);
        if (pPrefs->bHalfToFull) {
            RctSetRectangle(&rc, 111, 133, 12, 12);
            WinInvertRectangle(&rc, 0);
        }

        WinDrawChars("窗", 2, 124, 134);

        WinDrawChars("鼠", 2, 136, 134);
        if (pPrefs->bMouse) {
            RctSetRectangle(&rc, 135, 133, 12, 12);
            WinInvertRectangle(&rc, 0);
        }
        WinDrawChars("^", 1, 148, 134);
        WinDrawChars("←", 2, 132, 121);
        WinDrawChars("→", 2, 145, 121);

        d5 = pHWPens->CandiDispOfs * 2;
        ptr = pHWPens->Cands[x];

        // 顯示5個候選字
        if (ptr[d5] || ptr[d5 + 1]) {
            for (ofs = d5; ofs < d5 + 10; ofs += 2) {
                if (ptr[ofs]) {
                    WinDrawChars(&ptr[ofs], 2, x, 134);
                    x += 12;
                }
                else {
                    if (ptr[d5 + 1] <= ' ')
                        break;

                    WinDrawChars(&ptr[d5 + 1], 1, x, 134);
                    x += 12;
                }
            }
        }

        // 顯示10個聯想字
        if (pHWPens->AssocNum) {
            x = 3;
            for (ofs = pHWPens->AssocDispOfs; ofs < pHWPens->AssocOfs + 20; ofs += 2) {
                if (ofs - pHWPens->AssocOfs > pHWPens->AssocNum)
                    break;

                WinDrawChars(&pHWPens->AssocBuffer[ofs], 2, x, 121);
                x += 13;
            }
        }
    }
    else if (pPrefs->MenuMode == FOLDED)
        WinDrawChars("V", 1, 141, 141);
}

void RedrawMenu(HW_PREF* pPrefs, char* pHWPens)
{
    WinHandle winHandle = WinSetDrawWindow(WinGetDisplayWindow());

    ClearMenu(pPrefs->MenuMode);
    DrawMenu(pPrefs, pHWPens);
    WinSetDrawWindow(winHandle);
}

FieldPtr GetCurField(FormPtr frm)
{
    // Too bad to access directly FormType data member. Palm strongly disencourage
    // such behaviors
    if (frm->focus != noFocus &&
        frm->objects[frm->focus].object.field != frmFieldObj&&
        frm->objects[frm->focus].object.field != frmTableObj)
    {
        return TblGetCurrentField(frm->objects[frm->focus].object.field);
    }

    return NULL;
}

void EnqueueChChar(FieldPtr fp, UInt16* pCand, Boolean bBackspace)
{
    MemHandle    memHandle;
    HW_PREF*    pPrefs;
    UInt16        InsPos;
    
    FtrGet('HW99', 10, (UInt32 *)&memHandle);
    pPrefs = (HW_PREF*)MemHandleLock(memHandle); // a2
    InsPos = FldGetInsPtPosition(fp);    // d3
    if (bBackspace)
        FldDelete(fp, InsPos - 2, InsPos);
    FldInsert(fp, pCand, 2);

    if (pPrefs->bReged != 0xff) {
        pPrefs->bReged++;
        pPrefs->bReged %= 10;
        if (pPrefs->bReged == 0)
            ShowNag();
    }

    MemHandleUnlock(memHandle);
    InsPos = FldGetInsPtPosition(fp);
    FldSetSelection(fp, InsPos, InsPos);
}

void GetAssoc(HW_PENS* pHWPens, UInt16 Cand)
{
    pHWPens->AssocNum = 0;
    pHWPens->AssocOfs = 0;
    if (GetAssocFromAssocBuf(pHWPens->AssocBuffer, Cand) > 0) {
        pHWPens->AssocNum = *pHWPens->AssocBuffer;
        pHWPens->AssocBuffer++;
        if (pHWPens->AssocNum == -1) {
            pHWPens->AssocNum += *pHWPens->AssocBuffer;
            pHWPens->AssocBuffer++;
        }

        pHWPens->AssocNum *= 2;
        pHWPens->AssocOfs = 0;
    }
}

void EnqueueInputKeys(FormType* frm, HW_PENS* pHWPens, UInt16 index, BOOL bAssoc)
{
    FieldPtr fp = GetCurField(frm); // a3
    WChar ascii;
    UInt16 InsPos;

    if (fp != NULL) {
        if ((UInt8)pHWPens->Cands[index][0] == 0) {
            ascii = *((UInt8*)&pHWPens->Cands[index][0] + 1);
            if (ascii > ' ') {
                EvtEnqueueKey(ascii, 0, 0);
                if (++pHWPeg-{>index == 20)
                    pHWPens->index = 0;
                pHWPens->CandiDispOfs = 0;
                pHWPens->InsPtPos = FldGetInsPtPosition(fp) + 1;
            }
            else if (ascii == ' ')
                EvtEnqueueKey(32, 0, 0);
            else if (ascii == 8)
                EvtEnqueueKey(backspaceChr, 0, 0);
            else if (ascii == '\n')
                EvtEnqueueKey('\r', 0, 0), EvtEnqueueKey('\n', 0, 0);
        }
        else {
            EnqueueChChar(fp, pHWPens->Cands[index], false);
            if (++pHWPeg-{>index == 20)
                pHWPens->index = 0;
            pHWPens->CandiDispOfs = 0;
            pHWPens->InsPtPos = FldGetInsPtPosition(fp);
            if (bAssoc)
                GetAssoc(pHWPens, pHWPens->Cands[index][0]);
        }
    }
}

void AdjustCandIndex(FormPtr frm, HW_PENS* pHWPens)
{
    UInt16 InsPos = FldGetInsPtPosition(GetCurField(frm)); // d3
    Int16 InsOfs = InsPos - pHWPens->InsPtPos;
    Int16 index;

    pHWPens->InsPtPos = InsPos;
    if (InsOfs > 40 || InsOfs < -40)
        return;

    if (pHWPens->index == 0)
        index = 19;
    else
        index = pHWPens->index - 1; // d3 = index

    if (InsOfs < 0) {
        do {
            if (pHWPens->Cands[index][0] == 0) {
                if (pHWPens->Cands[index][1] == 0) {
                    if (index == 19)
                        pHWPens->index = 0;
                    else
                        pHWPens->index = index + 1;
                    return;
                }
                else {
                    InsOfs++;
                    if (index == 0)
                        index = 19;
                    else
                        index--;
                }
            }
            else {
                InsOfs += 2;
                if (index == 0)
                    index = 19;
                else
                    index--;
            }
        } while (InsOfs < 0); // 5F4C here
    } // goto 5fa2
    else if (InsOfs > 0) {
        do {
            if (pHWPens->Cands[index][0] == 0) {
                if (pHWPens->Cands[index][1] == 0) {
                    if (index == 19)
                        pHWPens->index = 0;
                    else
                        pHWPens->index = index + 1;
                    return;
                }
                else {
                    InsOfs--;
                    if (index == 19)
                        index = 0;
                    else
                        index++;
                }
            }
            else {
                InsOfs  -= 2;
                if (index == 19)
                    index = 0;
                else
                    index++;
            }
        } while (InsOfs > 0);
    }

    if (index == 19)
        pHWPens->index = 0;
    else
        pHWPens->index = index + 1;
}

BOOL OnPenDown(EventPtr eventP, HW_PREF* pPrefs, HW_PENS* pHWPens, HW_TRACE* pHWTrace)
{
    Coord x, y;
    FormPtr frm;
    Int8 MenuMode;
    UInt16 screenX, screenY;

    // d3 = eventP(a6)
    // a2 = pPrefs
    // a3 = pHWPens
    // a4 = pHWTrace

    x = eventP->screenX;
    y = eventP->screenY;

    WinWindowToDisplayPt(&x, &y);
    if (x > 160 || y > 160)
        return 0;

    MenuMode = pPrefs->MenuMode;

    switch (MenuMode)
    {
    case FOLDED:
        if (screenX > 141 && screenX < 151 && screenY > 141 && screenY < 151)
            break;
    case LOWER:
        if (screenY > 120 && screenY < 145)
            break;
    case UPPER:
        if (screenY > 80 && screenY < 105)
            break;
    default:
        if (pPrefs->bMouse)
            return false;

        pHWTrace->b2006 = 0;
        pHWTrace->points[pHWTrace->PtCount].x = screenX;
        pHWTrace->points[pHWTrace->PtCount].y = screenY;
        if (pHWTrace->PtCount == 0)
            pHWTrace->b2007 = 1;
        pHWTrace->PtCount++;
        pHWTrace->dw2002 = 0;
        return true;
    }

    // 如果已有筆跡取樣點, 將新點加入筆跡陣列.
    if (pHWTrace->PtCount) {
        pHWTrace->points[pHWTrace->PtCount].x = screenX;
        pHWTrace->points[pHWTrace->PtCount].y = screenY;
        pHWTrace->PtCount++;
        return true;
    }
    
    pHWTrace->b2006 = true;
    if (MenuMode == LOWER && x > 3 && x < 63 && y > 134 && y < 146 ||
        MenuMode == UPPER && x > 3 && x < 63 && y > 94 && y < 106)
    {
        UInt16 ofs = (pHWPens->CandiDispOfs + (x - 1)) / 12;
        char* p = (char*)&pHWPens->Cands[pHWPens->index - 1][ofs];
        
        if (*p == 0) {
            if (*(p + 1) == 0)
                return true;
            EvtEnqueueKey(backspaceChr, 0, 0);
            EvtEnqueueKey(*(p + 1), 0, 0);
        }
        else {
            EnqueueChChar(GetCurField(FrmGetActiveForm()), p, true);
            GetAssoc(pHWPens, *(UInt16*)p);
        }
        pPrefs->bRedrawMenu = true;
        return true;
    }


    if (MenuMode == LOWER && x > 3 && x < 133 && y > 121 && y < 133 ||
        MenuMode == UPPER && x > 3 && x < 133 && y > 81 && y < 93) // else goto 6b4a
    {
        UInt16 pos = (x - 1) / 13 * 2;
        if (pHWPens->AssocOfs + pos < pHWPens->AssocNum) {
            EnqueueChChar(GetCurField(FrmGetActiveForm()), pHWPens->AssocBuffer[pos], false);
            pHWPens->AssocNum = 0;
            pHWPens->AssocOfs = 0;
        }
        return true;
    }

    if (MenuMode == LOWER && x > 63 && x < 75 && y > 134 && y < 146 ||
        MenuMode == UPPER && x > 63 && x < 75 && y > 94 && y < 106) // else goto 6be0
    {
        UInt16 ofs = pHWPens->CandiDispOfs + 5;
        if (ofs == 10) {
            pHWPens->CandiDispOfs = 0;
            pPrefs->bRedrawMenu = true;
        }
        else if (pHWPens->Cands[pHWPens->index - 1][ofs] != 0) {
            pHWPens->CandiDispOfs + 5;
            pPrefs->bRedrawMenu = true;
        }
        return true;
    }

    if (MenuMode == LOWER && x > 132 && x < 144 && y > 121 && y < 133 ||
        MenuMode == UPPER && x > 132 && x < 144 && y > 81 && y < 93) // else goto 6c4a
    {
        if (pHWPens->AssocOfs) {
            pHWPens->AssocOfs -= 20;
            pPrefs->bRedrawMenu = true;
        }
        return true;
    }

    if (MenuMode == LOWER && x > 145 && x < 157 && y > 121 && y < 133 ||
        MenuMode == UPPER && x > 145 && x < 157 && y > 81 && y < 93) // else goto 6cba
    {
        if (pHWPens->AssocOfs + 20 < pHWPens->AssocNum) {
            pHWPens->AssocOfs += 20;
            pPrefs->bRedrawMenu = true;
        }
        re

相關文章