From 5234c555e76bf57f6a71a3af49c986368c9ee84e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?= <101191390+HIllya51@users.noreply.github.com> Date: Wed, 1 May 2024 14:59:50 +0800 Subject: [PATCH] test Update retrieval.py --- .github/workflows/build.yml | 10 +- LunaTranslator/LunaTranslator/keeprefs.py | 1 + LunaTranslator/pack32.cmd | 3 +- LunaTranslator/pack64.cmd | 3 +- LunaTranslator/requirements.txt | 4 +- LunaTranslator/retrieval.py | 275 ++++++++++++++ build.py | 21 +- plugins/exec/CMakeLists.txt | 16 +- plugins/exec/PyStand.cpp | 414 ++++++++++++++++++++++ plugins/exec/PyStand.h | 61 ++++ plugins/exec/luna.cpp | 22 -- 11 files changed, 766 insertions(+), 64 deletions(-) create mode 100644 LunaTranslator/retrieval.py create mode 100644 plugins/exec/PyStand.cpp create mode 100644 plugins/exec/PyStand.h delete mode 100644 plugins/exec/luna.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1480460..0d4c1932 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,16 +14,8 @@ jobs: include: - architecture: x86 fname: LunaTranslator_x86 - debug: 0 - architecture: x64 fname: LunaTranslator - debug: 0 - - architecture: x86 - fname: LunaTranslator_x86_debug - debug: 1 - - architecture: x64 - fname: LunaTranslator_debug - debug: 1 steps: - name: Checkout code uses: actions/checkout@v4 @@ -45,7 +37,7 @@ jobs: run: pip install cmake pefile requests - name: Run build script run: | - python build.py ${{ matrix.architecture }} ${{ matrix.debug }} + python build.py ${{ matrix.architecture }} - uses: actions/upload-artifact@v4 with: name: ${{ matrix.fname }} diff --git a/LunaTranslator/LunaTranslator/keeprefs.py b/LunaTranslator/LunaTranslator/keeprefs.py index a4b78c0d..d7c5a669 100644 --- a/LunaTranslator/LunaTranslator/keeprefs.py +++ b/LunaTranslator/LunaTranslator/keeprefs.py @@ -3,3 +3,4 @@ import pytz import uuid import xml.etree.ElementTree as ET import hashlib +import configparser \ No newline at end of file diff --git a/LunaTranslator/pack32.cmd b/LunaTranslator/pack32.cmd index cf7df2e1..e28a1e24 100644 --- a/LunaTranslator/pack32.cmd +++ b/LunaTranslator/pack32.cmd @@ -1 +1,2 @@ -python pack.py 1 \ No newline at end of file +@REM python pack.py 1 +%LOCALAPPDATA%\Programs\Python\Python37-32\python.exe retrieval.py 1 \ No newline at end of file diff --git a/LunaTranslator/pack64.cmd b/LunaTranslator/pack64.cmd index 7a1d8805..d2753ef5 100644 --- a/LunaTranslator/pack64.cmd +++ b/LunaTranslator/pack64.cmd @@ -1 +1,2 @@ -python pack.py 0 \ No newline at end of file +@REM python pack.py 0 +%LOCALAPPDATA%\Programs\Python\Python37\python.exe retrieval.py 0 \ No newline at end of file diff --git a/LunaTranslator/requirements.txt b/LunaTranslator/requirements.txt index 13160f57..90fe72e0 100644 --- a/LunaTranslator/requirements.txt +++ b/LunaTranslator/requirements.txt @@ -1,6 +1,4 @@ PyQt5==5.15.10 PyQt5-Qt5==5.15.2 -webviewpy -nuitka==2.2 -imageio +webviewpy==1.2.0 pefile \ No newline at end of file diff --git a/LunaTranslator/retrieval.py b/LunaTranslator/retrieval.py new file mode 100644 index 00000000..b0b66182 --- /dev/null +++ b/LunaTranslator/retrieval.py @@ -0,0 +1,275 @@ +import modulefinder, shutil, os, sys, pefile +import builtins + +x86 = int(sys.argv[1]) if len(sys.argv) > 1 else 0 +if x86: + downlevel = r"C:\Windows\SysWOW64\downlevel" + src = py37Pathlocal = ( + os.environ["LOCALAPPDATA"] + r"\Programs\Python\Python37-32" + "\\" + ) + tgt = r"..\build\LunaTranslator_x86\LunaTranslator\runtime" + targetdir = r"..\build\LunaTranslator_x86" + launch = r"..\plugins\builds\_x86" + baddll = "DLL64" + gooddll = "DLL32" + py37Path = "C:\\hostedtoolcache\\windows\\Python\\3.7.9\\x86\\python.exe" + py37Pathlocal = ( + os.environ["LOCALAPPDATA"] + r"\Programs\Python\Python37-32\python.exe" + ) + webviewappendix = r"Lib\site-packages\webviewpy\platform\win32\x86\webview.dll" +else: + baddll = "DLL32" + gooddll = "DLL64" + launch = r"..\plugins\builds\_x64" + tgt = r"..\build\LunaTranslator\LunaTranslator\runtime" + targetdir = r"..\build\LunaTranslator" + downlevel = r"C:\Windows\system32\downlevel" + src = py37Pathlocal = ( + os.environ["LOCALAPPDATA"] + r"\Programs\Python\Python37" + "\\" + ) + py37Path = "C:\\hostedtoolcache\\windows\\Python\\3.7.9\\x64\\python.exe" + py37Pathlocal = os.environ["LOCALAPPDATA"] + r"\Programs\Python\Python37\python.exe" + webviewappendix = r"Lib\site-packages\webviewpy\platform\win32\x64\webview.dll" +if os.path.exists(py37Path) == False: + py37Path = py37Pathlocal +py37Pathwebview = os.path.join(os.path.dirname(py37Path), webviewappendix) + +saveopen = builtins.open + + +def __open(*arg, **kwarg): + if len(arg) > 1: + mode = arg[1] + else: + mode = "" + if "b" not in mode: + kwarg["encoding"] = "utf8" + return saveopen(*arg, **kwarg) + + +builtins.open = __open + + +def get_import_table(file_path): + pe = pefile.PE(file_path) + import_dlls = [] + if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"): + for entry in pe.DIRECTORY_ENTRY_IMPORT: + dll_name = entry.dll.decode("utf-8") + import_dlls.append(dll_name) + return import_dlls + + +def get_dependencies(filename): + finder = modulefinder.ModuleFinder() + + finder.run_script(filename) + + dependencies = [] + for name, module in finder.modules.items(): + if module.__file__ is not None: + dependencies.append(module.__file__) + + return dependencies + + +def copycheck(src, tgt): + if not os.path.exists(src): + return + if src.lower().endswith("_ssl.pyd"): + return + if not os.path.exists(tgt): + os.makedirs(tgt, exist_ok=True) + if os.path.isdir(src): + tgt = os.path.join(tgt, os.path.basename(src)) + if os.path.exists(tgt): + shutil.rmtree(tgt) + shutil.copytree(src, tgt) + return + shutil.copy(src, tgt) + + +if os.path.exists(targetdir): + shutil.rmtree(targetdir) +copycheck(os.path.join(launch, "LunaTranslator.exe"), targetdir) +copycheck(os.path.join(launch, "LunaTranslator_admin.exe"), targetdir) +copycheck(os.path.join(launch, "LunaTranslator_debug.exe"), targetdir) +copycheck("./LunaTranslator", targetdir) +copycheck(r".\files", targetdir) +try: + shutil.rmtree(rf"{targetdir}\files\plugins\{baddll}") +except: + pass +shutil.copy(r"..\LICENSE", targetdir) +shutil.copy(py37Pathwebview, rf"{targetdir}\files\plugins\{gooddll}") + +all_dependencies = set() +for _d, _, _fs in os.walk("./LunaTranslator"): + for f in _fs: + if not f.endswith(".py"): + continue + base = os.path.basename(_d) + if base in [ + "tts", + "transoptimi", + "translator", + "scalemethod", + "ocrengines", + "winhttp", + "libcurl", + "network", + "hiraparse", + "cishu", + "textoutput", + ]: + continue + print(base, f) + got = get_dependencies(os.path.join(_d, f)) + all_dependencies = all_dependencies.union(set(got)) + +for dependency in all_dependencies: + if dependency.startswith("./"): + continue + print(dependency) + end = dependency[len(src) :] + if end.lower().startswith("lib"): + end = end[4:] + if end.lower().startswith("site-packages"): + end = end[len("site-packages") + 1 :] + elif end.lower().startswith("dlls"): + end = end[5:] + tgtreal = os.path.dirname(os.path.join(tgt, end)) + copycheck(dependency, tgtreal) + + +with open(os.path.join(tgt, "python37._pth"), "w") as ff: + ff.write(".\n..") + +copycheck(os.path.join(src, "python3.dll"), tgt) +copycheck(os.path.join(src, "python37.dll"), tgt) +copycheck(os.path.join(src, "Dlls/sqlite3.dll"), tgt) + +copycheck(os.path.join(src, "Lib/encodings"), os.path.join(tgt)) + + +copycheck(rf"{downlevel}\ucrtbase.dll", tgt) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/vcruntime140.dll"), + os.path.join(tgt), +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/vcruntime140_1.dll"), + os.path.join(tgt), +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/msvcp140.dll"), os.path.join(tgt) +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/Qt5/msvcp140_1.dll"), + os.path.join(tgt), +) + + +for _ in os.listdir(os.path.join(src, "Lib/site-packages/PyQt5")): + if _.startswith("sip"): + copycheck( + os.path.join(src, "Lib/site-packages/PyQt5", _), os.path.join(tgt, "PyQt5") + ) + +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/Qt5Core.dll"), + os.path.join(tgt, "PyQt5/Qt5/bin"), +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/Qt5Gui.dll"), + os.path.join(tgt, "PyQt5/Qt5/bin"), +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/bin/Qt5Widgets.dll"), + os.path.join(tgt, "PyQt5/Qt5/bin"), +) + +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/plugins/imageformats"), + os.path.join(tgt, "PyQt5/Qt5/plugins"), +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/plugins/platforms/qoffscreen.dll"), + os.path.join(tgt, "PyQt5/Qt5/plugins/platforms"), +) +copycheck( + os.path.join(src, "Lib/site-packages/PyQt5/Qt5/plugins/platforms/qwindows.dll"), + os.path.join(tgt, "PyQt5/Qt5/plugins/platforms"), +) +copycheck( + os.path.join( + src, "Lib/site-packages/PyQt5/Qt5/plugins/styles/qwindowsvistastyle.dll" + ), + os.path.join(tgt, "PyQt5/Qt5/plugins/styles"), +) + +collect = [] +for _dir, _, fs in os.walk(targetdir): + for f in fs: + collect.append(os.path.join(_dir, f)) +for src in collect: + if src.endswith(".pyc") or src.endswith("Thumbs.db"): + os.remove(f) + + elif src.lower().endswith(".pyd") or src.lower().endswith(".dll"): + + if src.endswith("QtWidgets.pyd"): + imports = [ + "api-ms-win-crt-runtime-l1-1-0.dll", + "api-ms-win-crt-heap-l1-1-0.dll", + ] + else: + imports = get_import_table(src) + print(src, imports) + if len(imports) == 0: + continue + with open(src, "rb") as ff: + bs = bytearray(ff.read()) + for _dll in imports: + if _dll.lower().startswith("api-ms-win-core"): + # 其实对于api-ms-win-core-winrt-XXX实际上是到ComBase.dll之类的,不过此项目中不包含这些 + _target = "kernel32.dll" + elif _dll.lower().startswith("api-ms-win-crt"): + _target = "ucrtbase.dll" + else: + continue + _dll = _dll.encode() + _target = _target.encode() + idx = bs.find(_dll) + bs[idx : idx + len(_dll)] = _target + b"\0" * (len(_dll) - len(_target)) + with open(os.path.join(tgt, os.path.basename(src)), "wb") as ff: + ff.write(bs) + +target = os.path.basename(targetdir) +if os.path.exists(rf"{targetdir}\..\{target}.zip"): + os.remove(rf"{targetdir}\..\{target}.zip") +if os.path.exists(rf"{targetdir}\..\{target}.7z"): + os.remove(rf"{targetdir}\..\{target}.7z") +os.system( + rf'"C:\Program Files\7-Zip\7z.exe" a -m0=Deflate -mx9 {targetdir}\..\{target}.zip {targetdir}' +) +os.system( + rf'"C:\Program Files\7-Zip\7z.exe" a -m0=LZMA2 -mx9 {targetdir}\..\{target}.7z {targetdir}' +) + +with open(r"C:\Program Files\7-Zip\7z.sfx", "rb") as ff: + sfx = ff.read() + +config = """ +;!@Install@!UTF-8! + + +;!@InstallEnd@! +""" +with open(rf"{targetdir}\..\{target}.7z", "rb") as ff: + data = ff.read() + +with open(rf"{targetdir}\..\{target}.exe", "wb") as ff: + ff.write(sfx) + ff.write(config.encode("utf8")) + ff.write(data) diff --git a/build.py b/build.py index b25fac36..73232986 100644 --- a/build.py +++ b/build.py @@ -242,26 +242,7 @@ if __name__ == "__main__": os.chdir(rootDir + "\\LunaTranslator") - cmdline=py37Path - cmdline+=' -m nuitka --standalone --assume-yes-for-downloads ' - if not isdebug: - cmdline+=' --disable-console ' - cmdline+=' --plugin-enable=pyqt5 ' - - if arch == "x86": - if isdebug: - cmdline+='--output-dir=..\\build\\x86_debug' - else: - cmdline+='--output-dir=..\\build\\x86' - else: - if isdebug: - cmdline+='--output-dir=..\\build\\x64_debug' - else: - cmdline+='--output-dir=..\\build\\x64' - cmdline+=' LunaTranslator\\LunaTranslator_main.py --windows-icon-from-ico=..\\plugins\\exec\\luna.ico ' - subprocess.run(f"{py37Path} -m pip install --upgrade pip") subprocess.run(f"{py37Path} -m pip install -r requirements.txt") - subprocess.run(cmdline) - subprocess.run(f'python pack.py {int(arch == "x86")} {int(isdebug)}') \ No newline at end of file + subprocess.run(f'{py37Path} retrieval.py {int(arch == "x86")}') \ No newline at end of file diff --git a/plugins/exec/CMakeLists.txt b/plugins/exec/CMakeLists.txt index 02857834..9f6f7fe8 100644 --- a/plugins/exec/CMakeLists.txt +++ b/plugins/exec/CMakeLists.txt @@ -1,13 +1,13 @@ project(LUNA) - -add_executable(LunaTranslator luna.cpp luna.rc) -#target_link_options(LunaTranslator PRIVATE /MANIFESTUAC:level="asInvoker") +option(PYSTAND_CONSOLE "Build PyStand as a console application." OFF) -add_executable(LunaTranslator_admin luna.cpp luna.rc) -set_target_properties(LunaTranslator_admin PROPERTIES LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" ") +# sources +set(sources PyStand.cpp luna.rc) -#target_link_options(LunaTranslator_admin PRIVATE /MANIFESTUAC:level="requireAdministrator") -#这个仅在visualstdio下管用,vscode cmake不管用了。 - \ No newline at end of file +add_executable(LunaTranslator_debug ${sources}) +target_compile_definitions(LunaTranslator_debug PRIVATE PYSTAND_CONSOLE) +add_executable(LunaTranslator WIN32 ${sources}) +add_executable(LunaTranslator_admin WIN32 ${sources}) +set_target_properties(LunaTranslator_admin PROPERTIES LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" ") \ No newline at end of file diff --git a/plugins/exec/PyStand.cpp b/plugins/exec/PyStand.cpp new file mode 100644 index 00000000..aa0d42a6 --- /dev/null +++ b/plugins/exec/PyStand.cpp @@ -0,0 +1,414 @@ +//===================================================================== +// +// PyStand.cpp - +// +// Created by skywind on 2022/02/03 +// Last Modified: 2023/03/17 20:06 +// +//===================================================================== +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#include +#include +#include +#include +#include + +#include "PyStand.h" + +#ifdef _MSC_VER +#pragma comment(lib, "shlwapi.lib") +#endif + + +//--------------------------------------------------------------------- +// dtor +//--------------------------------------------------------------------- +PyStand::~PyStand() +{ + FreeLibrary(_hDLL); +} + + +//--------------------------------------------------------------------- +// ctor +//--------------------------------------------------------------------- +PyStand::PyStand(const wchar_t *runtime) +{ + _hDLL = NULL; + _Py_Main = NULL; + if (CheckEnviron(runtime) == false) { + exit(1); + } + if (LoadPython() == false) { + exit(2); + } +} + + +//--------------------------------------------------------------------- +// ctor for ansi +//--------------------------------------------------------------------- +PyStand::PyStand(const char *runtime) +{ + _hDLL = NULL; + _Py_Main = NULL; + std::wstring rtp = Ansi2Unicode(runtime); + if (CheckEnviron(rtp.c_str()) == false) { + exit(1); + } + if (LoadPython() == false) { + exit(2); + } +} + + +//--------------------------------------------------------------------- +// char to wchar_t +//--------------------------------------------------------------------- +std::wstring PyStand::Ansi2Unicode(const char *text) +{ + int len = (int)strlen(text); + std::wstring wide; + int require = MultiByteToWideChar(CP_ACP, 0, text, len, NULL, 0); + if (require > 0) { + wide.resize(require); + MultiByteToWideChar(CP_ACP, 0, text, len, &wide[0], require); + } + return wide; +} + + +//--------------------------------------------------------------------- +// init: _args, _argv, _cwd, _pystand, _home, _runtime, +//--------------------------------------------------------------------- +bool PyStand::CheckEnviron(const wchar_t *rtp) +{ + // init: _args, _argv + LPWSTR *argvw; + int argc; + _args = GetCommandLineW(); + argvw = CommandLineToArgvW(_args.c_str(), &argc); + if (argvw == NULL) { + MessageBoxA(NULL, "Error in CommandLineToArgvW()", "ERROR", MB_OK); + return false; + } + _argv.resize(argc); + for (int i = 0; i < argc; i++) { + _argv[i] = argvw[i]; + } + LocalFree(argvw); + + // init: _cwd (current working directory) + wchar_t path[MAX_PATH + 10]; + GetCurrentDirectoryW(MAX_PATH + 1, path); + _cwd = path; + + // init: _pystand (full path of PyStand.exe) + GetModuleFileNameW(NULL, path, MAX_PATH + 1); +#if 0 + wsprintf(path, L"e:\\github\\tools\\pystand\\pystand.exe"); +#endif + _pystand = path; + + // init: _home + int size = (int)wcslen(path); + for (; size > 0; size--) { + if (path[size - 1] == L'/') break; + if (path[size - 1] == L'\\') break; + } + path[size] = 0; + SetCurrentDirectoryW(path); + GetCurrentDirectoryW(MAX_PATH + 1, path); + _home = path; + SetCurrentDirectoryW(_cwd.c_str()); + + // init: _runtime (embedded python directory) + bool abspath = false; + if (wcslen(rtp) >= 3) { + if (rtp[1] == L':') { + if (rtp[2] == L'/' || rtp[2] == L'\\') + abspath = true; + } + } + if (abspath == false) { + _runtime = _home + L"\\" + rtp; + } + else { + _runtime = rtp; + } + GetFullPathNameW(_runtime.c_str(), MAX_PATH + 1, path, NULL); + _runtime = path; + + // check home + std::wstring check = _runtime; + if (!PathFileExistsW(check.c_str())) { + std::wstring msg = L"Missing embedded Python3 in:\n" + check; + MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK); + return false; + } + + // check python3.dll + std::wstring check2 = _runtime + L"\\python3.dll"; + if (!PathFileExistsW(check2.c_str())) { + std::wstring msg = L"Missing python3.dll in:\r\n" + check; + MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK); + return false; + } + + // setup environment + SetEnvironmentVariableW(L"PYSTAND", _pystand.c_str()); + SetEnvironmentVariableW(L"PYSTAND_HOME", _home.c_str()); + SetEnvironmentVariableW(L"PYSTAND_RUNTIME", _runtime.c_str()); + + // unnecessary to init PYSTAND_SCRIPT here. +#if 0 + SetEnvironmentVariableW(L"PYSTAND_SCRIPT", _script.c_str()); +#endif + +#if 0 + wprintf(L"%s - %s\n", _pystand.c_str(), path); + MessageBoxW(NULL, _pystand.c_str(), _home.c_str(), MB_OK); +#endif + + return true; +} + + +//--------------------------------------------------------------------- +// load python +//--------------------------------------------------------------------- +bool PyStand::LoadPython() +{ + std::wstring runtime = _runtime; + std::wstring previous; + + // save current directory + wchar_t path[MAX_PATH + 10]; + GetCurrentDirectoryW(MAX_PATH + 1, path); + previous = path; + + // python dll must be load under "runtime" + SetCurrentDirectoryW(runtime.c_str()); + // LoadLibrary + _hDLL = (HINSTANCE)LoadLibraryA("python3.dll"); + if (_hDLL) { + _Py_Main = (t_Py_Main)GetProcAddress(_hDLL, "Py_Main"); + } + + // restore director + SetCurrentDirectoryW(previous.c_str()); + + if (_hDLL == NULL) { + std::wstring msg = L"Cannot load python3.dll from:\r\n" + runtime; + MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK); + return false; + } + else if (_Py_Main == NULL) { + std::wstring msg = L"Cannot find Py_Main() in:\r\n"; + msg += runtime + L"\\python3.dll"; + MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK); + return false; + } + return true; +} + + +//--------------------------------------------------------------------- +// run string +//--------------------------------------------------------------------- +int PyStand::RunString(const wchar_t *script) +{ + if (_Py_Main == NULL) { + return -1; + } + int hr = 0; + int i; + _py_argv.resize(0); + // init arguments + _py_argv.push_back(_argv[0]); + _py_argv.push_back(L"-I"); + _py_argv.push_back(L"-s"); + _py_argv.push_back(L"-S"); + _py_argv.push_back(L"-c"); + _py_argv.push_back(script); + for (i = 1; i < (int)_argv.size(); i++) { + _py_argv.push_back(_argv[i]); + } + // finalize arguments + _py_args.resize(0); + for (i = 0; i < (int)_py_argv.size(); i++) { + _py_args.push_back((wchar_t*)_py_argv[i].c_str()); + } + hr = _Py_Main((int)_py_args.size(), &_py_args[0]); + return hr; +} + + +//--------------------------------------------------------------------- +// run ansi string +//--------------------------------------------------------------------- +int PyStand::RunString(const char *script) +{ + std::wstring text = Ansi2Unicode(script); + return RunString(text.c_str()); +} + + + +//--------------------------------------------------------------------- +// static init script +//--------------------------------------------------------------------- +#ifndef PYSTAND_STATIC_NAME +#define PYSTAND_STATIC_NAME "LunaTranslator\\LunaTranslator_main.py" +#endif + + +//--------------------------------------------------------------------- +// LoadScript() +//--------------------------------------------------------------------- +int PyStand::DetectScript() +{ + // init: _script (init script like PyStand.int or PyStand.py) + int size = (int)_pystand.size() - 1; + for (; size >= 0; size--) { + if (_pystand[size] == L'.') break; + } + if (size < 0) size = (int)_pystand.size(); + std::wstring main = _pystand.substr(0, size); + std::vector exts; + std::vector scripts; + _script.clear(); +#if !(PYSTAND_DISABLE_STATIC) + std::wstring test; + test = _home + L"\\" + Ansi2Unicode(PYSTAND_STATIC_NAME); + if (PathFileExistsW(test.c_str())) { + _script = test; + } +#endif + if (_script.empty()) { + exts.push_back(L".int"); + exts.push_back(L".py"); + exts.push_back(L".pyw"); + for (int i = 0; i < (int)exts.size(); i++) { + std::wstring test = main + exts[i]; + scripts.push_back(test); + if (PathFileExistsW(test.c_str())) { + _script = test; + break; + } + } + if (_script.size() == 0) { + std::wstring msg = L"Can't find either of:\r\n"; + for (int j = 0; j < (int)scripts.size(); j++) { + msg += scripts[j] + L"\r\n"; + } + MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK); + return -1; + } + } + SetEnvironmentVariableW(L"PYSTAND_SCRIPT", _script.c_str()); + return 0; +} + + +//--------------------------------------------------------------------- +// init script +//--------------------------------------------------------------------- +const char *init_script = +"import sys\n" +"import os\n" +"PYSTAND = os.environ['PYSTAND']\n" +"PYSTAND_HOME = os.environ['PYSTAND_HOME']\n" +"PYSTAND_RUNTIME = os.environ['PYSTAND_RUNTIME']\n" +"PYSTAND_SCRIPT = os.environ['PYSTAND_SCRIPT']\n" +"sys.path_origin = [n for n in sys.path]\n" +"sys.PYSTAND = PYSTAND\n" +"sys.PYSTAND_HOME = PYSTAND_HOME\n" +"sys.PYSTAND_SCRIPT = PYSTAND_SCRIPT\n" +"def MessageBox(msg, info = 'Message'):\n" +" import ctypes\n" +" ctypes.windll.user32.MessageBoxW(None, str(msg), str(info), 0)\n" +" return 0\n" +"os.MessageBox = MessageBox\n" +#ifndef PYSTAND_CONSOLE +"try:\n" +" fd = os.open('CONOUT$', os.O_RDWR | os.O_BINARY)\n" +" fp = os.fdopen(fd, 'w')\n" +" sys.stdout = fp\n" +" sys.stderr = fp\n" +" attached = True\n" +"except Exception as e:\n" +" fp = open(os.devnull, 'w')\n" +" sys.stdout = fp\n" +" sys.stderr = fp\n" +" attached = False\n" +#endif +"sys.argv = [PYSTAND_SCRIPT] + sys.argv[1:]\n" +"text = open(PYSTAND_SCRIPT, 'rb').read()\n" +"environ = {'__file__': PYSTAND_SCRIPT, '__name__': '__main__'}\n" +"environ['__package__'] = None\n" +#ifndef PYSTAND_CONSOLE +"try:\n" +" code = compile(text, PYSTAND_SCRIPT, 'exec')\n" +" exec(code, environ)\n" +"except Exception:\n" +" if attached:\n" +" raise\n" +" import traceback, io\n" +" sio = io.StringIO()\n" +" traceback.print_exc(file = sio)\n" +" os.MessageBox(sio.getvalue(), 'Error')\n" +#else +"code = compile(text, PYSTAND_SCRIPT, 'exec')\n" +"exec(code, environ)\n" +#endif +""; + + +//--------------------------------------------------------------------- +// main +//--------------------------------------------------------------------- + +//! flag: -static +//! src: +//! link: stdc++, shlwapi, resource.o +//! prebuild: windres resource.rc -o resource.o +//! mode: win +//! int: objs + +#ifdef PYSTAND_CONSOLE +int main() +#else +int WINAPI +WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int show) +#endif +{ + PyStand ps("LunaTranslator\\runtime"); + if (ps.DetectScript() != 0) { + return 3; + } +#ifndef PYSTAND_CONSOLE + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + int fd = _fileno(stdout); + if (fd >= 0) { + std::string fn = std::to_string(fd); + SetEnvironmentVariableA("PYSTAND_STDOUT", fn.c_str()); + } + fd = _fileno(stdin); + if (fd >= 0) { + std::string fn = std::to_string(fd); + SetEnvironmentVariableA("PYSTAND_STDIN", fn.c_str()); + } + } +#endif + int hr = ps.RunString(init_script); + // printf("finalize\n"); + return hr; +} + + diff --git a/plugins/exec/PyStand.h b/plugins/exec/PyStand.h new file mode 100644 index 00000000..40a1194b --- /dev/null +++ b/plugins/exec/PyStand.h @@ -0,0 +1,61 @@ +//===================================================================== +// +// PyStand.h - +// +// Created by skywind on 2022/02/03 +// Last Modified: 2022/02/03 23:39:52 +// +//===================================================================== +#ifndef _PYSTAND_H_ +#define _PYSTAND_H_ + +#include +#include +#include +#include +#include + + +//--------------------------------------------------------------------- +// PyStand +//--------------------------------------------------------------------- +class PyStand +{ +public: + virtual ~PyStand(); + PyStand(const wchar_t *runtime); + PyStand(const char *runtime); + +public: + std::wstring Ansi2Unicode(const char *text); + + int RunString(const wchar_t *script); + int RunString(const char *script); + + int DetectScript(); + +protected: + bool CheckEnviron(const wchar_t *rtp); + bool LoadPython(); + + +protected: + typedef int (*t_Py_Main)(int argc, wchar_t **argv); + t_Py_Main _Py_Main; + +protected: + HINSTANCE _hDLL; + std::wstring _cwd; // current working directory + std::wstring _args; // arguments + std::wstring _pystand; // absolute path of pystand + std::wstring _runtime; // absolute path of embedded python runtime + std::wstring _home; // home directory of PyStand.exe + std::wstring _script; // init script like PyStand.int or PyStand.py + std::vector _argv; + std::vector _py_argv; + std::vector _py_args; +}; + + +#endif + diff --git a/plugins/exec/luna.cpp b/plugins/exec/luna.cpp deleted file mode 100644 index 89d5892f..00000000 --- a/plugins/exec/luna.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include -#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup") - -int main() -{ - TCHAR szPath[MAX_PATH]; - GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)); - std::wstring moduleName = szPath; - auto currpath = moduleName.substr(0, moduleName.rfind(L'\\')); - auto exe = currpath + L".\\LunaTranslator\\LunaTranslator_main.exe"; - if (!std::filesystem::exists(exe)) - { - MessageBoxW(0, (L"Can't find LunaTranslator\\LunaTranslator_main.exe, please download again."), L"Error", 0); - return 0; - } - STARTUPINFO _1 = {}; - PROCESS_INFORMATION _2; - CreateProcessW(exe.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, currpath.c_str(), &_1, &_2); - return 0; -}