From e3faa6f53b37393278d607cce8fbd00a2732ccdc Mon Sep 17 00:00:00 2001 From: kotofyt Date: Fri, 2 Jan 2026 01:56:22 +0200 Subject: [PATCH] now can package ipa, still problems with signing --- fpc/build.cpp | 13 ++-- fpc/external/build.cpp | 57 +++++++++++++++++ fpc/external/zsign | 1 + fpc/library/apple/appletool.cpp | 92 ++++++++++++++++++++++++++++ fpc/library/builder.cpp | 2 +- fpc/library/clang/ld.cpp | 17 +++-- fpc/library/ld.cpp | 4 +- fpc/library/libfpc.cpp | 9 +++ fpc/library/windows/ld.cpp | 8 +-- fpc/public/appletool.h | 31 ++++++++++ fpc/public/deploy.h | 0 fpc/public/{signtool.h => iostool.h} | 0 fpc/public/ld.h | 4 +- fpc/public/signer.h | 11 ++++ fpc/public/sysrootfetch.h | 3 + fpc/public/target.h | 6 ++ fpc/tests/ios_build/.fpccfg | 2 + fpc/tests/ios_build/build.cpp | 29 +++++++++ fpc/tests/ios_build/main.c | 6 ++ public/tier2/ifilesystem.h | 7 +++ stdfilesystems/filesystem_libc.cpp | 31 ++++++++++ 21 files changed, 312 insertions(+), 21 deletions(-) create mode 100644 fpc/external/build.cpp create mode 160000 fpc/external/zsign create mode 100644 fpc/library/apple/appletool.cpp create mode 100644 fpc/public/appletool.h create mode 100644 fpc/public/deploy.h rename fpc/public/{signtool.h => iostool.h} (100%) create mode 100644 fpc/public/signer.h create mode 100644 fpc/public/sysrootfetch.h create mode 100644 fpc/tests/ios_build/.fpccfg create mode 100644 fpc/tests/ios_build/main.c diff --git a/fpc/build.cpp b/fpc/build.cpp index c909602..deafa28 100644 --- a/fpc/build.cpp +++ b/fpc/build.cpp @@ -3,11 +3,11 @@ #include "helper.h" #include "tier0/platform.h" #include "tier1/interface.h" -#include "signal.h" -ADD_DEPENDENCY_BUILD_FILE(tier0, "../tier0/"); -ADD_DEPENDENCY_BUILD_FILE(tier1, "../tier1/"); -ADD_DEPENDENCY_BUILD_FILE(tier2, "../tier2/"); +ADD_DEPENDENCY_BUILD_FILE(tier0, "../tier0/build.cpp"); +ADD_DEPENDENCY_BUILD_FILE(tier1, "../tier1/build.cpp"); +ADD_DEPENDENCY_BUILD_FILE(tier2, "../tier2/build.cpp"); +ADD_DEPENDENCY_BUILD_FILE(signers, "signers/build.cpp"); CUtlVector g_fpcFiles = { @@ -30,6 +30,7 @@ CUtlVector g_libFpcFiles = { "library/ld.cpp", "library/android/apktool.cpp", + "library/apple/appletool.cpp", "library/clang/c.cpp", "library/clang/ld.cpp", @@ -49,7 +50,7 @@ DECLARE_BUILD_STAGE(libfpcbuild) CProject_t compileProject = {}; LinkProject_t ldProject = {}; - compileProject.m_szName = "fpc"; + compileProject.m_szName = "fpcbuild"; compileProject.files = g_libFpcFiles; compileProject.includeDirectories = g_IncludeDirectories; compileProject.bFPIC = true; @@ -62,7 +63,7 @@ DECLARE_BUILD_STAGE(libfpcbuild) CUtlString outputProject = linker->Link(&ldProject); - ADD_OUTPUT_OBJECT("fpc", outputProject) + ADD_OUTPUT_OBJECT("fpcbuild", outputProject) return 0; } diff --git a/fpc/external/build.cpp b/fpc/external/build.cpp new file mode 100644 index 0000000..e95e5a8 --- /dev/null +++ b/fpc/external/build.cpp @@ -0,0 +1,57 @@ +#include "c.h" +#include "ld.h" +#include "helper.h" +#include "tier0/platform.h" +#include "tier1/interface.h" +#include "tier2/ifilesystem.h" + +DECLARE_BUILD_STAGE(zsign) +{ + if (!filesystem) + { + void *pFilesystem = Plat_LoadLibrary("libfilesystem_std.so"); + CreateInterfaceFn pFilesystemFactory = Sys_GetFactory(pFilesystem); + + filesystem = (IFileSystem*)pFilesystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL); + } + IDirectoryHandle *pDir = filesystem->OpenDir("zsign"); + if (!pDir) + return 0; + filesystem->CloseDir(pDir); + + CProject_t compileProject = {}; + LinkProject_t ldProject = {}; + CUtlString szOutputProject = ""; + + compileProject.m_szName = "zsign"; + compileProject.m_target = Target_t::HostTarget(); + compileProject.bFPIC = true; + compileProject.files = { + "zsign/src/common/archive.cpp", + "zsign/src/common/base64.cpp", + "zsign/src/common/fs.cpp", + "zsign/src/common/json.cpp", + "zsign/src/common/log.cpp", + "zsign/src/common/sha.cpp", + "zsign/src/common/timer.cpp", + "zsign/src/common/util.cpp", + "zsign/src/archo.cpp", + "zsign/src/bundle.cpp", + "zsign/src/macho.cpp", + "zsign/src/openssl.cpp", + "zsign/src/signing.cpp", + "zsign/src/zsign.cpp", + }; + compileProject.includeDirectories = { + "zsign/src/common", + "zsign/src", + }; + ldProject = ccompiler->Compile(&compileProject); + + ldProject.linkType = ELINK_DYNAMIC_LIBRARY; + szOutputProject = linker->Link(&ldProject); + + ADD_OUTPUT_OBJECT("zsign", szOutputProject); + + return 0; +} diff --git a/fpc/external/zsign b/fpc/external/zsign new file mode 160000 index 0000000..cb49b1d --- /dev/null +++ b/fpc/external/zsign @@ -0,0 +1 @@ +Subproject commit cb49b1d34e8e0e02aac82f3579dbd7936232499b diff --git a/fpc/library/apple/appletool.cpp b/fpc/library/apple/appletool.cpp new file mode 100644 index 0000000..be63fc3 --- /dev/null +++ b/fpc/library/apple/appletool.cpp @@ -0,0 +1,92 @@ +#include "appletool.h" +#include "helper.h" +#include "runner.h" + +void AppleManifest_t::SetPackageID( CUtlString szPackageID ) +{ + m_szPackageID = szPackageID; +} + +void AppleManifest_t::SetPackageName( CUtlString szPackageName ) +{ + m_szPackageName = szPackageName; +} + +void AppleManifest_t::SetPackageExecutable( CUtlString szPackageExecutable ) +{ + m_szPackageExecutable = szPackageExecutable; +} + +CUtlString AppleManifest_t::BuildManifest() +{ + CPUProject_t project = {}; + project.m_szName = m_szPackageName; + unsigned int hash = project.GenerateProjectHash(); + CUtlString szOutputDir = CUtlString("%s/apple/%u_%s/app/",FPC_TEMPORAL_DIRNAME, hash, m_szPackageID.GetString()); + filesystem2->MakeDirectory(szOutputDir); + filesystem2->CopyFile(szOutputDir, m_szPackageExecutable); + CUtlString szInfoPlist = CUtlString("%s/Info.plist", szOutputDir.GetString()); + FILE *pInfoPlistFile = V_fopen(szInfoPlist, "wb"); + + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "CFBundleIdentifier\n"); + V_fprintf(pInfoPlistFile, "%s\n", m_szPackageID.GetString()); + V_fprintf(pInfoPlistFile, "CFBundleName\n"); + V_fprintf(pInfoPlistFile, "%s\n", m_szPackageName.GetString()); + V_fprintf(pInfoPlistFile, "CFBundleDisplayName\n"); + V_fprintf(pInfoPlistFile, "%s\n", m_szPackageName.GetString()); + V_fprintf(pInfoPlistFile, "CFBundleExecutable\n"); + V_fprintf(pInfoPlistFile, "%s\n", m_szPackageExecutable.GetFileName().GetString()); + V_fprintf(pInfoPlistFile, "CFBundlePackageType\n"); + V_fprintf(pInfoPlistFile, "APPL\n"); + V_fprintf(pInfoPlistFile, "CFBundleVersion\n"); + V_fprintf(pInfoPlistFile, "1.0\n"); + V_fprintf(pInfoPlistFile, "CFBundleShortVersionString\n"); + V_fprintf(pInfoPlistFile, "1.0\n"); + V_fprintf(pInfoPlistFile, "LSRequiresIPhoneOS\n"); + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "UIDeviceFamily\n"); + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "1\n"); + V_fprintf(pInfoPlistFile, "2\n"); + V_fprintf(pInfoPlistFile, "\n"); + V_fprintf(pInfoPlistFile, "\n"); + + V_fclose(pInfoPlistFile); + return szOutputDir; +} + + +class CAppleTool: public IAppleTool +{ +public: + virtual CUtlString BuildPackage( AppleManifest_t manifest, CUtlString szManifestDir ) override; + virtual CUtlString SignPackage( const char *szIpa, const char *szPassword ) override; +}; +CUtlString CAppleTool::BuildPackage( AppleManifest_t manifest, CUtlString szManifestDir ) +{ + CUtlVector args = {}; + args = { + "-r", + CUtlString("../%s.ipa", manifest.m_szPackageName.GetString()), + CUtlString("."), + }; + runner->Run("zip",szManifestDir, args); + runner->Wait(); + return CUtlString("../%s.ipa", manifest.m_szPackageName.GetString()); +} + +CUtlString CAppleTool::SignPackage( const char *szIpa, const char *szPassword ) +{ + +} + + +IAppleTool *AppleTool() +{ + static CAppleTool s_tool; + return &s_tool; +} diff --git a/fpc/library/builder.cpp b/fpc/library/builder.cpp index 1ac2296..ee40597 100644 --- a/fpc/library/builder.cpp +++ b/fpc/library/builder.cpp @@ -103,7 +103,7 @@ BuildFile_t *CProjectBuilder::BuildProject( const char *szProjectName, const cha *pfnGetProjectObject = FPC_GetProjectObject; for (auto a: stBuildFileInfo.m_dependantFiles) { - BuildProject("something", CUtlString("%s/%s/build.cpp",szWd.GetString(),a)); + BuildProject("something", CUtlString("%s/%s",szWd.GetString(),a)); } Plat_SetWorkingDir(szWd); diff --git a/fpc/library/clang/ld.cpp b/fpc/library/clang/ld.cpp index 0014d4e..621a57f 100644 --- a/fpc/library/clang/ld.cpp +++ b/fpc/library/clang/ld.cpp @@ -33,10 +33,10 @@ protected: virtual void UseDynamicLookup( CUtlVector &cmd, bool bUse ) override; // includes whole file - virtual void UseFullFile( CUtlVector &cmd ) override; + virtual void UseFullFile( CUtlVector &cmd, LinkProject_t *pProject ) override; // includes used stuff in a file - virtual void UsePartialFile( CUtlVector &cmd ) override; + virtual void UsePartialFile( CUtlVector &cmd, LinkProject_t *pProject ) override; virtual void LinkFile( CUtlVector &cmd, const char *szName ) override; virtual void LinkLibraryObject( CUtlVector &cmd, const char *szName ) override; @@ -72,6 +72,9 @@ void CClangLinker::SetTarget( CUtlVector &cmd, LinkProject_t *pProje if (pProject->linkType == ELINK_DYNAMIC_LIBRARY) cmd.AppendTail("-shared"); + if (pProject->m_target.kernel == TARGET_KERNEL_IOS) + cmd.AppendTail("-fuse-ld=lld"); + cmd.AppendTail("-target"); cmd.AppendTail(pProject->m_target.GetTriplet()); } @@ -137,15 +140,17 @@ void CClangLinker::UseDynamicLookup( CUtlVector &cmd, bool bUse ) } -void CClangLinker::UseFullFile( CUtlVector &cmd ) +void CClangLinker::UseFullFile( CUtlVector &cmd, LinkProject_t *pProject ) { - cmd.AppendTail("-Wl,--whole-archive"); + if (pProject->m_target.kernel != TARGET_KERNEL_IOS) + cmd.AppendTail("-Wl,--whole-archive"); } -void CClangLinker::UsePartialFile( CUtlVector &cmd ) +void CClangLinker::UsePartialFile( CUtlVector &cmd, LinkProject_t *pProject ) { - cmd.AppendTail("-Wl,--no-whole-archive"); + if (pProject->m_target.kernel != TARGET_KERNEL_IOS) + cmd.AppendTail("-Wl,--no-whole-archive"); } diff --git a/fpc/library/ld.cpp b/fpc/library/ld.cpp index 56f0367..4a4585b 100644 --- a/fpc/library/ld.cpp +++ b/fpc/library/ld.cpp @@ -102,12 +102,12 @@ CUtlVector ILinker::BuildLinkCommandLine( LinkProject_t *pProject, c SetOutputFile(cmd, szOutputFileName); SetSysroot(cmd, pProject, NULL); SetDefaultLibraryPaths(cmd, pProject); - UseFullFile(cmd); + UseFullFile(cmd, pProject); for (auto &o: pProject->objects) { LinkFile(cmd, o.m_szObjectFile); } - UsePartialFile(cmd); + UsePartialFile(cmd, pProject); for (auto &o: pProject->libraryObjects) { diff --git a/fpc/library/libfpc.cpp b/fpc/library/libfpc.cpp index 1f05ebb..13114e8 100644 --- a/fpc/library/libfpc.cpp +++ b/fpc/library/libfpc.cpp @@ -2,6 +2,9 @@ #include "runner.h" #include "c.h" #include "ld.h" +#include "tier2/ifilesystem.h" + +IFileSystem *filesystem; void *LibFpcInit() { @@ -9,6 +12,12 @@ void *LibFpcInit() runner = (IRunner*)CreateInterface(RUNNER_INTERFACE_NAME, NULL); ccompiler = (ICCompiler*)CreateInterface(CLANG_C_COMPILER_INTERFACE_NAME, NULL); linker = (ILinker*)CreateInterface(CLANG_LINKER_INTERFACE_NAME, NULL); + + void *pFilesystem = Plat_LoadLibrary("libfilesystem_std.so"); + CreateInterfaceFn pFilesystemFactory = Sys_GetFactory(pFilesystem); + filesystem = (IFileSystem*)pFilesystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL); + filesystem->Init(); + V_printf("----- %p\n",filesystem); return NULL; } diff --git a/fpc/library/windows/ld.cpp b/fpc/library/windows/ld.cpp index c91d27f..6e03dd2 100644 --- a/fpc/library/windows/ld.cpp +++ b/fpc/library/windows/ld.cpp @@ -32,10 +32,10 @@ protected: virtual void UseDynamicLookup( CUtlVector &cmd, bool bUse ) override; // includes whole file - virtual void UseFullFile( CUtlVector &cmd ) override; + virtual void UseFullFile( CUtlVector &cmd, LinkProject_t *pProject ) override; // includes used stuff in a file - virtual void UsePartialFile( CUtlVector &cmd ) override; + virtual void UsePartialFile( CUtlVector &cmd, LinkProject_t *pProject ) override; virtual void LinkFile( CUtlVector &cmd, const char *szName ) override; virtual void LinkLibraryObject( CUtlVector &cmd, const char *szName ) override; @@ -81,13 +81,13 @@ void CMSVCLinker::UseDynamicLookup( CUtlVector &cmd, bool bUse ) } -void CMSVCLinker::UseFullFile( CUtlVector &cmd ) +void CMSVCLinker::UseFullFile( CUtlVector &cmd, LinkProject_t *pProject ) { } -void CMSVCLinker::UsePartialFile( CUtlVector &cmd ) +void CMSVCLinker::UsePartialFile( CUtlVector &cmd, LinkProject_t *pProject ) { } diff --git a/fpc/public/appletool.h b/fpc/public/appletool.h new file mode 100644 index 0000000..76abd7e --- /dev/null +++ b/fpc/public/appletool.h @@ -0,0 +1,31 @@ +#ifndef Apple_TOOL_H +#define Apple_TOOL_H + +#include "tier0/platform.h" +#include "tier1/utlstring.h" +#include "legal.h" + +struct AppleManifest_t +{ +public: + void SetPackageID( CUtlString szPackageID ); + void SetPackageName( CUtlString szPackageName ); + void SetPackageExecutable( CUtlString szPackageExecutable ); + + CUtlString BuildManifest(); + + CUtlString m_szPackageName; + CUtlString m_szPackageID; + CUtlString m_szPackageExecutable; +}; + +abstract_class IAppleTool +{ +public: + virtual CUtlString BuildPackage( AppleManifest_t manifest, CUtlString szManifestDir ) = 0; + virtual CUtlString SignPackage( const char *szIpa, const char *szPassword ) = 0; +}; + +IAppleTool *AppleTool(); +#endif + diff --git a/fpc/public/deploy.h b/fpc/public/deploy.h new file mode 100644 index 0000000..e69de29 diff --git a/fpc/public/signtool.h b/fpc/public/iostool.h similarity index 100% rename from fpc/public/signtool.h rename to fpc/public/iostool.h diff --git a/fpc/public/ld.h b/fpc/public/ld.h index 16d9bc2..ae3c89d 100644 --- a/fpc/public/ld.h +++ b/fpc/public/ld.h @@ -121,10 +121,10 @@ protected: virtual void UseDynamicLookup( CUtlVector &cmd, bool bUse ) = 0; // includes whole file - virtual void UseFullFile( CUtlVector &cmd ) = 0; + virtual void UseFullFile( CUtlVector &cmd, LinkProject_t *pProject ) = 0; // includes used stuff in a file - virtual void UsePartialFile( CUtlVector &cmd ) = 0; + virtual void UsePartialFile( CUtlVector &cmd, LinkProject_t *pProject ) = 0; virtual void LinkFile( CUtlVector &cmd, const char *szName ) = 0; virtual void LinkLibraryObject( CUtlVector &cmd, const char *szName ) = 0; diff --git a/fpc/public/signer.h b/fpc/public/signer.h new file mode 100644 index 0000000..d822e08 --- /dev/null +++ b/fpc/public/signer.h @@ -0,0 +1,11 @@ +#ifndef SIGNER_H +#define SIGNER_H + +#include "c.h" +#include "ld.h" +#include "helper.h" +#include "tier0/platform.h" +#include "tier1/interface.h" +#include "tier0/platform.h" + +#endif diff --git a/fpc/public/sysrootfetch.h b/fpc/public/sysrootfetch.h new file mode 100644 index 0000000..50c55b1 --- /dev/null +++ b/fpc/public/sysrootfetch.h @@ -0,0 +1,3 @@ +#ifndef SYSROOT_FETCH_H +#define SYSROOT_FETCH_H +#endif diff --git a/fpc/public/target.h b/fpc/public/target.h index eac8229..6c90285 100644 --- a/fpc/public/target.h +++ b/fpc/public/target.h @@ -12,12 +12,18 @@ enum ETargetKernel { TARGET_KERNEL_UNKNOWN = 0, TARGET_KERNEL_LINUX, + + TARGET_KERNEL_WINDOWS_DEVICES = 0x100, TARGET_KERNEL_WINDOWS_MSVC, TARGET_KERNEL_WINDOWS_GNU, TARGET_KERNEL_WINDOWS = TARGET_KERNEL_WINDOWS_GNU, + + TARGET_KERNEL_APPLE_DEVICES = 0x200, TARGET_KERNEL_DARWIN, TARGET_KERNEL_IOS, TARGET_KERNEL_ANDROID, + + TARGET_KERNEL_WEB_DEVICES = 0x400, TARGET_KERNEL_WASI, TARGET_KERNEL_EMSCRIPTEN, }; diff --git a/fpc/tests/ios_build/.fpccfg b/fpc/tests/ios_build/.fpccfg new file mode 100644 index 0000000..0b63401 --- /dev/null +++ b/fpc/tests/ios_build/.fpccfg @@ -0,0 +1,2 @@ +[aarch64-apple-ios] +sysroot = "/home/kotofyt/clones/yay/xcode/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk" diff --git a/fpc/tests/ios_build/build.cpp b/fpc/tests/ios_build/build.cpp index e69de29..a4d99fb 100644 --- a/fpc/tests/ios_build/build.cpp +++ b/fpc/tests/ios_build/build.cpp @@ -0,0 +1,29 @@ +#include "helper.h" +#include "c.h" +#include "ld.h" +#include "tier1/utlstring.h" +#include "appletool.h" + +DECLARE_BUILD_STAGE(ios_build) +{ + V_printf("Cool\n"); + CProject_t compileProject = {}; + compileProject.m_szName = "ios_app"; + compileProject.m_target = Target_t::HostTarget(); + compileProject.m_target.kernel = TARGET_KERNEL_IOS; + compileProject.m_target.cpu = TARGET_CPU_AARCH64; + compileProject.files = { + "main.c", + }; + LinkProject_t ldProject = ccompiler->Compile(&compileProject); + CUtlString szOutput = linker->Link(&ldProject); + + AppleManifest_t manifest = {}; + manifest.SetPackageName("FPC Testing facility"); + manifest.SetPackageID("com.example.testfpc"); + manifest.SetPackageExecutable(szOutput); + CUtlString szIpa = AppleTool()->BuildPackage( manifest, manifest.BuildManifest() ); + CUtlString szPackage = AppleTool()->SignPackage(szIpa, NULL); + + return 0; +} diff --git a/fpc/tests/ios_build/main.c b/fpc/tests/ios_build/main.c new file mode 100644 index 0000000..0734f35 --- /dev/null +++ b/fpc/tests/ios_build/main.c @@ -0,0 +1,6 @@ +#include "stdio.h" +int main() +{ + printf("Hello, world!\n"); + return 0; +}; diff --git a/public/tier2/ifilesystem.h b/public/tier2/ifilesystem.h index 2ece1b2..75f1721 100644 --- a/public/tier2/ifilesystem.h +++ b/public/tier2/ifilesystem.h @@ -27,6 +27,9 @@ public: IFileSystem *m_pFileSystem; }; +abstract_class IDirectoryHandle +{ +}; //---------------------------------------------------------------------------- // IFileSystem is an app system which manages files, could have different @@ -53,6 +56,10 @@ public: // Leaks memory // Should be cleaned by the user virtual const char *ReadString( IFileHandle *pFile ) = 0; + + // Directory stuff + virtual IDirectoryHandle *OpenDir( const char *szDirName ) = 0; + virtual void CloseDir( IDirectoryHandle *pDir ) = 0; IFileSystem *m_pNext; diff --git a/stdfilesystems/filesystem_libc.cpp b/stdfilesystems/filesystem_libc.cpp index 78a3f84..65e57be 100644 --- a/stdfilesystems/filesystem_libc.cpp +++ b/stdfilesystems/filesystem_libc.cpp @@ -2,6 +2,7 @@ #include "tier1/interface.h" #include "tier0/lib.h" #include "errno.h" +#include "dirent.h" class CLIBCFileHandle : public IFileHandle { @@ -11,6 +12,12 @@ public: }; +class CLIBCDirectoryHandle: public IDirectoryHandle +{ +public: + DIR *m_pDir; +}; + class CLIBCFileSystem : public IFileSystem { public: @@ -129,6 +136,30 @@ public: return szData; }; + + virtual IDirectoryHandle *OpenDir( const char *szDirName ) override + { + CLIBCDirectoryHandle *pHandle = NULL; + DIR *pDir = opendir(szDirName); + if (pDir == NULL) + return NULL; + + pHandle = new CLIBCDirectoryHandle; + pHandle->m_pDir = pDir; + return pHandle; + + }; + + virtual void CloseDir( IDirectoryHandle *pDir ) override + { + if (!pDir) + return; + CLIBCDirectoryHandle *pCDir = (CLIBCDirectoryHandle*)pDir; + + closedir(pCDir->m_pDir); + + delete (CLIBCDirectoryHandle*)pDir; + }; }; EXPOSE_INTERFACE(CLIBCFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION)