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