CPython opens the stdlib zip with its own stdio, so it needs a real path.
On the desktop res:// is a directory and globalize_path() is enough; in an
exported build res:// is inside the PCK, which nothing outside Godot reads.
make_stdlib_zip.py now also writes <out>.sha256
mtpython_stdlib_project copies python27.zip + .sha256 into project/
(gitignored), and both export presets' include_filter lists them, so
they enter the PCK — checked with --export-pack
extension/src/python_stdlib.{h,cpp} (Metin2Python.stdlib_path in
GDScript): reads the bytes out of the PCK, writes
user://python27.zip.part, hashes the file as written against the
shipped digest, then renames. A sandbox copy is reused only when it
matches, so a first start killed mid-copy and a zip replaced by a new
build both re-stage instead of feeding zipimport a torn file.
PythonHost::SetDefaultStdLibPath / DefaultStdLibPath() answer with that
path ($MT_PYTHON_STDLIB still wins). python_stdlib.cpp is the only unit
that knows res:// / user://; port_platform stays godot-free.
Also fixes the Windows gate, which step 3b broke: without mtpython the
MinGW build compiled UserInterface/StdAfx.h (it includes ScriptLib/StdAfx.h,
as the original PCH does). port/CMakeLists.txt excludes that header and
PythonPackModule.cpp with ScriptLib, and platform/CMakeLists.txt excludes
platform/ScriptLib/ the same way.
Not done: on-device staging. The macOS test drives the mobile path with
force_stage=true, and nothing in the app boot calls it yet — the
interpreter only starts in the process with 2V0.
gates: python_stdlib_test.gd PASS (in-place path, staging, sha256, bytes
equal res://, no .part, ZIPReader finds encodings/__init__.py, no re-copy
on a second call, corrupted copy re-staged) · macOS ctest 27/27 ·
mingw + android + ios port_platform compile clean · port_map.py check 0
errors · key leak check 8/8 none.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
176 lines
9.5 KiB
CMake
176 lines
9.5 KiB
CMake
# port_logic — mirror of the 40250 client logic (extension/src/port/<Lib>/<File>.{h,cpp}).
|
|
# No godot-cpp dependency: libmtgodot links it, and it also builds on its own (top-level
|
|
# -DMTGODOT_BUILD_EXTENSION=OFF -DMTGODOT_BUILD_PORT=ON) for targets without an extension build.
|
|
# See docs/PORT-PLAN.md batch 2A.
|
|
|
|
file(GLOB_RECURSE MT_PORT_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
|
file(GLOB_RECURSE MT_PORT_HEADERS CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
|
if(WIN32)
|
|
# Stand-ins for Windows SDK headers; on Windows the real ones are used.
|
|
list(FILTER MT_PORT_HEADERS EXCLUDE REGEX "/common/shim/win32/")
|
|
endif()
|
|
# The script layer needs the embedded interpreter (mt3p::python), which has no Windows build yet
|
|
# (PORT-PLAN 批次 2P step 4). Without it, ScriptLib and its <Python-2.7/*> shims are left out.
|
|
# The units outside ScriptLib that reach it: UserInterface/StdAfx.h includes ScriptLib/StdAfx.h (as the
|
|
# original PCH does), and PythonPackModule.cpp is a Python module. Both go with it.
|
|
set(MT_PORT_NEEDS_PYTHON "/ScriptLib/|/UserInterface/StdAfx\\.h|/UserInterface/PythonPackModule\\.cpp")
|
|
if(NOT TARGET mtpython)
|
|
list(FILTER MT_PORT_SOURCES EXCLUDE REGEX "${MT_PORT_NEEDS_PYTHON}")
|
|
list(FILTER MT_PORT_HEADERS EXCLUDE REGEX "${MT_PORT_NEEDS_PYTHON}|/common/shim/sdk/Python-2\\.7/")
|
|
endif()
|
|
|
|
add_library(port_logic STATIC ${MT_PORT_SOURCES})
|
|
target_include_directories(port_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
# 40250 system headers the ported sources keep including verbatim: D3D8/D3DX/Miles SDK types everywhere,
|
|
# Windows/CRT ones only where the platform has none.
|
|
set(MT_PORT_SHIMS ${CMAKE_CURRENT_SOURCE_DIR}/common/shim/sdk)
|
|
if(NOT WIN32)
|
|
list(APPEND MT_PORT_SHIMS ${CMAKE_CURRENT_SOURCE_DIR}/common/shim/win32)
|
|
endif()
|
|
target_include_directories(port_logic PUBLIC ${MT_PORT_SHIMS})
|
|
# Libraries 40250 links that are vendored as-is: <lzo/lzo1x.h> (EterBase/lzo.h), <cryptopp/*>
|
|
# (EterBase/cipher.h), <Python-2.7/*> (ScriptLib).
|
|
target_link_libraries(port_logic PUBLIC mt3p::minilzo mt3p::cryptopp)
|
|
if(TARGET mtpython)
|
|
target_link_libraries(port_logic PUBLIC mt3p::python)
|
|
endif()
|
|
# 40250 sources carry MSVC `#pragma warning(...)` lines verbatim, and non-literal printf formats
|
|
# (EterImageLib/StdAfx.h) that the Android toolchain turns into errors.
|
|
set(MT_PORT_WARNING_FLAGS)
|
|
if(NOT MSVC)
|
|
set(MT_PORT_WARNING_FLAGS -Wno-unknown-pragmas -Wno-format-security)
|
|
endif()
|
|
target_compile_options(port_logic PUBLIC ${MT_PORT_WARNING_FLAGS})
|
|
target_compile_features(port_logic PUBLIC cxx_std_20)
|
|
# Armadillo nanomite markers (StdAfx.h `__asm _emit` jumps over a signature): x86-32 MSVC inline asm that no
|
|
# target here accepts, and a no-op without the Armadillo wrapper, so they expand to nothing.
|
|
target_compile_definitions(port_logic PUBLIC "NANOBEGIN=" "NANOEND=")
|
|
if(MINGW)
|
|
target_compile_options(port_logic PUBLIC -include ${CMAKE_CURRENT_SOURCE_DIR}/common/Win32MinMax.h)
|
|
endif()
|
|
|
|
# EterPack/EterPack.cpp includes EterPackKeys.generated.h in place of the two TEA key arrays: the keys are
|
|
# read from the reference EterPack.cpp at configure time and written into the build tree, never the repository.
|
|
# Without the reference the arrays are zero and every COMPRESS/SECURITY entry fails to decode.
|
|
set(MT_40250_SOURCE "$ENV{MT_40250_SOURCE}" CACHE PATH "40250 ClientVS22/source reference root")
|
|
if(NOT MT_40250_SOURCE)
|
|
set(MT_40250_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/../../../../40250/Server Client TMP4/ClientVS22/source")
|
|
endif()
|
|
set(MT_PORT_GENERATED ${CMAKE_CURRENT_BINARY_DIR}/generated)
|
|
set(mt_eterpack_ref "${MT_40250_SOURCE}/EterPack/EterPack.cpp")
|
|
set(mt_eterpack_keys "")
|
|
if(EXISTS "${mt_eterpack_ref}")
|
|
file(READ "${mt_eterpack_ref}" mt_ref_text)
|
|
foreach(arr s_adwEterPackKey s_adwEterPackSecurityKey)
|
|
string(REGEX MATCH "static DWORD ${arr}\\[\\][ \t\r\n]*=[ \t\r\n]*{[^}]*}" block "${mt_ref_text}")
|
|
string(REGEX MATCHALL "[0-9]+" values "${block}")
|
|
list(LENGTH values n)
|
|
if(NOT n EQUAL 4)
|
|
message(FATAL_ERROR "port: ${arr} not found in ${mt_eterpack_ref}")
|
|
endif()
|
|
list(JOIN values ", " values)
|
|
string(APPEND mt_eterpack_keys "static DWORD ${arr}[] = { ${values} };\n")
|
|
endforeach()
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${mt_eterpack_ref}")
|
|
else()
|
|
message(WARNING "port: ${mt_eterpack_ref} not found (set MT_40250_SOURCE); EterPack keys are zero")
|
|
string(APPEND mt_eterpack_keys "static DWORD s_adwEterPackKey[] = { 0, 0, 0, 0 };\n"
|
|
"static DWORD s_adwEterPackSecurityKey[] = { 0, 0, 0, 0 };\n")
|
|
endif()
|
|
file(CONFIGURE OUTPUT ${MT_PORT_GENERATED}/EterPackKeys.generated.h
|
|
CONTENT "// Generated from the 40250 reference EterPack.cpp by port/CMakeLists.txt; do not commit.\n#pragma once\n${mt_eterpack_keys}")
|
|
target_include_directories(port_logic PRIVATE ${MT_PORT_GENERATED})
|
|
if(WIN32)
|
|
target_link_libraries(port_logic PUBLIC winmm)
|
|
endif()
|
|
|
|
# 40250 Distribute|Win32 preprocessor definitions per library (vs_files/<Lib>/<Lib>.vcxproj),
|
|
# minus WIN32/_WINDOWS/_LIB/_CRT_SECURE_NO_WARNINGS: Windows-only branches go through platform/,
|
|
# never through ported logic. UserInterface/Locale_inc.h feature macros come with that header.
|
|
set(MT_PORT_DEFINES_default NDEBUG)
|
|
set(MT_PORT_DEFINES_GameLib NDEBUG USE_LOD)
|
|
set(MT_PORT_DEFINES_UserInterface NDEBUG USE_LOD _DISTRIBUTE)
|
|
|
|
function(mt_port_apply_defines file)
|
|
file(RELATIVE_PATH rel ${CMAKE_CURRENT_SOURCE_DIR} ${file})
|
|
string(REGEX REPLACE "/.*" "" lib "${rel}")
|
|
if(DEFINED MT_PORT_DEFINES_${lib})
|
|
set(defs ${MT_PORT_DEFINES_${lib}})
|
|
else()
|
|
set(defs ${MT_PORT_DEFINES_default})
|
|
endif()
|
|
set_property(SOURCE ${file} APPEND PROPERTY COMPILE_DEFINITIONS ${defs})
|
|
endfunction()
|
|
|
|
foreach(src IN LISTS MT_PORT_SOURCES)
|
|
mt_port_apply_defines(${src})
|
|
endforeach()
|
|
|
|
# Gate: every header under port/ compiles on its own (and twice, for its include guard), with its
|
|
# library's definitions. As in 40250, where each library's StdAfx.h is the precompiled header of
|
|
# every translation unit, the library's StdAfx.h comes first. Built by default, so every platform
|
|
# build enforces it.
|
|
set(MT_PORT_GATE_SOURCES)
|
|
foreach(hdr IN LISTS MT_PORT_HEADERS)
|
|
file(RELATIVE_PATH rel ${CMAKE_CURRENT_SOURCE_DIR} ${hdr})
|
|
string(MAKE_C_IDENTIFIER "${rel}" id)
|
|
set(tu ${CMAKE_CURRENT_BINARY_DIR}/header_gate/${id}.cpp)
|
|
string(REGEX REPLACE "/.*" "" lib "${rel}")
|
|
set(pch "")
|
|
# 40250 spells it StdAfx.h or Stdafx.h; use the on-disk name for case-sensitive hosts.
|
|
file(GLOB stdafx RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${lib}/[Ss]td[Aa]fx.h)
|
|
if(NOT lib STREQUAL "common" AND stdafx)
|
|
set(pch "#include \"${stdafx}\"\n")
|
|
endif()
|
|
file(CONFIGURE OUTPUT ${tu} CONTENT "${pch}#include \"${rel}\"\n#include \"${rel}\"\n")
|
|
set_property(SOURCE ${tu} APPEND PROPERTY OBJECT_DEPENDS ${hdr})
|
|
list(APPEND MT_PORT_GATE_SOURCES ${tu})
|
|
# Definitions follow the header's library, not the generated file's location.
|
|
if(DEFINED MT_PORT_DEFINES_${lib})
|
|
set_property(SOURCE ${tu} APPEND PROPERTY COMPILE_DEFINITIONS ${MT_PORT_DEFINES_${lib}})
|
|
else()
|
|
set_property(SOURCE ${tu} APPEND PROPERTY COMPILE_DEFINITIONS ${MT_PORT_DEFINES_default})
|
|
endif()
|
|
endforeach()
|
|
add_library(port_header_gate OBJECT ${MT_PORT_GATE_SOURCES})
|
|
target_include_directories(port_header_gate PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${MT_PORT_SHIMS})
|
|
target_link_libraries(port_header_gate PRIVATE mt3p::minilzo mt3p::cryptopp)
|
|
if(TARGET mtpython)
|
|
target_link_libraries(port_header_gate PRIVATE mt3p::python)
|
|
endif()
|
|
target_compile_features(port_header_gate PRIVATE cxx_std_20)
|
|
target_compile_definitions(port_header_gate PRIVATE "NANOBEGIN=" "NANOEND=")
|
|
if(MINGW)
|
|
target_compile_options(port_header_gate PRIVATE -include ${CMAKE_CURRENT_SOURCE_DIR}/common/Win32MinMax.h)
|
|
endif()
|
|
target_compile_options(port_header_gate PRIVATE ${MT_PORT_WARNING_FLAGS})
|
|
add_dependencies(port_logic port_header_gate)
|
|
|
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../platform ${CMAKE_CURRENT_BINARY_DIR}/../platform)
|
|
|
|
if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
|
|
add_executable(port_common_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_common_test.cpp)
|
|
target_link_libraries(port_common_test PRIVATE port_logic)
|
|
add_test(NAME port.common COMMAND $<TARGET_FILE:port_common_test>)
|
|
|
|
# Against the real 40250 Client/pack next to the reference source; skipped (77) when it is missing,
|
|
# failed instead under MT_ASSETS_STRICT=1.
|
|
set(MT_40250_CLIENT "$ENV{MT_40250_CLIENT}" CACHE PATH "40250 Client directory (pack/, Eternexus/)")
|
|
if(NOT MT_40250_CLIENT)
|
|
set(MT_40250_CLIENT "${MT_40250_SOURCE}/../../Client")
|
|
endif()
|
|
add_executable(port_eterpack_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_eterpack_test.cpp)
|
|
target_link_libraries(port_eterpack_test PRIVATE port_platform)
|
|
add_test(NAME port.eterpack COMMAND $<TARGET_FILE:port_eterpack_test> ${MT_40250_CLIENT})
|
|
set_tests_properties(port.eterpack PROPERTIES SKIP_RETURN_CODE 77)
|
|
|
|
if(TARGET mtpython)
|
|
add_executable(port_python_launcher_test ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_python_launcher_test.cpp)
|
|
target_link_libraries(port_python_launcher_test PRIVATE port_platform)
|
|
add_dependencies(port_python_launcher_test mtpython_stdlib)
|
|
add_test(NAME port.python_launcher
|
|
COMMAND $<TARGET_FILE:port_python_launcher_test> ${MT_40250_CLIENT} ${MT_PYTHON_STDLIB_ZIP})
|
|
set_tests_properties(port.python_launcher PROPERTIES SKIP_RETURN_CODE 77)
|
|
endif()
|
|
endif()
|