Load Windows 3.x .FON (NE resource containers) and raw .FNT font files for use with ExtTextOut. Multiple fonts can be active simultaneously. All available .FON files contain v2 fonts but VBESVGA.DRV requires v3 in 386 protected mode, so the loader converts on load. - Add NE resource table structures (NeResourceTypeT, NeResourceEntryT) - Add WdrvFontT opaque type with load/unload API - Implement buildFontFromFnt() v2→v3 converter - Implement wdrvLoadFontFon() NE resource parser - Move font from per-driver to global singleton (wdrvLoadFontBuiltin) - Add WdrvFontT parameter to wdrvExtTextOut (NULL = built-in) - Add Demo 6: Courier, Sans Serif, System fonts side by side - Copy fon/*.FON to bin/ during build Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
255 lines
11 KiB
C
255 lines
11 KiB
C
#ifndef NEFORMAT_H
|
|
#define NEFORMAT_H
|
|
|
|
// ============================================================================
|
|
// neformat.h - NE (New Executable) binary format structures
|
|
//
|
|
// Defines the on-disk structures for the NE executable format used by
|
|
// Windows 3.x 16-bit DLLs and drivers. An NE file has a DOS MZ stub
|
|
// header at offset 0, with a pointer at offset 0x3C to the NE header.
|
|
//
|
|
// The NE header contains:
|
|
// - Module metadata (flags, target OS, expected Windows version)
|
|
// - Offsets to the segment table, entry table, name tables, etc.
|
|
// - Auto-data segment index (DGROUP) and entry point CS:IP
|
|
//
|
|
// Each segment table entry describes one code or data segment with its
|
|
// file offset, size, flags, and minimum allocation size. Relocation
|
|
// records follow each segment's data in the file and describe fixups
|
|
// for inter-segment references, imported functions, and OS fixups.
|
|
//
|
|
// Also defines the standard DDI (Device Driver Interface) ordinal
|
|
// numbers exported by Windows 3.x display drivers.
|
|
// ============================================================================
|
|
|
|
#include <stdint.h>
|
|
|
|
// ============================================================================
|
|
// MZ (DOS) executable header - precedes the NE header
|
|
// ============================================================================
|
|
|
|
#define MZ_SIGNATURE 0x5A4D // 'MZ'
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t signature; // 0x00: 'MZ'
|
|
uint16_t lastPageBytes; // 0x02: bytes on last page
|
|
uint16_t pageCount; // 0x04: pages in file (512 bytes each)
|
|
uint16_t relocationCount; // 0x06: relocation entries
|
|
uint16_t headerParagraphs;// 0x08: header size in paragraphs
|
|
uint16_t minAlloc; // 0x0A: minimum extra paragraphs
|
|
uint16_t maxAlloc; // 0x0C: maximum extra paragraphs
|
|
uint16_t initSS; // 0x0E: initial SS
|
|
uint16_t initSP; // 0x10: initial SP
|
|
uint16_t checksum; // 0x12: checksum
|
|
uint16_t initIP; // 0x14: initial IP
|
|
uint16_t initCS; // 0x16: initial CS
|
|
uint16_t relocationOff; // 0x18: relocation table offset
|
|
uint16_t overlayNum; // 0x1A: overlay number
|
|
uint16_t reserved1[4]; // 0x1C: reserved
|
|
uint16_t oemId; // 0x24: OEM identifier
|
|
uint16_t oemInfo; // 0x26: OEM information
|
|
uint16_t reserved2[10]; // 0x28: reserved
|
|
uint32_t neHeaderOffset; // 0x3C: offset to NE header
|
|
} MzHeaderT;
|
|
|
|
// ============================================================================
|
|
// NE (New Executable) header
|
|
// ============================================================================
|
|
|
|
#define NE_SIGNATURE 0x454E // 'NE'
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t signature; // 0x00: 'NE'
|
|
uint8_t linkerMajor; // 0x02: linker version
|
|
uint8_t linkerMinor; // 0x03: linker revision
|
|
uint16_t entryTableOffset; // 0x04: offset to entry table (from NE header)
|
|
uint16_t entryTableSize; // 0x06: size of entry table
|
|
uint32_t fileCrc; // 0x08: file CRC
|
|
uint16_t moduleFlags; // 0x0C: module flags
|
|
uint16_t autoDataSegIndex; // 0x0E: auto data segment index (1-based)
|
|
uint16_t initialHeapSize; // 0x10: initial heap size
|
|
uint16_t initialStackSize; // 0x12: initial stack size
|
|
uint16_t entryPointIP; // 0x14: CS:IP entry point (IP)
|
|
uint16_t entryPointCS; // 0x16: CS:IP entry point (CS segment index)
|
|
uint16_t initialSP; // 0x18: SS:SP initial stack (SP)
|
|
uint16_t initialSS; // 0x1A: SS:SP initial stack (SS segment index)
|
|
uint16_t segmentCount; // 0x1C: number of segment table entries
|
|
uint16_t moduleRefCount; // 0x1E: number of module reference table entries
|
|
uint16_t nonResNameSize; // 0x20: size of non-resident name table
|
|
uint16_t segmentTableOffset; // 0x22: offset to segment table (from NE header)
|
|
uint16_t resourceTableOffset; // 0x24: offset to resource table (from NE header)
|
|
uint16_t resNameTableOffset; // 0x26: offset to resident name table (from NE)
|
|
uint16_t modRefTableOffset; // 0x28: offset to module reference table (from NE)
|
|
uint16_t importNameTableOffset; // 0x2A: offset to imported names table (from NE)
|
|
uint32_t nonResNameTableFileOffset; // 0x2C: file offset of non-resident name table
|
|
uint16_t movableEntryCount; // 0x30: number of movable entry points
|
|
uint16_t sectorAlignShift; // 0x32: sector alignment shift count
|
|
uint16_t resourceSegCount; // 0x34: number of resource segments
|
|
uint8_t targetOS; // 0x36: target operating system
|
|
uint8_t otherFlags; // 0x37: additional flags
|
|
uint16_t gangLoadAreaOffset; // 0x38: offset to gang-load area
|
|
uint16_t gangLoadAreaSize; // 0x3A: size of gang-load area
|
|
uint16_t swapAreaSize; // 0x3C: minimum code swap area size
|
|
uint16_t expectedWinVer; // 0x3E: expected Windows version
|
|
} NeHeaderT;
|
|
|
|
// NE module flags (moduleFlags field)
|
|
#define NE_FFLAGS_SINGLEDATA 0x0001 // Single shared DGROUP
|
|
#define NE_FFLAGS_MULTIPLEDATA 0x0002 // Multiple DGROUP (DLL with per-instance data)
|
|
#define NE_FFLAGS_GLOBALINIT 0x0004 // Global initialization
|
|
#define NE_FFLAGS_PROTMODE 0x0008 // Protected mode only
|
|
#define NE_FFLAGS_8086 0x0010 // 8086 instructions
|
|
#define NE_FFLAGS_80286 0x0020 // 80286 instructions
|
|
#define NE_FFLAGS_80386 0x0040 // 80386 instructions
|
|
#define NE_FFLAGS_80x87 0x0080 // uses 80x87
|
|
#define NE_FFLAGS_FULLSCREEN 0x0100 // full-screen application (not a DLL)
|
|
#define NE_FFLAGS_DLL 0x8000 // DLL or driver (not a task)
|
|
|
|
// NE target OS values
|
|
#define NE_OS_UNKNOWN 0x00
|
|
#define NE_OS_OS2 0x01
|
|
#define NE_OS_WINDOWS 0x02
|
|
#define NE_OS_DOS4 0x03
|
|
#define NE_OS_WIN386 0x04
|
|
|
|
// ============================================================================
|
|
// NE segment table entry
|
|
// ============================================================================
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t fileSectorOffset; // Logical sector offset in file (0 = no data)
|
|
uint16_t fileLength; // Length of segment in file (0 = 64K)
|
|
uint16_t flags; // Segment flags
|
|
uint16_t minAllocSize; // Minimum allocation size (0 = 64K)
|
|
} NeSegEntryT;
|
|
|
|
// Segment flags
|
|
#define NE_SEGF_DATA 0x0001 // Data segment (0 = code)
|
|
#define NE_SEGF_ALLOCATED 0x0002 // Loader has allocated memory
|
|
#define NE_SEGF_LOADED 0x0004 // Segment is loaded
|
|
#define NE_SEGF_MOVEABLE 0x0010 // Moveable segment
|
|
#define NE_SEGF_SHAREABLE 0x0020 // Shareable segment
|
|
#define NE_SEGF_PRELOAD 0x0040 // Preload segment
|
|
#define NE_SEGF_READONLY 0x0080 // Read-only (code) or execute-only (data)
|
|
#define NE_SEGF_HASRELOC 0x0100 // Has relocation data
|
|
#define NE_SEGF_DISCARD 0x1000 // Discardable
|
|
|
|
// ============================================================================
|
|
// NE relocation record
|
|
// ============================================================================
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint8_t srcType; // Source (fixup) type
|
|
uint8_t flags; // Relocation flags
|
|
uint16_t srcOffset; // Offset within segment of the fixup location
|
|
uint16_t target1; // Module index (1-based) or segment number
|
|
uint16_t target2; // Ordinal/offset or offset within segment
|
|
} NeRelocT;
|
|
|
|
// Relocation source types (srcType field)
|
|
#define NE_RELOC_LOBYTE 0x00 // Low byte fixup
|
|
#define NE_RELOC_SEGMENT 0x02 // 16-bit segment fixup
|
|
#define NE_RELOC_FAR_ADDR 0x03 // 32-bit far pointer (seg:off) fixup
|
|
#define NE_RELOC_OFFSET 0x05 // 16-bit offset fixup
|
|
#define NE_RELOC_FAR48_ADDR 0x0B // 48-bit far pointer fixup
|
|
#define NE_RELOC_OFFSET32 0x0D // 32-bit offset fixup
|
|
|
|
// Relocation target flags (flags field)
|
|
#define NE_RELF_INTERNALREF 0x00 // Internal reference
|
|
#define NE_RELF_IMPORTORD 0x01 // Import by ordinal
|
|
#define NE_RELF_IMPORTNAME 0x02 // Import by name
|
|
#define NE_RELF_OSFIXUP 0x03 // OS fixup
|
|
#define NE_RELF_TARGET_MASK 0x03 // Mask for target type
|
|
#define NE_RELF_ADDITIVE 0x04 // Additive fixup (don't zero target first)
|
|
|
|
// ============================================================================
|
|
// NE entry table structures
|
|
// ============================================================================
|
|
|
|
// Entry table is a series of bundles. Each bundle starts with:
|
|
// BYTE count - number of entries in this bundle (0 = end of table)
|
|
// BYTE indicator - 0x00 = empty, 0xFF = moveable, else fixed segment number
|
|
|
|
// Fixed entry (indicator = segment number 1-254)
|
|
typedef struct __attribute__((packed)) {
|
|
uint8_t flags; // Entry flags
|
|
uint16_t offset; // Offset within segment
|
|
} NeFixedEntryT;
|
|
|
|
// Moveable entry (indicator = 0xFF)
|
|
typedef struct __attribute__((packed)) {
|
|
uint8_t flags; // Entry flags
|
|
uint16_t int3fh; // INT 3Fh instruction (0xCD3F)
|
|
uint8_t segIndex; // Segment number (1-based)
|
|
uint16_t offset; // Offset within segment
|
|
} NeMoveableEntryT;
|
|
|
|
// Entry flags
|
|
#define NE_ENTRY_EXPORTED 0x01 // Entry is exported
|
|
#define NE_ENTRY_SHDATA 0x02 // Entry uses shared data segment
|
|
|
|
// ============================================================================
|
|
// NE resource table structures
|
|
// ============================================================================
|
|
|
|
#define RT_FONT 8
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t fileOffset; // shifted by rscAlignShift
|
|
uint16_t length; // shifted by rscAlignShift
|
|
uint16_t flags;
|
|
uint16_t resourceId; // high bit set = integer ID
|
|
uint32_t reserved;
|
|
} NeResourceEntryT; // 12 bytes
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint16_t typeId; // 0x8000|type = integer type, 0 = end
|
|
uint16_t count;
|
|
uint32_t reserved;
|
|
} NeResourceTypeT; // 8 bytes
|
|
|
|
// ============================================================================
|
|
// Display driver ordinal numbers (standard DDI exports)
|
|
// ============================================================================
|
|
|
|
#define DDI_ORD_BITBLT 1
|
|
#define DDI_ORD_COLORINFO 2
|
|
#define DDI_ORD_CONTROL 3
|
|
#define DDI_ORD_DISABLE 4
|
|
#define DDI_ORD_ENABLE 5
|
|
#define DDI_ORD_ENUMDFFONTS 6
|
|
#define DDI_ORD_ENUMOBJ 7
|
|
#define DDI_ORD_OUTPUT 8
|
|
#define DDI_ORD_PIXEL 9
|
|
#define DDI_ORD_REALIZEOBJECT 10
|
|
#define DDI_ORD_STRBLT 11
|
|
#define DDI_ORD_SCANLR 12
|
|
#define DDI_ORD_DEVICEMODE 13
|
|
#define DDI_ORD_EXTTEXTOUT 14
|
|
#define DDI_ORD_GETCHARWIDTH 15
|
|
#define DDI_ORD_DEVICEBITMAP 16
|
|
#define DDI_ORD_FASTBORDER 17
|
|
#define DDI_ORD_SETATTRIBUTE 18
|
|
#define DDI_ORD_DIBTODEVICE 19
|
|
#define DDI_ORD_CREATEBITMAP 20
|
|
#define DDI_ORD_DELETEBITMAP 21
|
|
#define DDI_ORD_SELECTBITMAP 22
|
|
#define DDI_ORD_BITMAPBITS 23
|
|
#define DDI_ORD_RECLIP 24
|
|
#define DDI_ORD_GETPALETTE 25
|
|
#define DDI_ORD_SETPALETTE 26
|
|
#define DDI_ORD_SETPALETTETRANS 27
|
|
#define DDI_ORD_UPDATECOLORS 28
|
|
#define DDI_ORD_STRETCHBLT 29
|
|
#define DDI_ORD_STRETCHDIBITS 30
|
|
#define DDI_ORD_SELECTPALETTE 31
|
|
#define DDI_ORD_INQUIRE 101
|
|
#define DDI_ORD_SETCURSOR 102
|
|
#define DDI_ORD_MOVECURSOR 103
|
|
#define DDI_ORD_CHECKCRSR 104
|
|
#define DDI_ORD_GETDRIVERRESID 450
|
|
|
|
// Maximum DDI ordinal we track
|
|
#define DDI_MAX_ORDINAL 500
|
|
|
|
#endif // NEFORMAT_H
|