User:Lucas Brooks/Researches/Windows 1.0 DR5 Notepad Decompilation

First full decompilation of a Windows 1.0 DR5 application. Picked Notepad because its source code was released by Microsoft through Chicago SDKs before. DR5's Notepad does not have anything in common with the Notepad application in modern versions of Windows, the code was rewritten from scratch after DR5.

This isn't a step by step guide, it simply outlines the main steps required to fully decompile a DR5 application to source code. You must have a good level of knowledge before attempting this.

Module Definition File Reconstruction[edit | edit source]

The NE header includes all the information required to reconstruct the Module Definition for an executable. We can recover information about the segments, stack/heap size and Load/Free procedures by examining the HEADER segment.

HEADER:0000 HEADER          segment byte public 'MODULE' use16
HEADER:0000                 assume cs:HEADER
HEADER:0000                 assume es:nothing, ss:nothing, ds:_DATA, fs:nothing, gs:nothing
...
HEADER:0012 flags           dw 1
...
HEADER:0018 HeapSize        dw 2048
HEADER:001A StackSize       dw 0
HEADER:001C StartProc       dd 0
HEADER:0020 LoadProc        dd 2098Bh
HEADER:0024 FreeProc        dd 20AB9h
...
HEADER:004F HEADER          ends

Application flag of 1 means there can only be a single data segment (the application may only start once per session). Stack size of 0 means it wasn't defined in the Module Definition file and StartProc being 0 (NULL) means no StartProc. Looking at ENTNAME segment we can recover the module name.

ENTNAME:0098 ENTNAME         segment byte public 'MODULE' use16
ENTNAME:0098                 assume cs:ENTNAME
ENTNAME:0098                 ;org 98h
ENTNAME:0098                 assume es:nothing, ss:nothing, ds:_DATA, fs:nothing, gs:nothing
ENTNAME:0098                 db 7,'NOTEPAD'
ENTNAME:00A0                 dw 0
ENTNAME:00A2                 db 0
ENTNAME:00A2 ENTNAME         ends

Module name is quite obvious. Next, we recover segment information by looking at the SEGTABLE segment.

SEGTABLE:0050 SEGTABLE        segment byte public 'MODULE' use16
SEGTABLE:0050                 assume cs:SEGTABLE
SEGTABLE:0050                 ;org 50h
SEGTABLE:0050                 assume es:nothing, ss:nothing, ds:_DATA, fs:nothing, gs:nothing
SEGTABLE:0050                 SEGENT <41h, 0, 1Ch, 1, 9Ch, 40h>
SEGTABLE:005C                 SEGENT <0, 1Dh, 0AEh, 37h, 0, 5Ch>
SEGTABLE:005C SEGTABLE        ends

Two moveable segment, first is the code segment (_TEXT) and second is the data segment (_DATA).

Now we have all the information required to reconstruct the Module Definition file. It looks like this:

NAME    Notepad
CODE MOVEABLE DATA MOVEABLE SINGLE
HEAPSIZE 2048
LOADPROC NotepadLoad FREEPROC NotepadFree

Source Code Reconstruction[edit | edit source]

There is no decompiler for the 8086/8088 architecture, so decompilation must be done manually. All API functions use the PASCAL calling convention (arguments pushed in the order they appear in the source code, and the caller is not responsible for the stack cleanup). Start the process by reading the disassembly and identifying offset references. Once the disassembly is good enough so that it can be re-assembled back to the exact same binary, the decompilation should begin. Start with the LoadProc first, and then decompile each function called by the LoadProc, and each function they call and etc., until all loader functions have been decompiled. Then decompile the class procedures - the PaintProc, the InputProc and etc., and the functions they call. Finally decompile the FreeProc and whatever else not referenced or decompiled. Reconstruct the header file if you can identify constants.

Decompiled C code:

/*                                      */
/*    Notepad application               */
/*                                      */
/*   Copyright (C) 1984 Microsoft Inc.  */
/*                                      */

#include "windows.h"
#include "notepad.h"

/* function prototypes */
int FAR PASCAL NotepadCreate(HWND);
BOOL FAR PASCAL NotepadDestroy(HWND, int, FARPROC);
int FAR PASCAL NotepadIcon(HWND, int);
int FAR PASCAL NotepadSize(HWND, int, int);
int FAR PASCAL NotepadInput(HWND, int, RESULTBLOCK far *);
void NEAR PASCAL DisplayDialog(int);
BOOL FAR PASCAL CallDlgProc(HWND, int, RESULTBLOCK far *);
BOOL NEAR PASCAL OpenProc(HWND, int, RESULTBLOCK far *);
BOOL NEAR PASCAL SaveAsProc(HWND, int, RESULTBLOCK far *);
BOOL NEAR PASCAL SaveChangesProc(HWND, int, RESULTBLOCK far *);
BOOL NEAR PASCAL CreateProc(HWND, int, RESULTBLOCK far *);
BOOL NEAR PASCAL ErrorProc(HWND, int, RESULTBLOCK far *);
void NEAR PASCAL SaveFile();
void NEAR PASCAL ReadFile(LPSTR);
void NEAR PASCAL NameToUpper(LPSTR);
BOOL NEAR PASCAL DoesFileExist();
int FAR PASCAL NotepadScroll(HWND, int, int);
int NEAR PASCAL PosToLine(int);
BOOL FAR PASCAL NotepadLoad(HANDLE, HANDLE, LPNEWPARMS);
void FAR PASCAL NotepadFree(HANDLE);


/* initialized global variables */
int iLine = 0;
FARPROC pCallDlgProc = (FARPROC)CallDlgProc;
NEARPROC DlgProcs[6] = {OpenProc, SaveAsProc, SaveChangesProc, CreateProc, ErrorProc, ErrorProc};
PSTR DlgNames[6] = {"dtOpen", "dtSaveAs", "dtSaveChanges", "dtCreate", "dtFileTooBig", "dtDiskError"};


/* uninitialized global variables */
char szFileName1[20];                   /* main filename - the current file */
HRF hResFile;                           /* resource file handle */
WORD hFile;                             /* file handle for text files */
char szFileName2[20];                   /* temp filename used for creating files */
HWND hCtrl;                             /* edit control - the soul of the program */
HWND hParent;                           /* the shell of the soul */
BOOL (NEAR PASCAL *pDlgProc)(HWND, int, RESULTBLOCK far *);     /* pointer to the desired dlg proc */

/* enums for arrays */
enum DialogsIDs {DLG_OPEN, DLG_SAVEAS, DLG_SAVE, DLG_CREATE, DLG_TOOBIG, DLG_DISKERROR};


/* code - meat of the program */

int FAR PASCAL NotepadCreate(hWindow)
HWND hWindow;
{
    /* create the edit control */
    CTLDEF CtlDef;
    CtlDef.hwndParent = hParent = hWindow;
    CtlDef.lpszClass = (LPSTR)"MEdit";
    CtlDef.x = 0;
    CtlDef.y = 0;
    CtlDef.cx = 0;
    CtlDef.cy = 0;
    CtlDef.bOverlap = FALSE;
    CtlDef.bVisible = TRUE;
    CtlDef.bBorder = FALSE;
    CtlDef.bHScroll = FALSE;
    CtlDef.bVScroll = FALSE;
    CtlDef.style = ELEFT;
    CtlDef.ID = 0;
    CtlDef.lpszText = (LPSTR)"";
    hCtrl = CreateCtl((LPCTLDEF)&CtlDef);

    /* set the filename to be the window title */
    GetName(hWindow, 20, (LPSTR)szFileName1);

    /* open or create the file and read it */
    hFile = _lopen((LPSTR)szFileName1, 0);
    if (hFile == -1)
    {
        hFile = _lcreat((LPSTR)szFileName1, 0);
    }
    ReadFile((LPSTR)szFileName1);
}

BOOL FAR PASCAL NotepadDestroy(hWindow, command, lpProc)
HWND hWindow;
int command;
FARPROC lpProc;
{
    /* save the file if there are unsaved changes */
    if (command == QUERYDESTROY)
    {
        if (CtlMsg0(hCtrl, EMGETMODIFY))
        {
            SaveFile();
        }
    }
    return TRUE;
}

int FAR PASCAL NotepadIcon(hWindow, bIconic)
HWND hWindow;
int bIconic;
{
    /* save the file if there are unsaved changes */
    if (bIconic == WINDOWCLOSE)
    {
        if (CtlMsg0(hCtrl, EMGETMODIFY))
        {
            SaveFile();
        }
    }
}

int FAR PASCAL NotepadSize(hWindow, newWidth, newHeight)
HWND hWindow;
int newWidth;
int newHeight;
{
    int iLineIndex;
    RECT rcRect;

    /* handle resizes if window is not iconic */
    if(!IsIconic(hWindow))
    {
        SetCtlRedraw(hCtrl, FALSE);
        iLineIndex = (int)CtlMsg1(hCtrl, EMLINEINDEX, iLine);
        GetClientRect(hWindow, (LPRECT)&rcRect);
        MoveCtl(hCtrl, hWindow, 0, 0, rcRect.right, rcRect.bottom, 0);
        InflateRect((LPRECT)&rcRect, -8, -2);
        rcRect.bottom = 32000;
        CtlMsg2(hCtrl, EMSETRECTNP, (long)&rcRect);
        CtlMsg2(hCtrl, EMLINESCROLL, MAKELONG(0, -PosToLine(iLineIndex)));
        SetCtlRedraw(hCtrl, TRUE);
    }
}

int FAR PASCAL NotepadInput(hWindow, argc, argv)
HWND hWindow;
int argc;
RESULTBLOCK FAR *argv;
{
    HWND hClipWnd;
    int iNumFmts;
    HWND hFocusWnd;
    HANDLE hFormats;
    int n;
    int iFormat;
    HANDLE hText;
    LPDATAFORMATS lpFormats;

    if (pDlgProc == NULL)
    {
        n = argv[0].resval.numval.n;
        hParent = hWindow;
        switch (n)
        {
            case MI_OPEN:
                /* file --> open */
                if(!(int)CtlMsg0(hCtrl, EMGETMODIFY))
                {
                    /* if no unsaved changes */
                    DisplayDialog(DLG_OPEN);
                    break;
                }
                /* prompt the user to save the file */
                DisplayDialog(DLG_SAVE);
                break;

            case MI_SAVE:
                /* file --> save */
                SaveFile();
                break;

            case MI_SAVEAS:
                /* file --> save as */
                DisplayDialog(DLG_SAVEAS);
                break;

            case MI_CUT:
                /* edit --> cut */
                hFocusWnd = GetFocus();
                if(!ExchangeData(hFocusWnd, QUERYFORMAT, TEXT_FORMAT, NULL, (LPHANDLE)&hFormats))
                {
                    lpFormats = (LPDATAFORMATS)LockObject(hFormats);
                    iNumFmts = lpFormats->noOfFormats;
                    iFormat = *lpFormats->DataFormats;
                    UnlockObject(hFormats);
                    if (iNumFmts != 0)
                    {
                        hClipWnd = GetClipboardWnd();
                        if (hClipWnd != NULL)
                        {
                            if(!ExchangeData(hFocusWnd, SENDSELECTION, iFormat, NULL, (LPHANDLE)&hText))
                            {
                                /* delete clipboard and edit control text then paste to clip*/
                                ExchangeData(hFocusWnd, DELETESELECTION, iFormat, NULL, (LPHANDLE)NULL);
                                ExchangeData(hClipWnd, DELETESELECTION, TEXT_FORMAT, NULL, (LPHANDLE)NULL);
                                ExchangeData(hClipWnd, INSERTSELECTION, iFormat, hText, (LPHANDLE)&hParent);
                            }
                        }
                    }
                }
                break;

            case MI_COPY:
                /* edit --> copy */
                hFocusWnd = GetFocus();
                if(!ExchangeData(hFocusWnd, QUERYFORMAT, TEXT_FORMAT, NULL, (LPHANDLE)&hFormats))
                {
                    lpFormats = (LPDATAFORMATS)LockObject(hFormats);
                    iNumFmts = lpFormats->noOfFormats;
                    iFormat = *lpFormats->DataFormats;
                    UnlockObject(hFormats);
                    if (iNumFmts != 0)
                    {
                        hClipWnd = GetClipboardWnd();
                        if (hClipWnd != NULL)
                        {
                            if(!ExchangeData(hFocusWnd, SENDSELECTION, iFormat, NULL, (LPHANDLE)&hText))
                            {
                                /* delete clipboard text then paste to clip*/
                                ExchangeData(hClipWnd, DELETESELECTION, TEXT_FORMAT, NULL, (LPHANDLE)NULL);
                                ExchangeData(hClipWnd, INSERTSELECTION, iFormat, hText, (LPHANDLE)&hParent);
                            }
                        }
                    }
                }
                break;

            case MI_PASTE:
                /* edit --> paste */
                hFocusWnd = GetFocus();
                if(!ExchangeData(hFocusWnd, QUERYFORMAT, TEXT_FORMAT, NULL, (LPHANDLE)&hFormats))
                {
                    lpFormats = (LPDATAFORMATS)LockObject(hFormats);
                    iNumFmts = lpFormats->noOfFormats;
                    iFormat = *lpFormats->DataFormats;
                    UnlockObject(hFormats);
                    if (iNumFmts != 0)
                    {
                        hClipWnd = GetClipboardWnd();
                        if (hClipWnd != NULL)
                        {
                            if(!ExchangeData(hClipWnd, SENDSELECTION, iFormat, NULL, (LPHANDLE)&hText))
                            {
                                /* check clipboard text size */
                                if (hCtrl == hFocusWnd)
                                {
                                    if (GetSize(hText) >= 6144)
                                    {
                                        DisplayDialog(DLG_TOOBIG);
                                        break;
                                    }
                                }
                                /* delete selected text and paste from clipboard */
                                ExchangeData(hFocusWnd, DELETESELECTION, TEXT_FORMAT, NULL, (LPHANDLE)NULL);
                                ExchangeData(hFocusWnd, INSERTSELECTION, iFormat, hText, (LPHANDLE)&hParent);
                                /* check edit control text size */
                                if (hCtrl == hFocusWnd)
                                {
                                    if (GetCtlTextLength(hCtrl) >= 6144)
                                    {
                                        DisplayDialog(DLG_TOOBIG);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                break;

            case MI_CLEAR:
                /* edit --> clear */
                hFocusWnd = GetFocus();
                if(!ExchangeData(hFocusWnd, QUERYFORMAT, TEXT_FORMAT, NULL, (LPHANDLE)&hFormats))
                {
                    lpFormats = (LPDATAFORMATS)LockObject(hFormats);
                    iNumFmts = lpFormats->noOfFormats;
                    iFormat = *lpFormats->DataFormats;
                    UnlockObject(hFormats);
                    if (iNumFmts != 0)
                    {
                        hClipWnd = GetClipboardWnd();
                        if (hClipWnd != NULL)
                        {
                            /* delete the selected text */
                            ExchangeData(hFocusWnd, DELETESELECTION, TEXT_FORMAT, NULL, (LPHANDLE)NULL);
                        }
                    }
                }
                break;

            case MI_SELALL:
                /* edit --> select all */
                SetEditSel(hCtrl, 0, 0x7FFF);       /* select everything (from 0 to max) */
                break;

            default:
                break;
        }
    }
    return;
}

void NEAR PASCAL DisplayDialog(iDlgID)
int iDlgID;
{
    /* set the dlg proc to what we want and create the dlg */
    pDlgProc = DlgProcs[iDlgID];
    CreateDlg(hParent, pCallDlgProc, (HANDLE)hResFile, (LPSTR)DlgNames[iDlgID]);
}

BOOL FAR PASCAL CallDlgProc(hDlg, cArg, rgArg)
HWND hDlg;
int cArg;
RESULTBLOCK far *rgArg;
{
    /* call the near dlg proc */
    return (*pDlgProc)(hDlg, cArg, rgArg);
}

BOOL NEAR PASCAL OpenProc(hDlg, cArg, rgArg)
HWND hDlg;
int cArg;
RESULTBLOCK far *rgArg;
{
    switch (rgArg[0].resval.numval.n)
    {
        case 0:
            /* dialog init - set current filename as default text */
            SetDlgItemText(hDlg, 3, (LPSTR)szFileName1);
            break;

        case 1:
            /* clicked open */
            /* save text to a temp buffer and convert to upper */
            GetDlgItemText(hDlg, 3, (LPSTR)szFileName2, 20);
            NameToUpper((LPSTR)szFileName2);

            /* close the dialog */
            pDlgProc = NULL;
            DestroyDlg(hDlg);

            /* set loading status and read file - show create dlg if cannot open */
            SetName(hParent, (LPSTR)"Loading file...");
            hFile = _lopen((LPSTR)szFileName2, 0);
            if (hFile == -1)
            {
                DisplayDialog(DLG_CREATE);
                break;
            }
            ReadFile((LPSTR)szFileName2);

            /* clear loading status */
            SetName(hParent, (LPSTR)szFileName1);
            break;

        case 2:
            /* clicked cancel - close the dialog and do nothing */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            break;

        default:
            return FALSE;
    }
    return TRUE;
}

BOOL NEAR PASCAL SaveAsProc(hDlg, cArg, rgArg)
HWND hDlg;
int cArg;
RESULTBLOCK far *rgArg;
{
    char szFileName[20];

    switch (rgArg[0].resval.numval.n)
    {
        case 0:
            /* dialog init - set current filename as default text */
            SetDlgItemText(hDlg, 3, (LPSTR)szFileName1);
            break;

        case 1:
            /* clicked save */
            /* get input and convert to upper */
            GetDlgItemText(hDlg, 3, (LPSTR)szFileName, 20);
            NameToUpper((LPSTR)szFileName);

            /* set as the main filename */
            lstrcpy((LPSTR)szFileName1, (LPSTR)szFileName);

            /* close diaglog */
            pDlgProc = NULL;
            DestroyDlg(hDlg);

            /* save file */
            SaveFile();
            break;

        case 2:
            /* clicked cancel - close the dialog and do nothing */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            break;

        default:
            return FALSE;
    }
    return TRUE;
}

BOOL NEAR PASCAL SaveChangesProc(hDlg, cArg, rgArg)
HWND hDlg;
int cArg;
RESULTBLOCK far *rgArg;
{
    switch (rgArg[0].resval.numval.n)
    {
        case 0:
            /* dialog init */
            return FALSE;

        case 1:
            /* clicked yes - save file and close dlg */
            SaveFile();
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            DisplayDialog(DLG_OPEN);
            break;

        case 2:
            /* clicked no - close dlg and display open file dlg */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            DisplayDialog(DLG_OPEN);
            break;

        case 3:
            /* clicked cancel - close the dialog and do nothing */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            break;

        default:
            return FALSE;
    }
    return TRUE;
}

BOOL NEAR PASCAL CreateProc(hDlg, cArg, rgArg)
HWND hDlg;
int cArg;
RESULTBLOCK far *rgArg;
{
    switch (rgArg[0].resval.numval.n)
    {
        case 1:
            /* clicked ok - create file, read and set window name */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            hFile = _lcreat((LPSTR)szFileName2, 0);
            ReadFile((LPSTR)szFileName2);
            SetName(hParent, (LPSTR)szFileName1);
            break;

        case 2:
            /* clicked cancel - close the dialog and do nothing */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            break;

        default:
            return FALSE;
    }
    return TRUE;
}

BOOL NEAR PASCAL ErrorProc(hDlg, cArg, rgArg)
HWND hDlg;
int cArg;
RESULTBLOCK far *rgArg;
{
    switch (rgArg[0].resval.numval.n)
    {
        case 1:
            /* clicked cancel - close the dialog and do nothing */
            pDlgProc = NULL;
            DestroyDlg(hDlg);
            break;

        default:
            return FALSE;
    }
    return TRUE;
}

void NEAR PASCAL SaveFile()
{
    int iLength;
    HANDLE pBuffer;
    LPSTR lpBuffer;

    /* set status */
    SetName(hParent, (LPSTR)"Saving file...");

    /* get text length, allocate memory and copy to memory */
    iLength = GetCtlTextLength(hCtrl);
    pBuffer = Alloc(GLOBAL + MOVEABLE, (long)(iLength + 1));
    lpBuffer = LockObject(pBuffer);
    GetCtlText(hCtrl, lpBuffer, iLength + 1);

    /* create or erase the file */
    hFile = _lcreat((LPSTR)szFileName1, 0);

    /* check file handle and write to file */
    if (hFile == -1)
    {
        DisplayDialog(DLG_DISKERROR);
    }
    else
    {
        _lwrite(hFile, lpBuffer, (long)iLength);
        _lclose(hFile);
        CtlMsg1(hCtrl, 9, 0);
    }

    /* dispose the allocated memory and clear status */
    UnlockObject(pBuffer);
    Free(pBuffer);
    SetName(hParent, (LPSTR)szFileName1);
}

void NEAR PASCAL ReadFile(szFileName)
LPSTR szFileName;
{
    long dwLength;
    HANDLE pBuffer;
    LPSTR lpTextBuf;

    /* check if handle is invalid */
    if (hFile == -1)
    {
        DisplayDialog(DLG_DISKERROR);
        return;
    }
    
    /* get length and check if too big*/
    dwLength = _llseek(hFile, 0L, 2);
    if (dwLength > 6144)
    {
        _lclose(hFile);
        DisplayDialog(DLG_TOOBIG);
        return;
    }

    /* read file, set control text and clean up */
    pBuffer = Alloc(GLOBAL + MOVEABLE, dwLength + 1);   /* allocate file size + 1 bytes of memory */
    lpTextBuf = LockObject(pBuffer);
    _llseek(hFile, 0L, 0);
    _lread(hFile, lpTextBuf, dwLength);
    _lclose(hFile);
    lpTextBuf[dwLength] = '\0';                         /* convert to null-terminated string */
    iLine = 0;
    SetCtlText(hCtrl, lpTextBuf);
    UnlockObject(pBuffer);
    Free(pBuffer);

    /* set name of this file as the main filename */
    lstrcpy((LPSTR)szFileName1, (LPSTR)szFileName);     
}

void NEAR PASCAL NameToUpper(szFileName)
LPSTR szFileName;
{
    /* convert filename to upper case */
    int letter;
    while (TRUE)
    {
        letter = *szFileName;
        if (letter == 0)
        {
            break;
        }
        if (letter >= 'a' && letter <= 'z')
        {
            *szFileName += -32;
        }
        szFileName++;
    }
}

BOOL NEAR PASCAL DoesFileExist()
{
    /* test if we can open a file */
    WORD hFileTest = _lopen((LPSTR)szFileName1, 0);
    _lclose(hFileTest);

    if (hFileTest != -1)
    {
        return TRUE;
    }
    return FALSE;
}

int FAR PASCAL NotepadScroll(hWindow, scrollCommand, amount)
HWND    hWindow;
int     scrollCommand;
int     amount;
{
    int iNewPos;
    int iNewLine;
    int iLineIndex = (int)CtlMsg1(hCtrl, EMLINEINDEX, iLine);
    int iTextLen = GetCtlTextLength(hCtrl);

    switch (scrollCommand)
    {
        case LINEUP:
            /* scroll up by a line (if not at start) */
            if (iLineIndex != 0)
            {
                CtlMsg2(hCtrl, EMLINESCROLL, MAKELONG(0, 1));
                iLine--;
                break;
            }
            break;

        case LINEDOWN:
            /* scroll down by a line (if not at end) */
            if (iLineIndex != iTextLen)
            {
                CtlMsg2(hCtrl, EMLINESCROLL, MAKELONG(0, -1));
                iLine++;
                break;
            }
            break;

        case VERTICALPOSITION:
            /* get the position based on a percentage */
            iNewPos = (unsigned int)amount * iTextLen / 100;
            iNewLine = PosToLine(iNewPos);
            CtlMsg2(hCtrl, EMLINESCROLL, MAKELONG(0, iLine - iNewLine));
            iLine = iNewLine;
            break;

        case TOPPERCENTAGE:
            /* return the position of the thumb as a percentage */
            if (iTextLen == 0)
            {
                return 0;
            }
            return (unsigned int)100 * iLineIndex / iTextLen;

        default:
            break;
    }
    return;
}

int NEAR PASCAL PosToLine(iLineIndex)
int iLineIndex;
{
    /* loop until we're at or past the desired position */
    int i;
    for (i = 0; LOWORD(CtlMsg1(hCtrl, EMLINEINDEX, i + 1)) < iLineIndex; i++)
    {
    }
    return i;
}

BOOL FAR PASCAL NotepadLoad(hInstance, hPrev, lpParms)
HANDLE hInstance;
HANDLE hPrev;
LPNEWPARMS lpParms;
{
    PWNDCLASS pNotepadCls = (PWNDCLASS)LocalAlloc(LPTR, sizeof(WNDCLASS));
    if (hResFile = OpenResourceFile((LPSTR)"Notepad.Res"))
    {
        pNotepadCls->hClsCursor = CursorLoad(hResFile, (LPSTR)"Arrow");
        pNotepadCls->hClsIconId = IconLoad(hResFile, (LPSTR)"Notepad");
        pNotepadCls->hMenu = MenuLoad(hResFile, (LPSTR)"mtMenuBar");
        pNotepadCls->lpClsName = (LPSTR)"Notepad";
        pNotepadCls->hBackBrush = GetStockObject(WHITE_BRUSH);
        pNotepadCls->clsWndCreate = NotepadCreate;
        pNotepadCls->clsWndDestroy = NotepadDestroy;
        pNotepadCls->clsWndIcon = NotepadIcon;
        pNotepadCls->clsWndInput = NotepadInput;
        pNotepadCls->clsWndScroll = NotepadScroll;
        pNotepadCls->clsWndSize = NotepadSize;
        if (!RegisterClass((LPWNDCLASS)pNotepadCls))
        {
            return FALSE;
        }
        Free((HANDLE)pNotepadCls);
        InitCtlMgr();
        RegisterCtlClass((LPSTR)"MEdit");
        lpParms->hWnd = CreateWindow("Notepad", "NOTEPAD.TXT", TRUE, FALSE, TRUE, NULL, 100, 0, hInstance);
        return TRUE;
    }
    return FALSE;
}

void FAR PASCAL NotepadFree(hInstance)
HANDLE hInstance;
{
    CloseResourceFile(hResFile);
}

Header file:

/* Notepad menu defs */

#define MI_OPEN 256
#define MI_SAVE 257
#define MI_SAVEAS 258
#define MI_CUT 259
#define MI_COPY 260
#define MI_PASTE 261
#define MI_CLEAR 262
#define MI_SELALL 263

Resource Script Reconstruction[edit | edit source]

Some resources must be manually decompiled. The .RES files can be extracted, but DR5's dialog format is different from the final dialog format, so there is no decompiler or documentation. You start with a dialog template and do trial and error, until you get something that compiles back to the exact same binary.

/*                                      */
/*    Notepad application               */
/*                                      */
/*   Copyright (C) 1984 Microsoft Inc.  */
/*                                      */

#include "notepad.h"

Arrow   CURSOR  ARROW.CUR
Notepad ICON    NOTEPAD.ICO

dtOpen  DIALOG 4 8 128 44
  CAPTION "Open file..."
  BEGIN
    PUSHBUTTON "Open", 1, 28, 20, 28, 12
    PUSHBUTTON "Cancel", 2, 72, 20, 28, 12
    EDITITEM 3 ,12 ,4 ,104 ,12 
  END

dtSaveChanges   DIALOG 4 8 128 44
  CAPTION "Save changes before closing?"
  BEGIN
    PUSHBUTTON "Yes", 1, 28, 4, 28, 12
    PUSHBUTTON "No", 2, 28, 20, 28, 12
    PUSHBUTTON "Cancel", 3, 72, 20, 28, 12
  END

dtSaveAs    DIALOG 4 8 128 44
  CAPTION "Save current file as..."
  BEGIN
    PUSHBUTTON "Save", 1, 28, 20, 28, 12
    PUSHBUTTON "Cancel", 2, 72, 20, 28, 12
    EDITITEM 3 ,12 ,4 ,104 ,12 
  END

dtCreate    DIALOG 4 8 160 44
  CAPTION "File not found: create new file?"
  BEGIN
    PUSHBUTTON "OK", 1, 28, 20, 28, 12
    PUSHBUTTON "Cancel", 2, 72, 20, 28, 12
  END

dtFileTooBig    DIALOG 4 8 160 28
  CAPTION "That file is too large"
  BEGIN
    PUSHBUTTON "Cancel", 1, 42, 4, 76, 12
  END

dtDiskError DIALOG 4 8 152 28
  CAPTION "Disk error"
  BEGIN
    PUSHBUTTON "Cancel", 1, 42, 4, 76, 12
  END

mtMenuBar   MENU
  BEGIN
    POPUP "File"
      BEGIN
        MENUITEM "Open...", MI_OPEN
        MENUITEM "Save", MI_SAVE
        MENUITEM "Save As...", MI_SAVEAS
      END
    POPUP "Edit"
      BEGIN
        MENUITEM "Cut", MI_CUT
        MENUITEM "Copy", MI_COPY
        MENUITEM "Paste", MI_PASTE
        MENUITEM "Clear", MI_CLEAR
        MENUITEM "Select All", MI_SELALL
      END
  END

Makefile Reconstruction[edit | edit source]

Makefiles are easy to reconstruct, just use the default template and change the filenames and you should be good to go.

notepad.dfo: notepad.def
    mc notepad

notepad.res: notepad.rc notepad.h arrow.cur notepad.ico
    rc notepad.rc

notepad.obj: notepad.c notepad.h
    cc -d -c -Asnw -Gsw -Oas -Zped -I c:\lib notepad.c

notepad.exe: notepad.dfo notepad.obj
    link notepad.dfo notepad,,/map/li,\lib\windows \lib\gdi \lib\kernel \lib\clip \lib\swlibc
    mapsym notepad
    ne notepad