// dfm2form.c - Convert Delphi 1.0 binary DFM (TPF0) to .form protocol text // // Usage: dfm2form [output.form] // // Reads a binary DFM file, extracts the form and control definitions, // and outputs protocol commands (FORM.CREATE, CTRL.CREATE, EVENT.BIND, // FORM.SHOW) suitable for the remote forms system. #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- typedef enum { ctUnknown = 0, ctLabel, ctEdit, ctButton, ctCheckBox, ctListBox, ctComboBox, ctMemo, ctImage, ctGroupBox, ctRadioButton, ctPanel, ctScrollBar, ctMediaPlayer, ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup, ctBitBtn, ctSpeedButton, ctTabSet, ctNotebook, ctTabbedNotebook, ctMaskEdit, ctOutline, ctBevel, ctHeader, ctScrollBox, ctStringGrid } CtrlTypeE; typedef struct { char name[64]; CtrlTypeE type; int32_t left; int32_t top; int32_t width; int32_t height; char caption[256]; char text[4096]; char items[4096]; int32_t checked; int32_t enabled; int32_t visible; int32_t maxLength; int32_t readOnly; int32_t scrollBars; int32_t tabOrder; int32_t itemIndex; bool hasCaption; bool hasText; bool hasItems; bool hasChecked; bool hasEnabled; bool hasVisible; bool hasMaxLength; bool hasReadOnly; bool hasScrollBars; bool hasTabOrder; bool hasItemIndex; bool hasOnClick; bool hasOnChange; bool hasOnDblClick; bool hasOnEnter; bool hasOnExit; bool hasOnKeyDown; bool hasOnKeyUp; bool hasOnMouseDown; bool hasOnMouseUp; int32_t stretch; int32_t center; int32_t transparent; int32_t bevelOuter; int32_t bevelInner; int32_t borderStyle; int32_t kind; int32_t min; int32_t max; int32_t position; int32_t largeChange; int32_t smallChange; int32_t autoOpen; char fileName[256]; char deviceType[64]; bool hasStretch; bool hasCenter; bool hasTransparent; bool hasBevelOuter; bool hasBevelInner; bool hasBorderStyle; bool hasKind; bool hasMin; bool hasMax; bool hasPosition; bool hasLargeChange; bool hasSmallChange; bool hasAutoOpen; bool hasFileName; bool hasDeviceType; int32_t parentCtrlIdx; int32_t columns; int32_t shortCut; int32_t layout; int32_t numGlyphs; int32_t groupIndex; int32_t down; int32_t allowAllUp; int32_t outlineStyle; int32_t shape; int32_t style; char editMask[256]; bool hasColumns; bool hasShortCut; bool hasLayout; bool hasNumGlyphs; bool hasGroupIndex; bool hasDown; bool hasAllowAllUp; bool hasOutlineStyle; bool hasShape; bool hasStyle; bool hasEditMask; int32_t colCount; int32_t rowCount; int32_t fixedCols; int32_t fixedRows; int32_t defaultColWidth; int32_t defaultRowHeight; int32_t options; bool hasColCount; bool hasRowCount; bool hasFixedCols; bool hasFixedRows; bool hasDefaultColWidth; bool hasDefaultRowHeight; bool hasOptions; bool hasOnSetEditText; } DfmCtrlT; typedef struct { char name[64]; int32_t width; int32_t height; char caption[256]; DfmCtrlT ctrls[256]; int32_t ctrlCount; } DfmFormT; // DFM value type tags enum { vaNull = 0x00, vaList = 0x01, vaInt8 = 0x02, vaInt16 = 0x03, vaInt32 = 0x04, vaExtended = 0x05, vaString = 0x06, vaIdent = 0x07, vaFalse = 0x08, vaTrue = 0x09, vaBinary = 0x0A, vaSet = 0x0B, vaLString = 0x0C, vaNil = 0x0D, vaCollection = 0x0E }; // --------------------------------------------------------------------------- // Prototypes // --------------------------------------------------------------------------- static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl); static void emitForm(FILE *out, int32_t formId, DfmFormT *form); static void escapeStr(const char *src, char *dst, int32_t dstSize); static void initCtrl(DfmCtrlT *ctrl); static void initForm(DfmFormT *form); static CtrlTypeE mapClassName(const char *className); static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, bool isRoot, int32_t parentIdx); static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, DfmCtrlT *ctrl, bool isForm); static int32_t readByte(const uint8_t *data, int32_t size, int32_t *pos); static int32_t readInt16LE(const uint8_t *data, int32_t size, int32_t *pos); static int32_t readInt32LE(const uint8_t *data, int32_t size, int32_t *pos); static int32_t readIntValue(const uint8_t *data, int32_t size, int32_t *pos, uint8_t tag); static bool readStr(const uint8_t *data, int32_t size, int32_t *pos, char *buf, int32_t bufSize); static void skipValue(const uint8_t *data, int32_t size, int32_t *pos, uint8_t tag); static int stricmp_local(const char *a, const char *b); static void usage(const char *progName); // --------------------------------------------------------------------------- // Low-level DFM readers // --------------------------------------------------------------------------- static int32_t readByte(const uint8_t *data, int32_t size, int32_t *pos) { if (*pos >= size) { fprintf(stderr, "Error: unexpected end of file at offset %d\n", *pos); exit(1); } return data[(*pos)++]; } static int32_t readInt16LE(const uint8_t *data, int32_t size, int32_t *pos) { if (*pos + 2 > size) { fprintf(stderr, "Error: unexpected end of file at offset %d\n", *pos); exit(1); } int16_t val = (int16_t)(data[*pos] | (data[*pos + 1] << 8)); *pos += 2; return val; } static int32_t readInt32LE(const uint8_t *data, int32_t size, int32_t *pos) { if (*pos + 4 > size) { fprintf(stderr, "Error: unexpected end of file at offset %d\n", *pos); exit(1); } int32_t val = (int32_t)(data[*pos] | (data[*pos + 1] << 8) | (data[*pos + 2] << 16) | (data[*pos + 3] << 24)); *pos += 4; return val; } static bool readStr(const uint8_t *data, int32_t size, int32_t *pos, char *buf, int32_t bufSize) { int32_t len = readByte(data, size, pos); if (*pos + len > size) { fprintf(stderr, "Error: string overflows file at offset %d\n", *pos); exit(1); } int32_t copyLen = (len < bufSize - 1) ? len : bufSize - 1; memcpy(buf, data + *pos, copyLen); buf[copyLen] = '\0'; *pos += len; return true; } static int32_t readIntValue(const uint8_t *data, int32_t size, int32_t *pos, uint8_t tag) { switch (tag) { case vaInt8: return (int8_t)readByte(data, size, pos); case vaInt16: return readInt16LE(data, size, pos); case vaInt32: return readInt32LE(data, size, pos); default: return 0; } } // --------------------------------------------------------------------------- // Skip unknown property values // --------------------------------------------------------------------------- static void skipValue(const uint8_t *data, int32_t size, int32_t *pos, uint8_t tag) { int32_t len; char buf[256]; switch (tag) { case vaNull: break; case vaList: // skip items until vaNull while (*pos < size) { uint8_t itemTag = readByte(data, size, pos); if (itemTag == vaNull) { break; } skipValue(data, size, pos, itemTag); } break; case vaInt8: *pos += 1; break; case vaInt16: *pos += 2; break; case vaInt32: *pos += 4; break; case vaExtended: *pos += 10; break; case vaString: len = readByte(data, size, pos); *pos += len; break; case vaIdent: len = readByte(data, size, pos); *pos += len; break; case vaFalse: case vaTrue: case vaNil: break; case vaBinary: len = readInt32LE(data, size, pos); *pos += len; break; case vaSet: // repeated strings, empty string terminates while (*pos < size) { readStr(data, size, pos, buf, sizeof(buf)); if (buf[0] == '\0') { break; } } break; case vaLString: len = readInt32LE(data, size, pos); *pos += len; break; case vaCollection: // items bracketed by vaList/vaNull while (*pos < size) { uint8_t itemTag = readByte(data, size, pos); if (itemTag == vaNull) { break; } skipValue(data, size, pos, itemTag); } break; default: fprintf(stderr, "Warning: unknown value tag 0x%02X at offset %d\n", tag, *pos); break; } } // --------------------------------------------------------------------------- // Case-insensitive string compare // --------------------------------------------------------------------------- static int stricmp_local(const char *a, const char *b) { while (*a && *b) { int ca = tolower((unsigned char)*a); int cb = tolower((unsigned char)*b); if (ca != cb) { return ca - cb; } a++; b++; } return (unsigned char)*a - (unsigned char)*b; } // --------------------------------------------------------------------------- // Map Delphi class name to control type // --------------------------------------------------------------------------- static CtrlTypeE mapClassName(const char *className) { if (stricmp_local(className, "TLabel") == 0) { return ctLabel; } if (stricmp_local(className, "TEdit") == 0) { return ctEdit; } if (stricmp_local(className, "TButton") == 0) { return ctButton; } if (stricmp_local(className, "TCheckBox") == 0) { return ctCheckBox; } if (stricmp_local(className, "TListBox") == 0) { return ctListBox; } if (stricmp_local(className, "TComboBox") == 0) { return ctComboBox; } if (stricmp_local(className, "TMemo") == 0) { return ctMemo; } if (stricmp_local(className, "TImage") == 0) { return ctImage; } if (stricmp_local(className, "TGroupBox") == 0) { return ctGroupBox; } if (stricmp_local(className, "TRadioButton") == 0) { return ctRadioButton; } if (stricmp_local(className, "TPanel") == 0) { return ctPanel; } if (stricmp_local(className, "TScrollBar") == 0) { return ctScrollBar; } if (stricmp_local(className, "TMediaPlayer") == 0) { return ctMediaPlayer; } if (stricmp_local(className, "TMainMenu") == 0) { return ctMainMenu; } if (stricmp_local(className, "TPopupMenu") == 0) { return ctPopupMenu; } if (stricmp_local(className, "TMenuItem") == 0) { return ctMenuItem; } if (stricmp_local(className, "TRadioGroup") == 0) { return ctRadioGroup; } if (stricmp_local(className, "TBitBtn") == 0) { return ctBitBtn; } if (stricmp_local(className, "TSpeedButton") == 0) { return ctSpeedButton; } if (stricmp_local(className, "TTabSet") == 0) { return ctTabSet; } if (stricmp_local(className, "TNotebook") == 0) { return ctNotebook; } if (stricmp_local(className, "TTabbedNotebook") == 0) { return ctTabbedNotebook; } if (stricmp_local(className, "TMaskEdit") == 0) { return ctMaskEdit; } if (stricmp_local(className, "TOutline") == 0) { return ctOutline; } if (stricmp_local(className, "TBevel") == 0) { return ctBevel; } if (stricmp_local(className, "THeader") == 0) { return ctHeader; } if (stricmp_local(className, "TScrollBox") == 0) { return ctScrollBox; } if (stricmp_local(className, "TStringGrid") == 0) { return ctStringGrid; } return ctUnknown; } // --------------------------------------------------------------------------- // Init helpers // --------------------------------------------------------------------------- static void initCtrl(DfmCtrlT *ctrl) { memset(ctrl, 0, sizeof(DfmCtrlT)); ctrl->enabled = 1; ctrl->visible = 1; ctrl->itemIndex = -1; ctrl->tabOrder = -1; ctrl->bevelOuter = 1; // bvRaised ctrl->largeChange = 1; ctrl->smallChange = 1; ctrl->parentCtrlIdx = -1; } static void initForm(DfmFormT *form) { memset(form, 0, sizeof(DfmFormT)); } // --------------------------------------------------------------------------- // Parse properties from DFM binary // --------------------------------------------------------------------------- static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, DfmCtrlT *ctrl, bool isForm) { char propName[64]; char strBuf[4096]; while (*pos < size) { // Property name (empty = end of properties) int32_t nameLen = readByte(data, size, pos); if (nameLen == 0) { break; } // Read the property name (*pos)--; // back up, readStr reads the length byte readStr(data, size, pos, propName, sizeof(propName)); // Read value tag uint8_t tag = readByte(data, size, pos); // Match known properties if (isForm && stricmp_local(propName, "Caption") == 0) { if (tag == vaString) { readStr(data, size, pos, form->caption, sizeof(form->caption)); } else if (tag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(form->caption) - 1) ? len : (int32_t)sizeof(form->caption) - 1; memcpy(form->caption, data + *pos, copyLen); form->caption[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, tag); } } else if (isForm && stricmp_local(propName, "ClientWidth") == 0) { form->width = readIntValue(data, size, pos, tag); } else if (isForm && stricmp_local(propName, "ClientHeight") == 0) { form->height = readIntValue(data, size, pos, tag); } else if (isForm && stricmp_local(propName, "Width") == 0 && form->width == 0) { form->width = readIntValue(data, size, pos, tag); } else if (isForm && stricmp_local(propName, "Height") == 0 && form->height == 0) { form->height = readIntValue(data, size, pos, tag); } else if (!isForm && stricmp_local(propName, "Left") == 0) { ctrl->left = readIntValue(data, size, pos, tag); } else if (!isForm && stricmp_local(propName, "Top") == 0) { ctrl->top = readIntValue(data, size, pos, tag); } else if (!isForm && stricmp_local(propName, "Width") == 0) { ctrl->width = readIntValue(data, size, pos, tag); } else if (!isForm && stricmp_local(propName, "Height") == 0) { ctrl->height = readIntValue(data, size, pos, tag); } else if (!isForm && stricmp_local(propName, "Caption") == 0) { if (tag == vaString) { readStr(data, size, pos, ctrl->caption, sizeof(ctrl->caption)); } else if (tag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(ctrl->caption) - 1) ? len : (int32_t)sizeof(ctrl->caption) - 1; memcpy(ctrl->caption, data + *pos, copyLen); ctrl->caption[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, tag); } ctrl->hasCaption = true; } else if (!isForm && stricmp_local(propName, "Text") == 0) { if (tag == vaString) { readStr(data, size, pos, ctrl->text, sizeof(ctrl->text)); } else if (tag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(ctrl->text) - 1) ? len : (int32_t)sizeof(ctrl->text) - 1; memcpy(ctrl->text, data + *pos, copyLen); ctrl->text[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, tag); } ctrl->hasText = true; } else if (!isForm && (stricmp_local(propName, "Items.Strings") == 0 || stricmp_local(propName, "Tabs.Strings") == 0 || stricmp_local(propName, "Lines.Strings") == 0 || stricmp_local(propName, "Pages.Strings") == 0 || stricmp_local(propName, "Sections.Strings") == 0) && tag == vaList) { // List of strings for ListBox/ComboBox ctrl->items[0] = '\0'; int32_t itemsLen = 0; while (*pos < size) { uint8_t itemTag = readByte(data, size, pos); if (itemTag == vaNull) { break; } if (itemTag == vaString) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else if (itemTag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(strBuf) - 1) ? len : (int32_t)sizeof(strBuf) - 1; memcpy(strBuf, data + *pos, copyLen); strBuf[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, itemTag); continue; } int32_t slen = (int32_t)strlen(strBuf); if (itemsLen + slen + 2 < (int32_t)sizeof(ctrl->items)) { if (itemsLen > 0) { ctrl->items[itemsLen++] = '\n'; } memcpy(ctrl->items + itemsLen, strBuf, slen); itemsLen += slen; ctrl->items[itemsLen] = '\0'; } } ctrl->hasItems = true; } else if (!isForm && stricmp_local(propName, "Checked") == 0) { if (tag == vaTrue) { ctrl->checked = 1; } else if (tag == vaFalse) { ctrl->checked = 0; } else { ctrl->checked = readIntValue(data, size, pos, tag); } ctrl->hasChecked = true; } else if (!isForm && stricmp_local(propName, "State") == 0) { // TCheckBox.State: cbUnchecked=0, cbChecked=1, cbGrayed=2 if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "cbChecked") == 0) { ctrl->checked = 1; } else { ctrl->checked = 0; } } else { ctrl->checked = readIntValue(data, size, pos, tag); } ctrl->hasChecked = true; } else if (!isForm && stricmp_local(propName, "Enabled") == 0) { if (tag == vaTrue) { ctrl->enabled = 1; } else if (tag == vaFalse) { ctrl->enabled = 0; } else { ctrl->enabled = readIntValue(data, size, pos, tag); } ctrl->hasEnabled = true; } else if (!isForm && stricmp_local(propName, "Visible") == 0) { if (tag == vaTrue) { ctrl->visible = 1; } else if (tag == vaFalse) { ctrl->visible = 0; } else { ctrl->visible = readIntValue(data, size, pos, tag); } ctrl->hasVisible = true; } else if (!isForm && stricmp_local(propName, "MaxLength") == 0) { ctrl->maxLength = readIntValue(data, size, pos, tag); ctrl->hasMaxLength = true; } else if (!isForm && stricmp_local(propName, "ReadOnly") == 0) { if (tag == vaTrue) { ctrl->readOnly = 1; } else if (tag == vaFalse) { ctrl->readOnly = 0; } else { ctrl->readOnly = readIntValue(data, size, pos, tag); } ctrl->hasReadOnly = true; } else if (!isForm && stricmp_local(propName, "ScrollBars") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "ssNone") == 0) { ctrl->scrollBars = 0; } else if (stricmp_local(strBuf, "ssHorizontal") == 0) { ctrl->scrollBars = 1; } else if (stricmp_local(strBuf, "ssVertical") == 0) { ctrl->scrollBars = 2; } else if (stricmp_local(strBuf, "ssBoth") == 0) { ctrl->scrollBars = 3; } else { ctrl->scrollBars = 0; } } else { ctrl->scrollBars = readIntValue(data, size, pos, tag); } ctrl->hasScrollBars = true; } else if (!isForm && stricmp_local(propName, "TabOrder") == 0) { ctrl->tabOrder = readIntValue(data, size, pos, tag); ctrl->hasTabOrder = true; } else if (!isForm && (stricmp_local(propName, "ItemIndex") == 0 || stricmp_local(propName, "TabIndex") == 0 || stricmp_local(propName, "PageIndex") == 0)) { ctrl->itemIndex = readIntValue(data, size, pos, tag); ctrl->hasItemIndex = true; } else if (stricmp_local(propName, "OnClick") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnClick = true; } } else if (stricmp_local(propName, "OnChange") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnChange = true; } } else if (stricmp_local(propName, "OnDblClick") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnDblClick = true; } } else if (stricmp_local(propName, "OnEnter") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnEnter = true; } } else if (stricmp_local(propName, "OnExit") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnExit = true; } } else if (stricmp_local(propName, "OnKeyDown") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnKeyDown = true; } } else if (stricmp_local(propName, "OnKeyUp") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnKeyUp = true; } } else if (stricmp_local(propName, "OnMouseDown") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnMouseDown = true; } } else if (stricmp_local(propName, "OnMouseUp") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnMouseUp = true; } } else if (!isForm && stricmp_local(propName, "Stretch") == 0) { if (tag == vaTrue) { ctrl->stretch = 1; } else if (tag == vaFalse) { ctrl->stretch = 0; } else { ctrl->stretch = readIntValue(data, size, pos, tag); } ctrl->hasStretch = true; } else if (!isForm && stricmp_local(propName, "Center") == 0) { if (tag == vaTrue) { ctrl->center = 1; } else if (tag == vaFalse) { ctrl->center = 0; } else { ctrl->center = readIntValue(data, size, pos, tag); } ctrl->hasCenter = true; } else if (!isForm && stricmp_local(propName, "Transparent") == 0) { if (tag == vaTrue) { ctrl->transparent = 1; } else if (tag == vaFalse) { ctrl->transparent = 0; } else { ctrl->transparent = readIntValue(data, size, pos, tag); } ctrl->hasTransparent = true; } else if (!isForm && stricmp_local(propName, "BevelOuter") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "bvNone") == 0) { ctrl->bevelOuter = 0; } else if (stricmp_local(strBuf, "bvLowered") == 0) { ctrl->bevelOuter = 1; } else if (stricmp_local(strBuf, "bvRaised") == 0) { ctrl->bevelOuter = 2; } else { ctrl->bevelOuter = 0; } } else { ctrl->bevelOuter = readIntValue(data, size, pos, tag); } ctrl->hasBevelOuter = true; } else if (!isForm && stricmp_local(propName, "BevelInner") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "bvNone") == 0) { ctrl->bevelInner = 0; } else if (stricmp_local(strBuf, "bvLowered") == 0) { ctrl->bevelInner = 1; } else if (stricmp_local(strBuf, "bvRaised") == 0) { ctrl->bevelInner = 2; } else { ctrl->bevelInner = 0; } } else { ctrl->bevelInner = readIntValue(data, size, pos, tag); } ctrl->hasBevelInner = true; } else if (!isForm && stricmp_local(propName, "BorderStyle") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "bsNone") == 0) { ctrl->borderStyle = 0; } else if (stricmp_local(strBuf, "bsSingle") == 0) { ctrl->borderStyle = 1; } else { ctrl->borderStyle = 0; } } else { ctrl->borderStyle = readIntValue(data, size, pos, tag); } ctrl->hasBorderStyle = true; } else if (!isForm && stricmp_local(propName, "Kind") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "sbHorizontal") == 0) { ctrl->kind = 0; } else if (stricmp_local(strBuf, "sbVertical") == 0) { ctrl->kind = 1; } else if (stricmp_local(strBuf, "bkCustom") == 0) { ctrl->kind = 0; } else if (stricmp_local(strBuf, "bkOK") == 0) { ctrl->kind = 1; } else if (stricmp_local(strBuf, "bkCancel") == 0) { ctrl->kind = 2; } else if (stricmp_local(strBuf, "bkHelp") == 0) { ctrl->kind = 3; } else if (stricmp_local(strBuf, "bkYes") == 0) { ctrl->kind = 4; } else if (stricmp_local(strBuf, "bkNo") == 0) { ctrl->kind = 5; } else if (stricmp_local(strBuf, "bkClose") == 0) { ctrl->kind = 6; } else if (stricmp_local(strBuf, "bkAbort") == 0) { ctrl->kind = 7; } else if (stricmp_local(strBuf, "bkRetry") == 0) { ctrl->kind = 8; } else if (stricmp_local(strBuf, "bkIgnore") == 0) { ctrl->kind = 9; } else if (stricmp_local(strBuf, "bkAll") == 0) { ctrl->kind = 10; } else { ctrl->kind = 0; } } else { ctrl->kind = readIntValue(data, size, pos, tag); } ctrl->hasKind = true; } else if (!isForm && stricmp_local(propName, "Min") == 0) { ctrl->min = readIntValue(data, size, pos, tag); ctrl->hasMin = true; } else if (!isForm && stricmp_local(propName, "Max") == 0) { ctrl->max = readIntValue(data, size, pos, tag); ctrl->hasMax = true; } else if (!isForm && stricmp_local(propName, "Position") == 0) { ctrl->position = readIntValue(data, size, pos, tag); ctrl->hasPosition = true; } else if (!isForm && stricmp_local(propName, "LargeChange") == 0) { ctrl->largeChange = readIntValue(data, size, pos, tag); ctrl->hasLargeChange = true; } else if (!isForm && stricmp_local(propName, "SmallChange") == 0) { ctrl->smallChange = readIntValue(data, size, pos, tag); ctrl->hasSmallChange = true; } else if (!isForm && stricmp_local(propName, "FileName") == 0) { if (tag == vaString) { readStr(data, size, pos, ctrl->fileName, sizeof(ctrl->fileName)); } else if (tag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(ctrl->fileName) - 1) ? len : (int32_t)sizeof(ctrl->fileName) - 1; memcpy(ctrl->fileName, data + *pos, copyLen); ctrl->fileName[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, tag); } ctrl->hasFileName = true; } else if (!isForm && stricmp_local(propName, "DeviceType") == 0) { if (tag == vaIdent) { readStr(data, size, pos, ctrl->deviceType, sizeof(ctrl->deviceType)); } else if (tag == vaString) { readStr(data, size, pos, ctrl->deviceType, sizeof(ctrl->deviceType)); } else if (tag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(ctrl->deviceType) - 1) ? len : (int32_t)sizeof(ctrl->deviceType) - 1; memcpy(ctrl->deviceType, data + *pos, copyLen); ctrl->deviceType[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, tag); } ctrl->hasDeviceType = true; } else if (!isForm && stricmp_local(propName, "AutoOpen") == 0) { if (tag == vaTrue) { ctrl->autoOpen = 1; } else if (tag == vaFalse) { ctrl->autoOpen = 0; } else { ctrl->autoOpen = readIntValue(data, size, pos, tag); } ctrl->hasAutoOpen = true; } else if (!isForm && stricmp_local(propName, "Columns") == 0) { ctrl->columns = readIntValue(data, size, pos, tag); ctrl->hasColumns = true; } else if (!isForm && stricmp_local(propName, "ShortCut") == 0) { ctrl->shortCut = readIntValue(data, size, pos, tag); ctrl->hasShortCut = true; } else if (!isForm && stricmp_local(propName, "Layout") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "blGlyphLeft") == 0) { ctrl->layout = 0; } else if (stricmp_local(strBuf, "blGlyphRight") == 0) { ctrl->layout = 1; } else if (stricmp_local(strBuf, "blGlyphTop") == 0) { ctrl->layout = 2; } else if (stricmp_local(strBuf, "blGlyphBottom") == 0) { ctrl->layout = 3; } else { ctrl->layout = 0; } } else { ctrl->layout = readIntValue(data, size, pos, tag); } ctrl->hasLayout = true; } else if (!isForm && stricmp_local(propName, "NumGlyphs") == 0) { ctrl->numGlyphs = readIntValue(data, size, pos, tag); ctrl->hasNumGlyphs = true; } else if (!isForm && stricmp_local(propName, "GroupIndex") == 0) { ctrl->groupIndex = readIntValue(data, size, pos, tag); ctrl->hasGroupIndex = true; } else if (!isForm && stricmp_local(propName, "Down") == 0) { if (tag == vaTrue) { ctrl->down = 1; } else if (tag == vaFalse) { ctrl->down = 0; } else { ctrl->down = readIntValue(data, size, pos, tag); } ctrl->hasDown = true; } else if (!isForm && stricmp_local(propName, "AllowAllUp") == 0) { if (tag == vaTrue) { ctrl->allowAllUp = 1; } else if (tag == vaFalse) { ctrl->allowAllUp = 0; } else { ctrl->allowAllUp = readIntValue(data, size, pos, tag); } ctrl->hasAllowAllUp = true; } else if (!isForm && stricmp_local(propName, "EditMask") == 0) { if (tag == vaString) { readStr(data, size, pos, ctrl->editMask, sizeof(ctrl->editMask)); } else if (tag == vaLString) { int32_t len = readInt32LE(data, size, pos); int32_t copyLen = (len < (int32_t)sizeof(ctrl->editMask) - 1) ? len : (int32_t)sizeof(ctrl->editMask) - 1; memcpy(ctrl->editMask, data + *pos, copyLen); ctrl->editMask[copyLen] = '\0'; *pos += len; } else { skipValue(data, size, pos, tag); } ctrl->hasEditMask = true; } else if (!isForm && stricmp_local(propName, "OutlineStyle") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "osText") == 0) { ctrl->outlineStyle = 0; } else if (stricmp_local(strBuf, "osPlusMinusText") == 0) { ctrl->outlineStyle = 1; } else if (stricmp_local(strBuf, "osPlusMinus") == 0) { ctrl->outlineStyle = 2; } else if (stricmp_local(strBuf, "osPictureText") == 0) { ctrl->outlineStyle = 3; } else if (stricmp_local(strBuf, "osPicturePlusMinusText") == 0) { ctrl->outlineStyle = 4; } else if (stricmp_local(strBuf, "osTreeText") == 0) { ctrl->outlineStyle = 5; } else if (stricmp_local(strBuf, "osTreePictureText") == 0) { ctrl->outlineStyle = 6; } else { ctrl->outlineStyle = 0; } } else { ctrl->outlineStyle = readIntValue(data, size, pos, tag); } ctrl->hasOutlineStyle = true; } else if (!isForm && stricmp_local(propName, "Shape") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "bsBox") == 0) { ctrl->shape = 0; } else if (stricmp_local(strBuf, "bsFrame") == 0) { ctrl->shape = 1; } else if (stricmp_local(strBuf, "bsTopLine") == 0) { ctrl->shape = 2; } else if (stricmp_local(strBuf, "bsBottomLine") == 0) { ctrl->shape = 3; } else if (stricmp_local(strBuf, "bsLeftLine") == 0) { ctrl->shape = 4; } else if (stricmp_local(strBuf, "bsRightLine") == 0) { ctrl->shape = 5; } else { ctrl->shape = 0; } } else { ctrl->shape = readIntValue(data, size, pos, tag); } ctrl->hasShape = true; } else if (!isForm && stricmp_local(propName, "Style") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (stricmp_local(strBuf, "bsLowered") == 0) { ctrl->style = 0; } else if (stricmp_local(strBuf, "bsRaised") == 0) { ctrl->style = 1; } else { ctrl->style = 0; } } else { ctrl->style = readIntValue(data, size, pos, tag); } ctrl->hasStyle = true; } else if (!isForm && stricmp_local(propName, "ColCount") == 0) { ctrl->colCount = readIntValue(data, size, pos, tag); ctrl->hasColCount = true; } else if (!isForm && stricmp_local(propName, "RowCount") == 0) { ctrl->rowCount = readIntValue(data, size, pos, tag); ctrl->hasRowCount = true; } else if (!isForm && stricmp_local(propName, "FixedCols") == 0) { ctrl->fixedCols = readIntValue(data, size, pos, tag); ctrl->hasFixedCols = true; } else if (!isForm && stricmp_local(propName, "FixedRows") == 0) { ctrl->fixedRows = readIntValue(data, size, pos, tag); ctrl->hasFixedRows = true; } else if (!isForm && stricmp_local(propName, "DefaultColWidth") == 0) { ctrl->defaultColWidth = readIntValue(data, size, pos, tag); ctrl->hasDefaultColWidth = true; } else if (!isForm && stricmp_local(propName, "DefaultRowHeight") == 0) { ctrl->defaultRowHeight = readIntValue(data, size, pos, tag); ctrl->hasDefaultRowHeight = true; } else if (!isForm && stricmp_local(propName, "Options") == 0) { if (tag == vaSet) { ctrl->options = 0; while (*pos < size) { readStr(data, size, pos, strBuf, sizeof(strBuf)); if (strBuf[0] == '\0') { break; } if (stricmp_local(strBuf, "goFixedVertLine") == 0) { ctrl->options |= 0x0001; } else if (stricmp_local(strBuf, "goFixedHorzLine") == 0) { ctrl->options |= 0x0002; } else if (stricmp_local(strBuf, "goVertLine") == 0) { ctrl->options |= 0x0004; } else if (stricmp_local(strBuf, "goHorzLine") == 0) { ctrl->options |= 0x0008; } else if (stricmp_local(strBuf, "goRangeSelect") == 0) { ctrl->options |= 0x0010; } else if (stricmp_local(strBuf, "goDrawFocusSelected") == 0) { ctrl->options |= 0x0020; } else if (stricmp_local(strBuf, "goRowSizing") == 0) { ctrl->options |= 0x0040; } else if (stricmp_local(strBuf, "goColSizing") == 0) { ctrl->options |= 0x0080; } else if (stricmp_local(strBuf, "goRowMoving") == 0) { ctrl->options |= 0x0100; } else if (stricmp_local(strBuf, "goColMoving") == 0) { ctrl->options |= 0x0200; } else if (stricmp_local(strBuf, "goEditing") == 0) { ctrl->options |= 0x0400; } else if (stricmp_local(strBuf, "goTabs") == 0) { ctrl->options |= 0x0800; } else if (stricmp_local(strBuf, "goThumbTracking") == 0) { ctrl->options |= 0x1000; } } } else { ctrl->options = readIntValue(data, size, pos, tag); } ctrl->hasOptions = true; } else if (stricmp_local(propName, "OnSetEditText") == 0) { if (tag == vaIdent) { readStr(data, size, pos, strBuf, sizeof(strBuf)); } else { skipValue(data, size, pos, tag); } if (!isForm) { ctrl->hasOnSetEditText = true; } } else { skipValue(data, size, pos, tag); } } } // --------------------------------------------------------------------------- // Parse a component (form or child control) recursively // --------------------------------------------------------------------------- static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, bool isRoot, int32_t parentIdx) { // Check for flags byte (Delphi 1.0 rarely uses this but handle it) uint8_t peek = data[*pos]; if ((peek & 0xF0) == 0xF0) { uint8_t flags = readByte(data, size, pos); if (flags & 0x02) { // ffChildPos: skip tagged integer uint8_t childTag = readByte(data, size, pos); readIntValue(data, size, pos, childTag); } } // Class name char className[64]; readStr(data, size, pos, className, sizeof(className)); // Instance name char instName[64]; readStr(data, size, pos, instName, sizeof(instName)); if (isRoot) { // This is the form itself snprintf(form->name, sizeof(form->name), "%s", instName); parseProperties(data, size, pos, form, NULL, true); // Parse child components while (*pos < size) { peek = data[*pos]; if (peek == 0x00) { (*pos)++; // consume terminator break; } parseComponent(data, size, pos, form, false, -1); } } else { // Child control CtrlTypeE type = mapClassName(className); if (type == ctUnknown) { fprintf(stderr, "Warning: unknown control class '%s' (%s), skipping\n", className, instName); // Still need to parse properties and children to advance pos DfmCtrlT dummy; initCtrl(&dummy); parseProperties(data, size, pos, form, &dummy, false); // Skip nested children while (*pos < size) { peek = data[*pos]; if (peek == 0x00) { (*pos)++; break; } parseComponent(data, size, pos, form, false, -1); } return true; } if (form->ctrlCount >= 256) { fprintf(stderr, "Error: too many controls (max 256)\n"); exit(1); } int32_t myIdx = form->ctrlCount; DfmCtrlT *ctrl = &form->ctrls[myIdx]; initCtrl(ctrl); ctrl->type = type; ctrl->parentCtrlIdx = parentIdx; snprintf(ctrl->name, sizeof(ctrl->name), "%s", instName); parseProperties(data, size, pos, form, ctrl, false); form->ctrlCount++; // Recurse into children (menu items, panels, etc.) while (*pos < size) { peek = data[*pos]; if (peek == 0x00) { (*pos)++; break; } parseComponent(data, size, pos, form, false, myIdx); } } return true; } // --------------------------------------------------------------------------- // Escape a string for protocol output // --------------------------------------------------------------------------- static void escapeStr(const char *src, char *dst, int32_t dstSize) { int32_t di = 0; for (int32_t si = 0; src[si] != '\0' && di < dstSize - 2; si++) { char c = src[si]; if (c == '"') { if (di + 2 >= dstSize - 1) { break; } dst[di++] = '\\'; dst[di++] = '"'; } else if (c == '\\') { if (di + 2 >= dstSize - 1) { break; } dst[di++] = '\\'; dst[di++] = '\\'; } else if (c == '\n') { if (di + 2 >= dstSize - 1) { break; } dst[di++] = '\\'; dst[di++] = 'n'; } else if (c == '\r') { if (di + 2 >= dstSize - 1) { break; } dst[di++] = '\\'; dst[di++] = 'r'; } else if (c == '\t') { if (di + 2 >= dstSize - 1) { break; } dst[di++] = '\\'; dst[di++] = 't'; } else { dst[di++] = c; } } dst[di] = '\0'; } // --------------------------------------------------------------------------- // Emit protocol commands for a single control // --------------------------------------------------------------------------- static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl) { static const char *typeNames[] = { "Unknown", "Label", "Edit", "Button", "CheckBox", "ListBox", "ComboBox", "Memo", "Image", "GroupBox", "RadioButton", "Panel", "ScrollBar", "MediaPlayer", "MainMenu", "PopupMenu", "MenuItem", "RadioGroup", "BitBtn", "SpeedButton", "TabSet", "Notebook", "TabbedNotebook", "MaskEdit", "Outline", "Bevel", "Header", "ScrollBox", "StringGrid" }; char escaped[8192]; if (ctrl->type == ctMainMenu || ctrl->type == ctPopupMenu || ctrl->type == ctMenuItem) { fprintf(out, "CTRL.CREATE %d %d %s 0 0 0 0", formId, ctrlId, typeNames[ctrl->type]); } else { fprintf(out, "CTRL.CREATE %d %d %s %d %d %d %d", formId, ctrlId, typeNames[ctrl->type], ctrl->left, ctrl->top, ctrl->width, ctrl->height); } // Inline properties if (ctrl->hasCaption) { escapeStr(ctrl->caption, escaped, sizeof(escaped)); fprintf(out, " Caption=\"%s\"", escaped); } if (ctrl->hasText) { escapeStr(ctrl->text, escaped, sizeof(escaped)); fprintf(out, " Text=\"%s\"", escaped); } if (ctrl->hasItems) { escapeStr(ctrl->items, escaped, sizeof(escaped)); fprintf(out, " Items=\"%s\"", escaped); } if (ctrl->hasChecked) { fprintf(out, " Checked=%d", ctrl->checked); } if (ctrl->hasEnabled && ctrl->enabled == 0) { fprintf(out, " Enabled=0"); } if (ctrl->hasVisible && ctrl->visible == 0) { fprintf(out, " Visible=0"); } if (ctrl->hasMaxLength && ctrl->maxLength > 0) { fprintf(out, " MaxLength=%d", ctrl->maxLength); } if (ctrl->hasReadOnly && ctrl->readOnly) { fprintf(out, " ReadOnly=1"); } if (ctrl->hasScrollBars && ctrl->scrollBars != 0) { fprintf(out, " ScrollBars=%d", ctrl->scrollBars); } if (ctrl->hasTabOrder && ctrl->tabOrder >= 0) { fprintf(out, " TabOrder=%d", ctrl->tabOrder); } if (ctrl->hasItemIndex && ctrl->itemIndex >= 0) { fprintf(out, " ItemIndex=%d", ctrl->itemIndex); } if (ctrl->hasStretch && ctrl->stretch) { fprintf(out, " Stretch=1"); } if (ctrl->hasCenter && ctrl->center) { fprintf(out, " Center=1"); } if (ctrl->hasTransparent && ctrl->transparent) { fprintf(out, " Transparent=1"); } if (ctrl->hasBevelOuter) { fprintf(out, " BevelOuter=%d", ctrl->bevelOuter); } if (ctrl->hasBevelInner && ctrl->bevelInner != 0) { fprintf(out, " BevelInner=%d", ctrl->bevelInner); } if (ctrl->hasBorderStyle && ctrl->borderStyle != 0) { fprintf(out, " BorderStyle=%d", ctrl->borderStyle); } if (ctrl->hasKind && ctrl->kind != 0) { fprintf(out, " Kind=%d", ctrl->kind); } if (ctrl->hasMin) { fprintf(out, " Min=%d", ctrl->min); } if (ctrl->hasMax) { fprintf(out, " Max=%d", ctrl->max); } if (ctrl->hasPosition && ctrl->position != 0) { fprintf(out, " Position=%d", ctrl->position); } if (ctrl->hasLargeChange && ctrl->largeChange != 1) { fprintf(out, " LargeChange=%d", ctrl->largeChange); } if (ctrl->hasSmallChange && ctrl->smallChange != 1) { fprintf(out, " SmallChange=%d", ctrl->smallChange); } if (ctrl->hasFileName) { escapeStr(ctrl->fileName, escaped, sizeof(escaped)); fprintf(out, " FileName=\"%s\"", escaped); } if (ctrl->hasDeviceType) { escapeStr(ctrl->deviceType, escaped, sizeof(escaped)); fprintf(out, " DeviceType=\"%s\"", escaped); } if (ctrl->hasAutoOpen && ctrl->autoOpen) { fprintf(out, " AutoOpen=1"); } if (ctrl->type == ctMenuItem && ctrl->parentCtrlIdx >= 0) { fprintf(out, " Parent=%d", ctrl->parentCtrlIdx + 1); } if (ctrl->hasColumns && ctrl->columns != 0) { fprintf(out, " Columns=%d", ctrl->columns); } if (ctrl->hasShortCut && ctrl->shortCut != 0) { fprintf(out, " ShortCut=%d", ctrl->shortCut); } if (ctrl->hasLayout && ctrl->layout != 0) { fprintf(out, " Layout=%d", ctrl->layout); } if (ctrl->hasNumGlyphs && ctrl->numGlyphs > 1) { fprintf(out, " NumGlyphs=%d", ctrl->numGlyphs); } if (ctrl->hasGroupIndex && ctrl->groupIndex != 0) { fprintf(out, " GroupIndex=%d", ctrl->groupIndex); } if (ctrl->hasDown && ctrl->down) { fprintf(out, " Down=1"); } if (ctrl->hasAllowAllUp && ctrl->allowAllUp) { fprintf(out, " AllowAllUp=1"); } if (ctrl->hasEditMask && ctrl->editMask[0] != '\0') { escapeStr(ctrl->editMask, escaped, sizeof(escaped)); fprintf(out, " EditMask=\"%s\"", escaped); } if (ctrl->hasOutlineStyle && ctrl->outlineStyle != 0) { fprintf(out, " OutlineStyle=%d", ctrl->outlineStyle); } if (ctrl->hasShape) { fprintf(out, " Shape=%d", ctrl->shape); } if (ctrl->hasStyle && ctrl->style != 0) { fprintf(out, " Style=%d", ctrl->style); } if (ctrl->hasColCount) { fprintf(out, " ColCount=%d", ctrl->colCount); } if (ctrl->hasRowCount) { fprintf(out, " RowCount=%d", ctrl->rowCount); } if (ctrl->hasFixedCols) { fprintf(out, " FixedCols=%d", ctrl->fixedCols); } if (ctrl->hasFixedRows) { fprintf(out, " FixedRows=%d", ctrl->fixedRows); } if (ctrl->hasDefaultColWidth) { fprintf(out, " DefaultColWidth=%d", ctrl->defaultColWidth); } if (ctrl->hasDefaultRowHeight) { fprintf(out, " DefaultRowHeight=%d", ctrl->defaultRowHeight); } if (ctrl->hasOptions) { fprintf(out, " Options=%d", ctrl->options); } fprintf(out, "\n"); // Emit EVENT.BIND for non-auto-wired events // Auto-wired: Button/CheckBox→Click, Edit→Change, ListBox→Select, // ComboBox→Select+Change, Memo→Change bool autoClick = (ctrl->type == ctButton || ctrl->type == ctCheckBox || ctrl->type == ctRadioButton || ctrl->type == ctMenuItem || ctrl->type == ctRadioGroup || ctrl->type == ctBitBtn || ctrl->type == ctSpeedButton); bool autoChange = (ctrl->type == ctEdit || ctrl->type == ctComboBox || ctrl->type == ctMemo || ctrl->type == ctScrollBar || ctrl->type == ctTabSet || ctrl->type == ctTabbedNotebook || ctrl->type == ctMaskEdit); bool autoSelect = (ctrl->type == ctListBox || ctrl->type == ctComboBox); if (ctrl->hasOnClick && !autoClick) { fprintf(out, "EVENT.BIND %d %d Click\n", formId, ctrlId); } if (ctrl->hasOnChange && !autoChange) { fprintf(out, "EVENT.BIND %d %d Change\n", formId, ctrlId); } if (ctrl->hasOnDblClick) { fprintf(out, "EVENT.BIND %d %d DblClick\n", formId, ctrlId); } if (ctrl->hasOnEnter) { fprintf(out, "EVENT.BIND %d %d Enter\n", formId, ctrlId); } if (ctrl->hasOnExit) { fprintf(out, "EVENT.BIND %d %d Exit\n", formId, ctrlId); } if (ctrl->hasOnKeyDown) { fprintf(out, "EVENT.BIND %d %d KeyDown\n", formId, ctrlId); } if (ctrl->hasOnKeyUp) { fprintf(out, "EVENT.BIND %d %d KeyUp\n", formId, ctrlId); } if (ctrl->hasOnMouseDown) { fprintf(out, "EVENT.BIND %d %d MouseDown\n", formId, ctrlId); } if (ctrl->hasOnMouseUp) { fprintf(out, "EVENT.BIND %d %d MouseUp\n", formId, ctrlId); } if (ctrl->hasOnSetEditText) { fprintf(out, "EVENT.BIND %d %d SetEditText\n", formId, ctrlId); } // Suppress unused variable warning for autoSelect (void)autoSelect; } // --------------------------------------------------------------------------- // Emit the complete form // --------------------------------------------------------------------------- static void emitForm(FILE *out, int32_t formId, DfmFormT *form) { char escaped[512]; escapeStr(form->caption, escaped, sizeof(escaped)); fprintf(out, "FORM.CREATE %d %d %d \"%s\"\n", formId, form->width, form->height, escaped); for (int32_t i = 0; i < form->ctrlCount; i++) { emitCtrl(out, formId, i + 1, &form->ctrls[i]); } fprintf(out, "FORM.SHOW %d\n", formId); } // --------------------------------------------------------------------------- // Usage // --------------------------------------------------------------------------- static void usage(const char *progName) { fprintf(stderr, "Usage: %s [output.form]\n", progName); exit(1); } // --------------------------------------------------------------------------- // Main // --------------------------------------------------------------------------- int main(int argc, char *argv[]) { const char *inputPath = NULL; const char *outputPath = NULL; // Parse arguments int32_t i = 1; while (i < argc) { if (argv[i][0] == '-') { usage(argv[0]); } else if (inputPath == NULL) { inputPath = argv[i]; } else if (outputPath == NULL) { outputPath = argv[i]; } else { usage(argv[0]); } i++; } if (inputPath == NULL) { usage(argv[0]); } // Read input file FILE *fin = fopen(inputPath, "rb"); if (fin == NULL) { fprintf(stderr, "Error: cannot open '%s': %s\n", inputPath, strerror(errno)); exit(1); } fseek(fin, 0, SEEK_END); long fileSize = ftell(fin); fseek(fin, 0, SEEK_SET); uint8_t *data = (uint8_t *)malloc(fileSize); if (data == NULL) { fprintf(stderr, "Error: out of memory\n"); fclose(fin); exit(1); } if ((long)fread(data, 1, fileSize, fin) != fileSize) { fprintf(stderr, "Error: failed to read '%s'\n", inputPath); free(data); fclose(fin); exit(1); } fclose(fin); // Verify TPF0 signature if (fileSize < 4 || memcmp(data, "TPF0", 4) != 0) { fprintf(stderr, "Error: '%s' is not a Delphi binary DFM (missing TPF0 signature)\n", inputPath); free(data); exit(1); } // Parse DfmFormT form; initForm(&form); int32_t pos = 4; // skip TPF0 parseComponent(data, (int32_t)fileSize, &pos, &form, true, -1); // Default caption if empty if (form.caption[0] == '\0') { snprintf(form.caption, sizeof(form.caption), "%s", form.name); } // Output FILE *fout; if (outputPath != NULL) { fout = fopen(outputPath, "w"); if (fout == NULL) { fprintf(stderr, "Error: cannot create '%s': %s\n", outputPath, strerror(errno)); free(data); exit(1); } } else { fout = stdout; } emitForm(fout, 0, &form); if (fout != stdout) { fclose(fout); } free(data); return 0; }