Difference between revisions of "Windows Tiny Intro"

From SizeCoding
Jump to: navigation, search
 
(24 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
== Introduction ==
 
== Introduction ==
This category is for the tiniest Windows-32 intros, that often use custom Windows PE header in combination with CPU software rendering.
+
This category is for the tiniest [[Windows|Windows / win32 intros]], that often use custom Windows PE header in combination with a minimal shader setup or CPU software rendering.
  
 
=== Tools ===
 
=== Tools ===
Assembler: NASM
+
* Assembler: NASM ( https://nasm.us/ )
 +
* Optional : PPEE ( https://www.mzrst.com/ ) Professional PE file Explorer
  
 
== Re-using Windows Header ==
 
== Re-using Windows Header ==
Line 11: Line 12:
 
==== PE Header Structure ====
 
==== PE Header Structure ====
 
Here is the windows header structure:
 
Here is the windows header structure:
<code>
+
 
 +
<syntaxhighlight lang=nasm>
 +
                ORG 0x400000
 +
base:
 
                 DW 'MZ'      ; 00. e_magic
 
                 DW 'MZ'      ; 00. e_magic
                 DW 0          ; 02. e_cblp                        ;UNUSED
+
                 DW 0          ; 02. e_cblp                        ; UNUSED
 
                 DD 'PE'      ; 04. Signature
 
                 DD 'PE'      ; 04. Signature
 +
 +
                ; COFF header
 
                 DW 014CH      ; 08. Target Machine (Intel 386)
 
                 DW 014CH      ; 08. Target Machine (Intel 386)
                 DW 0          ; 0A. NumberOfSections (use 1 for 1 overlapping section)
+
                 DW 0          ; 0A. NumberOfSections (use 1 for overlapping section)
 
                 DW 0,0        ; 0C. TimeDateStamp                ; UNUSED   
 
                 DW 0,0        ; 0C. TimeDateStamp                ; UNUSED   
 
                 DW 0,0        ; 10. PointerToSymbolTable          ; UNUSED
 
                 DW 0,0        ; 10. PointerToSymbolTable          ; UNUSED
Line 22: Line 28:
 
                 DW 8          ; 18. Size Of Optional Header
 
                 DW 8          ; 18. Size Of Optional Header
 
                 DW 2          ; 1A. Characteristics (2 = IMAGE_FILE_EXECUTABLE_IMAGE)
 
                 DW 2          ; 1A. Characteristics (2 = IMAGE_FILE_EXECUTABLE_IMAGE)
 +
               
 +
                ; Optional header
 
                 DW 010BH      ; 1C. Magic (PE32 32bit EXE)
 
                 DW 010BH      ; 1C. Magic (PE32 32bit EXE)
 
                 DW 0          ; 1E. Major+MinorLinkerVersion      ; UNUSED
 
                 DW 0          ; 1E. Major+MinorLinkerVersion      ; UNUSED
                 DW 0,0        ; 20.SizeOfCode                    ; UNUSED
+
                 DW 0,0        ; 20. SizeOfCode                    ; UNUSED
                 DW 0,0        ; 24.SizeOfInitializedData          ; UNUSED
+
                 DW 0,0        ; 24. SizeOfInitializedData          ; UNUSED
                 DW 0,0        ; 28.SizeOfUninitializedData        ; UNUSED
+
                 DW 0,0        ; 28. SizeOfUninitializedData        ; UNUSED
                 DD start-BASE ; 2C.AddressOfEntryPoint
+
                 DD start-base ; 2C. AddressOfEntryPoint
                 DD 0          ; 30.BaseOfCode
+
                 DD 0          ; 30. BaseOfCode                     ; UNUSED
edit:            DD 'edit'    ; 34.BaseOfData
+
                 DD 0          ; 34. BaseOfData                    ; UNUSED
                DD BASE      ; 38.ImageBase
 
                DD 4          ; 3C.SectionAlignment (.e_lfanew)
 
                DD 4          ; 40.FileAlignment
 
                DD 0          ; 44.Major+MinorOperatingSystemVersion  ; UNUSED
 
                 DD 0          ; 48.Major+MinorImageVersion            ; UNUSED
 
                DW 4          ; 4C.MajorSubsystemVersion (use 3 of 4)
 
                DW 0          ; 4E.MinorSubsystemVersion              ; UNUSED
 
                DD 0          ; 50.Win32VersionValue                  ; UNUSED
 
                DD 0          ; 54.SizeOfImage (malloc)                ; UNUSED
 
 
 
                DD 30H        ; 58.SizeOfHeaders
 
                DD 0          ; 5C.CheckSum                            ; UNUSED/UNCHECKED
 
                DW 2          ; 60.Subsystem 2->gui
 
                DW 0          ; 62.DllCharacteristics
 
start:                        ; 64.SizeOfStackReserve
 
  
; Padding to 268 bytes (minimal exe file length for Win7+)
+
                ; Windows specific fields
</code>
+
                DD base      ; 38. ImageBase
 +
                DD 4          ; 3C. SectionAlignment (.e_lfanew / PE offset +4)
 +
                DD 4          ; 40. FileAlignment
 +
                DD 0          ; 44. Major+MinorOperatingSystemVersion  ; UNUSED
 +
                DD 0          ; 48. Major+MinorImageVersion            ; UNUSED
 +
                DW 4          ; 4C. MajorSubsystemVersion              ; (use 3 of 4)
 +
                DW 0          ; 4E. MinorSubsystemVersion              ; UNUSED
 +
                DD 0          ; 50. Win32VersionValue                  ; UNUSED
 +
                DD 0          ; 54. SizeOfImage (malloc)                ; UNUSED
 +
                DD 30H        ; 58. SizeOfHeaders
 +
                DD 0          ; 5C. CheckSum                            ; UNUSED/UNCHECKED
 +
                DW 2          ; 60. Subsystem (Win32 GUI)
 +
                DW 0          ; 62. DllCharacteristics
 +
                              ; 64. SizeOfStackReserve  (program entry)
 +
start:
 +
                ; Program entry
 +
                ; Padding to 268 bytes (minimal exe file length for Win7+)
 +
</syntaxhighlight>
  
 
==== Importing Functions ====
 
==== Importing Functions ====
 
An important part of doing your own Windows Header management is that you will need to import functions manually.  
 
An important part of doing your own Windows Header management is that you will need to import functions manually.  
 
This can be done via header import, Import by Ordinal (specific to a windows version) or Import by hash.
 
This can be done via header import, Import by Ordinal (specific to a windows version) or Import by hash.
 +
 +
==== Minimal Required Function imports ====
 +
Here is a list for minimal function imports for any graphical intro.
 +
* <code>LoadLibraryA</code> - Required to load libraries
 +
* <code>GetAsyncKeyState(VK_ESCAPE)</code> - Check for escape key
 +
* <code>ExitProcess(0)</code> - Close down application
 +
 +
==== Optional Function imports ====
 +
* <code>hwnd = CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0)</code> - Set up a Maximised Window
 +
* <code>t=GetTickCount()</code> - Optional: Get Current ticks
 +
* <code>ShowCursor(NULL)</code> - Optional: Hide mousecursor
  
 
== Graphics ==
 
== Graphics ==
 
For 512 bytes, there are 2 ways to go about rendering graphics:
 
For 512 bytes, there are 2 ways to go about rendering graphics:
  
* CPU Software Rendering
+
==== Minimal OpenGL/GLSL Rendering ====
* Minimal OpenGL/GLSL Rendering
+
See http://www.sizecoding.org/wiki/1K_Intro for more information and setup
 +
 
 +
==== CPU Software Rendering ====
 +
The following function imports are needed for software rendering:
 +
* GetHDC
 +
* GetWindowRect
 +
* StretchDIBits
 +
* SwapBuffers
 +
 
 +
Or for the slower on-screen approach:
 +
* SetPixel
  
 
== Sound ==
 
== Sound ==
Line 76: Line 107:
 
An alternative option is to use a very simple Bytebeat softsynth (Check out https://bytebeat.demozoo.org if you are not familiar with the concept).  
 
An alternative option is to use a very simple Bytebeat softsynth (Check out https://bytebeat.demozoo.org if you are not familiar with the concept).  
  
This requires an additional setup of the Win32 WaveOut callback, which can then be played and filled realtime with data.
+
The cheapest way to do this is to reserve and setup a 44 byte WAV header and precalculate the bytebeat music,  
 +
and then use the <code>sndPlaySoundA</code> call to play it as a looping sample at startup.
  
 
For a taste of what is possible with small (<256 character) bytebeats formulas, check out one of the Bytebeat Music competitions from the Lovebyte parties, for example https://demozoo.org/parties/4760/#competition_18623
 
For a taste of what is possible with small (<256 character) bytebeats formulas, check out one of the Bytebeat Music competitions from the Lovebyte parties, for example https://demozoo.org/parties/4760/#competition_18623
Line 88: Line 120:
 
* <code>CreateDecompressor(0x20000002, 0, &HDecompressor)</code> - Create Decompressor object
 
* <code>CreateDecompressor(0x20000002, 0, &HDecompressor)</code> - Create Decompressor object
 
* <code>Decompress(HDecompressor, CompressedPayload, PackOutputSize, 0x11000, PackInputSize,0)</code> - Decompress payload
 
* <code>Decompress(HDecompressor, CompressedPayload, PackOutputSize, 0x11000, PackInputSize,0)</code> - Decompress payload
 
  
 
== 512 byte intros for Windows ==
 
== 512 byte intros for Windows ==
Line 102: Line 133:
 
* [https://demozoo.org/productions/338132/ Pixondex - Tiny 256 byte intro effect for Windows]
 
* [https://demozoo.org/productions/338132/ Pixondex - Tiny 256 byte intro effect for Windows]
 
* [https://demozoo.org/productions/286628/ Xor pattern in 256 bytes]
 
* [https://demozoo.org/productions/286628/ Xor pattern in 256 bytes]
 +
 +
== PE Header Resources ==
 +
* [http://www.phreedom.org/research/tinype/ Tiny PE header information]
 +
* [https://in4k.untergrund.net/various%20web%20articles/Iczelion_s_PE_Tutorial_1_Overview_of_PE_File_Format.htm Overview of PE File Format]
 +
* [http://in4k.untergrund.net/various%20web%20articles/pe1.zip LUEVELSMEYER’s description about PE file format]
 +
* [https://davidesnotes.com/articles/1/ Article about Making a 874 byte Windows EXE]
 +
* [https://github.com/PlummersSoftwareLLC/HelloAssembly/blob/main/Theron/header_tiny.asm Another Tiny PE Header with information]
 +
 +
== Seminars ==
 +
* [https://youtube.com/Up_RSAYi0OI Ideas and Techniques for Smaller Executables Seminar Video]
 +
* [ftp://ftp.untergrund.net/users/in4kadmin/files/compression.pdf Ideas and Techniques for Smaller Executables Seminar Slides]
  
 
== Other Resources ==
 
== Other Resources ==
* [https://davidesnotes.com/articles/1/ Article about Making a 874 byte Windows EXE]
+
* [https://github.com/coderforlife/ms-compress Open source implementations of Microsoft compression algorithms]
* [http://www.phreedom.org/research/tinype/ Tiny PE header information]
+
* [https://learn.microsoft.com/en-us/windows/win32/api/compressapi/nf-compressapi-createdecompressor CreateDecompressor API call]

Latest revision as of 07:09, 15 April 2024

Introduction

This category is for the tiniest Windows / win32 intros, that often use custom Windows PE header in combination with a minimal shader setup or CPU software rendering.

Tools

Re-using Windows Header

The minimum Windows PE Header size on modern systems (Windows7 and up) is 268 bytes, even though part of this header is useless padding. So whilst it is perfectly possible to do a small graphical effect within thatn 268 byte limit by re-using part of the header, doing anything smaller than this lower limit is only possible on older Windows versions like Windows XP.

PE Header Structure

Here is the windows header structure:

                 ORG	0x400000
 base:
                 DW 'MZ'       ; 00. e_magic
                 DW 0          ; 02. e_cblp                        ; UNUSED
                 DD 'PE'       ; 04. Signature

                 ; COFF header
                 DW 014CH      ; 08. Target Machine (Intel 386)
                 DW 0          ; 0A. NumberOfSections (use 1 for overlapping section)
                 DW 0,0        ; 0C. TimeDateStamp                 ; UNUSED  
                 DW 0,0        ; 10. PointerToSymbolTable          ; UNUSED
                 DW 0,0        ; 14. NumberOfSymbols               ; UNUSED
                 DW 8          ; 18. Size Of Optional Header
                 DW 2          ; 1A. Characteristics (2 = IMAGE_FILE_EXECUTABLE_IMAGE)
                 
                 ; Optional header
                 DW 010BH      ; 1C. Magic (PE32 32bit EXE)
                 DW 0          ; 1E. Major+MinorLinkerVersion      ; UNUSED
                 DW 0,0        ; 20. SizeOfCode                     ; UNUSED
                 DW 0,0        ; 24. SizeOfInitializedData          ; UNUSED
                 DW 0,0        ; 28. SizeOfUninitializedData        ; UNUSED
                 DD start-base ; 2C. AddressOfEntryPoint
                 DD 0          ; 30. BaseOfCode                     ; UNUSED
                 DD 0          ; 34. BaseOfData                     ; UNUSED

                 ; Windows specific fields
                 DD base       ; 38. ImageBase
                 DD 4          ; 3C. SectionAlignment (.e_lfanew / PE offset +4)
                 DD 4          ; 40. FileAlignment
                 DD 0          ; 44. Major+MinorOperatingSystemVersion   ; UNUSED
                 DD 0          ; 48. Major+MinorImageVersion             ; UNUSED
                 DW 4          ; 4C. MajorSubsystemVersion               ; (use 3 of 4)
                 DW 0          ; 4E. MinorSubsystemVersion               ; UNUSED
                 DD 0          ; 50. Win32VersionValue                   ; UNUSED
                 DD 0          ; 54. SizeOfImage (malloc)                ; UNUSED
                 DD 30H        ; 58. SizeOfHeaders
                 DD 0          ; 5C. CheckSum                            ; UNUSED/UNCHECKED
                 DW 2          ; 60. Subsystem (Win32 GUI)
                 DW 0          ; 62. DllCharacteristics
                               ; 64. SizeOfStackReserve  (program entry)
 start:
                 ; Program entry
                 ; Padding to 268 bytes (minimal exe file length for Win7+)

Importing Functions

An important part of doing your own Windows Header management is that you will need to import functions manually. This can be done via header import, Import by Ordinal (specific to a windows version) or Import by hash.

Minimal Required Function imports

Here is a list for minimal function imports for any graphical intro.

  • LoadLibraryA - Required to load libraries
  • GetAsyncKeyState(VK_ESCAPE) - Check for escape key
  • ExitProcess(0) - Close down application

Optional Function imports

  • hwnd = CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0) - Set up a Maximised Window
  • t=GetTickCount() - Optional: Get Current ticks
  • ShowCursor(NULL) - Optional: Hide mousecursor

Graphics

For 512 bytes, there are 2 ways to go about rendering graphics:

Minimal OpenGL/GLSL Rendering

See http://www.sizecoding.org/wiki/1K_Intro for more information and setup

CPU Software Rendering

The following function imports are needed for software rendering:

  • GetHDC
  • GetWindowRect
  • StretchDIBits
  • SwapBuffers

Or for the slower on-screen approach:

  • SetPixel

Sound

There are a few ways to producing sound/music in Tiny Windows Intros.

Win32 Single Note MIDI

Using the built-in MIDI, this requires just 2 calls

  • midiOutOpen(midihandle, 0,0,0,0) - To initialise the MIDI
  • midiOutShortMsg(midihandle, 0x209035C0) - 4 byte Midi message that sets instrument + plays a single note on channel 0

For a full list of Midi commands, check out this webpage: https://computermusicresource.com/MIDI.Commands.html

Win32 MIDI Player

Depending on the sound budget, you can think about including a small MIDI tune instead. Similar to the single note MIDI approach, but playing multiple notes from data on 1 or more different channels.

Bytebeat softsynth

An alternative option is to use a very simple Bytebeat softsynth (Check out https://bytebeat.demozoo.org if you are not familiar with the concept).

The cheapest way to do this is to reserve and setup a 44 byte WAV header and precalculate the bytebeat music, and then use the sndPlaySoundA call to play it as a looping sample at startup.

For a taste of what is possible with small (<256 character) bytebeats formulas, check out one of the Bytebeat Music competitions from the Lovebyte parties, for example https://demozoo.org/parties/4760/#competition_18623

Compression

Compression for the smaller Windows Tiny Intros are tricky, but there have been some experiments using the Windows Cabinet Decompressor calls

These functions allow for MSZIP (deflate) and MSLZ content to be loaded from the binary as payload at the cost of a a few more win32 function imports. Well this might be a good solution for 512 byte intros, the tipping point for Crinkler compression at the moment lies at the 800-850 bytes mark give or take.

From cabinet library:

  • CreateDecompressor(0x20000002, 0, &HDecompressor) - Create Decompressor object
  • Decompress(HDecompressor, CompressedPayload, PackOutputSize, 0x11000, PackInputSize,0) - Decompress payload

512 byte intros for Windows

256 / 268 byte intros for Windows

PE Header Resources

Seminars

Other Resources