Skip to content

Commit

Permalink
Migrate edicon to Ruby Implementation
Browse files Browse the repository at this point in the history
This pull request introduces a Ruby-based implementation of the previously C-based `edicon.exe`. Opting for a Ruby reimplement over fixing the existing C code will shorten the build times and make maintenance easier, leveraging widely accessible Microsoft documentation.

Reference Information:
The original `edicon.c` contained several issues that are being addressed by this reimplementation:

- `BeginUpdateResource` was incorrectly compared to `INVALID_HANDLE_VALUE`. Proper error checking requires comparison to `NULL`.
- The calculation of `GroupIconSize` was incorrect. It should have been calculated using the size of `IconDirResEntry` multiplied by the number of images, instead of using `IconDirectoryEntry`.
This reimplement ensures that the functionality of `EdIcon` is preserved while simplifying future enhancements and maintenance.
  • Loading branch information
shinokaro committed Aug 9, 2024
1 parent 201f51a commit 23b6597
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 157 deletions.
4 changes: 1 addition & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@ task :build_stub do
sh "ridk exec make -C src"
cp "src/stub.exe", "share/ocran/stub.exe"
cp "src/stubw.exe", "share/ocran/stubw.exe"
cp "src/edicon.exe", "share/ocran/edicon.exe"
end

file "share/ocran/stub.exe" => :build_stub
file "share/ocran/stubw.exe" => :build_stub
file "share/ocran/edicon.exe" => :build_stub

task :test => :build_stub

task :clean do
rm_f Dir["{bin,samples}/*.exe"]
rm_f Dir["share/ocran/{stub,stubw,edicon}.exe"]
rm_f Dir["share/ocran/{stub,stubw}.exe"]
sh "ridk exec make -C src clean"
end

Expand Down
148 changes: 148 additions & 0 deletions lib/ocran/ed_icon.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# frozen_string_literal: true
require "fiddle/import"
require "fiddle/types"

module Ocran
# Changes the Icon in a PE executable.
module EdIcon
extend Fiddle::Importer
dlload "kernel32.dll"

include Fiddle::Win32Types
typealias "LPVOID", "void*"
typealias "LPCWSTR", "char*"

module Successive
include Enumerable

def each
return to_enum(__method__) unless block_given?

entry = self
while true
yield(entry)
entry = self.class.new(tail)
end
end

def tail
to_ptr + self.class.size
end
end

# Icon file header
IconHeader = struct(
[
"WORD Reserved",
"WORD ResourceType",
"WORD ImageCount"
]
).include(Successive)

icon_info = [
"BYTE Width",
"BYTE Height",
"BYTE Colors",
"BYTE Reserved",
"WORD Planes",
"WORD BitsPerPixel",
"DWORD ImageSize"
]

# Icon File directory entry structure
IconDirectoryEntry = struct(icon_info + ["DWORD ImageOffset"]).include(Successive)

# Group Icon Resource directory entry structure
IconDirResEntry = struct(icon_info + ["WORD ResourceID"]).include(Successive)

class IconFile < IconHeader
def initialize(icon_filename)
@data = File.binread(icon_filename)
super(Fiddle::Pointer.to_ptr(@data))
end

def entries
IconDirectoryEntry.new(self.tail).take(self.ImageCount)
end
end

class GroupIcon < IconHeader
attr_reader :size

def initialize(image_count, resource_type)
@size = IconHeader.size + image_count * IconDirResEntry.size
super(Fiddle.malloc(@size), Fiddle::RUBY_FREE)
self.Reserved = 0
self.ResourceType = resource_type
self.ImageCount = image_count
end

def entries
IconDirResEntry.new(self.tail).take(self.ImageCount)
end
end

INVALID_HANDLE_VALUE = Fiddle::Pointer.new(-1)

MAKEINTRESOURCE = -> (i) { Fiddle::Pointer.new(i) }
RT_ICON = MAKEINTRESOURCE.(3)
RT_GROUP_ICON = MAKEINTRESOURCE.(RT_ICON.to_i + 11)

MAKELANGID = -> (p, s) { s << 10 | p }
LANG_NEUTRAL = 0x00
SUBLANG_DEFAULT = 0x01
LANGID = MAKELANGID.(LANG_NEUTRAL, SUBLANG_DEFAULT)

extern "DWORD GetLastError()"
extern "HANDLE BeginUpdateResourceW(LPCWSTR, BOOL)"
extern "BOOL EndUpdateResourceW(HANDLE, BOOL)"
extern "BOOL UpdateResourceW(HANDLE, LPCWSTR, LPCWSTR, WORD, LPVOID, DWORD)"

class << self
def update_icon(executable_filename, icon_filename)
update_resource(executable_filename) do |handle|
icon_file = IconFile.new(icon_filename)
icon_entries = icon_file.entries

# Create the RT_ICON resources
icon_entries.each_with_index do |entry, i|
if UpdateResourceW(handle, RT_ICON, 101 + i, LANGID, icon_file.to_i + entry.ImageOffset, entry.ImageSize) == 0
raise "failed to UpdateResource(#{GetLastError()})"
end
end

# Create the RT_GROUP_ICON structure
group_icon = GroupIcon.new(icon_file.ImageCount, icon_file.ResourceType)
group_icon.entries.zip(icon_entries).each_with_index do |(res, icon), i|
res.Width = icon.Width
res.Height = icon.Height
res.Colors = icon.Colors
res.Reserved = icon.Reserved
res.Planes = icon.Planes
res.BitsPerPixel = icon.BitsPerPixel
res.ImageSize = icon.ImageSize
res.ResourceID = 101 + i
end

# Save the RT_GROUP_ICON resource
if UpdateResourceW(handle, RT_GROUP_ICON, 100, LANGID, group_icon, group_icon.size) == 0
raise "Failed to create group icon(#{GetLastError()})"
end
end
end

def update_resource(executable_filename)
handle = BeginUpdateResourceW(executable_filename.encode("UTF-16LE"), 0)
if handle == Fiddle::NULL
raise "Failed to BeginUpdateResourceW(#{GetLastError()})"
end

yield(handle)

if EndUpdateResourceW(handle, 0) == 0
raise "Failed to EndUpdateResourceW(#{GetLastError()})"
end
end
end
end
end
3 changes: 2 additions & 1 deletion lib/ocran/stub_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ def initialize(path, chdir_before: nil, debug_extract: nil, debug_mode: nil,
stub.close

if icon_path
system(EDICON_PATH, stub.path, icon_path.to_s, exception: true)
require_relative "ed_icon"
EdIcon.update_icon(stub.path, icon_path.to_s)
end

File.open(stub, "ab") do |of|
Expand Down
10 changes: 3 additions & 7 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ STUB_CFLAGS = -D_CONSOLE $(CFLAGS)
STUBW_CFLAGS = -mwindows $(CFLAGS)
# -D_MBCS

all: stub.exe stubw.exe edicon.exe
all: stub.exe stubw.exe

stubicon.o: stub.rc
windres -i $< -o $@
Expand All @@ -21,9 +21,6 @@ stub.exe: $(OBJS) stub.o $(CONSOLE_OBJS)
stubw.exe: $(OBJS) stubw.o $(WINDOW_OBJS)
$(CC) $(STUBW_CFLAGS) $(OBJS) stubw.o $(WINDOW_OBJS) -o $@

edicon.exe: edicon.o
$(CC) $(CFLAGS) edicon.o -o edicon

error_console.o: error.c error.h
$(CC) $(STUB_CFLAGS) -o $@ -c $<

Expand Down Expand Up @@ -61,10 +58,9 @@ script_info_window.o: script_info.c script_info.h
$(CC) $(STUBW_CFLAGS) -o $@ -c $<

clean:
rm -f $(OBJS) stub.exe stubw.exe edicon.exe edicon.o stubw.o stub.o \
rm -f $(OBJS) stub.exe stubw.exe stubw.o stub.o \
$(CONSOLE_OBJS) $(WINDOW_OBJS)

install: stub.exe stubw.exe edicon.exe
install: stub.exe stubw.exe
cp -f stub.exe $(BINDIR)/stub.exe
cp -f stubw.exe $(BINDIR)/stubw.exe
cp -f edicon.exe $(BINDIR)/edicon.exe
146 changes: 0 additions & 146 deletions src/edicon.c

This file was deleted.

0 comments on commit 23b6597

Please sign in to comment.