Skip to content

Commit 880ad5e

Browse files
committed
Add multibyte path and filename support for resource handling in OCRAN
This commit introduces comprehensive support for multibyte (UTF-8) characters in file and directory names during resource extraction and script execution. Key improvements: - Added UTF-8 manifest to stub.exe to ensure correct handling of UTF-8 paths using ANSI (A) APIs on modern Windows environments. - Added test cases covering: - Executables run from multibyte directories - Scripts with multibyte filenames - Resource files with multibyte names - Resource directories with multibyte names - Adjusted test scripts to perform byte-wise filename matching to bypass Windows/Ruby encoding mismatches. - Tests now conditionally skip if console code page is not UTF-8 (65001), preventing false negatives on non-UTF-8 environments (e.g., CP932). These changes ensure that OCRAN reliably handles multibyte resources in real-world Japanese or emoji-rich file systems.
1 parent e8395bd commit 880ad5e

File tree

10 files changed

+127
-3
lines changed

10 files changed

+127
-3
lines changed

CHANGELOG.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
=== 1.3.17
22
- Modified `rake test` to no longer build stubs during test execution. Use `rake build_stub` when stub generation is necessary.
3+
- Add full support for multibyte (UTF-8) names across executables, scripts, and resources. Requires Windows 10 version 1903 or later.
4+
- Added UTF-8 manifest to stub.exe to enable UTF-8 support in ANSI API calls.
35

46
=== 1.3.16
57
- Support for Ruby 3.0 and above. Drop ruby 2.6 and 2.7 support.

src/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
SRCS = lzma/LzmaDec.c
2-
OBJS = $(SRCS:.c=.o) stubicon.o
2+
OBJS = $(SRCS:.c=.o) stub.res
33
CONSOLE_OBJS = error_console.o filesystem_utils_console.o inst_dir_console.o script_info_console.o unpack_console.o
44
WINDOW_OBJS = error_window.o filesystem_utils_window.o inst_dir_window.o script_info_window.o unpack_window.o
55
CC = gcc
@@ -12,8 +12,8 @@ STUBW_CFLAGS = -mwindows $(CFLAGS)
1212

1313
all: stub.exe stubw.exe edicon.exe
1414

15-
stubicon.o: stub.rc
16-
windres -i $< -o $@
15+
stub.res: stub.rc
16+
windres -i stub.rc -O coff -o stub.res
1717

1818
stub.exe: $(OBJS) stub.o $(CONSOLE_OBJS)
1919
$(CC) $(STUB_CFLAGS) $(OBJS) stub.o $(CONSOLE_OBJS) -o $@

src/stub.manifest

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3+
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
4+
<security>
5+
<requestedPrivileges>
6+
<requestedExecutionLevel level="asInvoker"/>
7+
</requestedPrivileges>
8+
</security>
9+
</trustInfo>
10+
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
11+
<application>
12+
<!--The ID below indicates application support for Windows Vista -->
13+
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
14+
<!--The ID below indicates application support for Windows 7 -->
15+
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
16+
<!--The ID below indicates application support for Windows 8 -->
17+
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
18+
<!--The ID below indicates application support for Windows 8.1 -->
19+
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
20+
<!--The ID below indicates application support for Windows 10 -->
21+
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
22+
</application>
23+
</compatibility>
24+
<application>
25+
<windowsSettings>
26+
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
27+
</windowsSettings>
28+
</application>
29+
</assembly>

src/stub.rc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
#include <winresrc.h>
2+
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "stub.manifest"
13
101 ICON vit-ruby.ico
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Dir.chdir File.dirname(__FILE__)
2+
3+
dir_expected = "äあ💎"
4+
file_expected = "äあ💎.txt"
5+
expected_content = "Hello Multibyte File!\n"
6+
7+
dir_actual = Dir.entries(".").find { |d| File.directory?(d) && d.bytes == dir_expected.bytes }
8+
raise "Directory not found" unless dir_actual
9+
10+
file_actual = Dir.entries(dir_actual).find { |f| f.bytes == file_expected.bytes }
11+
raise "File not found in directory" unless file_actual
12+
13+
content = File.read(File.join(dir_actual, file_actual))
14+
raise "File content mismatch" unless content == expected_content
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello Multibyte File!
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Dir.chdir File.dirname(__FILE__)
2+
3+
expected_name = "äあ💎.txt"
4+
expected_content = "Hello Multibyte File!\n"
5+
6+
actual_name = Dir.entries(".").find { |f| f.bytes == expected_name.bytes }
7+
raise "File not found" unless actual_name
8+
9+
content = File.read(actual_name)
10+
raise "File content mismatch" unless content == expected_content
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello Multibyte File!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
content = "Hello from multibyte-named script"

test/test_ocra.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,4 +807,68 @@ def test_debug
807807
end
808808
end
809809
end
810+
811+
# Tests whether an OCRAN-built executable runs correctly from a directory
812+
# with multibyte (UTF-8) characters in its name.
813+
def test_multibyte_path_execution
814+
with_fixture 'helloworld' do
815+
exe_name = "helloworld.exe"
816+
assert system("ruby", ocran, "helloworld.rb", *DefaultArgs)
817+
assert File.exist?(exe_name)
818+
819+
multibyte_dir = "äあ💎"
820+
821+
pristine_env exe_name do
822+
mkdir_p multibyte_dir
823+
cp exe_name, multibyte_dir
824+
Dir.chdir(multibyte_dir) do
825+
assert system(exe_name)
826+
end
827+
end
828+
end
829+
end
830+
831+
# Tests building and running a Ruby script with multibyte (UTF-8) characters
832+
# in its filename. Skipped unless the console code page is UTF-8 (65001),
833+
# as ruby.exe misinterprets arguments under non-UTF-8 environments.
834+
def test_multibyte_script_filename
835+
cp = `chcp`.force_encoding(Encoding::BINARY)[/\d+/] || "unknown"
836+
unless cp == "65001"
837+
skip "Skipped: console code page must be UTF-8 (65001), got #{cp}"
838+
end
839+
840+
with_fixture 'multibyte_script' do
841+
script = "äあ💎.rb"
842+
assert system("ruby", ocran, script, *DefaultArgs)
843+
exe_name = script.sub(/\.rb$/, '.exe')
844+
assert File.exist?(exe_name)
845+
pristine_env exe_name do
846+
assert system(exe_name)
847+
end
848+
end
849+
end
850+
851+
# Tests if a multibyte-named resource file is correctly included and read
852+
# at runtime after being packaged by OCRAN.
853+
def test_multibyte_resource_file
854+
with_fixture 'multibyte_file' do
855+
assert system("ruby", ocran, "resource.rb", "äあ💎.txt", *DefaultArgs)
856+
assert File.exist?("resource.exe")
857+
pristine_env "resource.exe" do
858+
assert system("resource.exe")
859+
end
860+
end
861+
end
862+
863+
# Tests that OCRAN can handle resource files stored in a directory
864+
# with multibyte (UTF-8) characters in its name.
865+
def test_multibyte_resource_dir
866+
with_fixture 'multibyte_dir' do
867+
assert system("ruby", ocran, "resource.rb", "äあ💎/äあ💎.txt", *DefaultArgs)
868+
assert File.exist?("resource.exe")
869+
pristine_env "resource.exe" do
870+
assert system("resource.exe")
871+
end
872+
end
873+
end
810874
end

0 commit comments

Comments
 (0)