mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-08 09:55:32 +00:00
Merge pull request #2419 from keymanapp/feat/developer/2101-kmdecomp
feat(developer): add kmdecomp
This commit is contained in:
commit
175bb404b4
12 changed files with 142 additions and 58 deletions
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
|
||||
TARGETS=kmcmpdll kmcomp kmanalyze kmconvert tike samples setup inst
|
||||
TARGETS=kmcmpdll kmcomp kmanalyze kmconvert tike samples setup inst kmdecomp
|
||||
MANIFESTS=kmcomp tike setup
|
||||
CLEANS=clean-developer
|
||||
|
||||
|
|
@ -19,6 +19,10 @@ kmcmpdll:
|
|||
cd $(ROOT)\src\developer\kmcmpdll
|
||||
$(MAKE) $(TARGET)
|
||||
|
||||
kmdecomp:
|
||||
cd $(ROOT)\src\developer\kmdecomp
|
||||
$(MAKE) $(TARGET)
|
||||
|
||||
kmcomp: kmcmpdll
|
||||
cd $(ROOT)\src\developer\kmcomp
|
||||
$(MAKE) $(TARGET)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## 13.0 alpha
|
||||
* Start version 13.0
|
||||
* Feature: Add unsupported kmdecomp decompiler utility (#2419)
|
||||
|
||||
## 2019-11-18 12.0.55 stable
|
||||
* Bug Fix: Some keyboards were incorrectly marked as mobile-capable (#2334)
|
||||
|
|
|
|||
|
|
@ -166,6 +166,14 @@
|
|||
<RegistryValue Root="HKCR" Key="KeymanDeveloper.File.OnScreenKeyboard\Shell\edit\Command" Type="string" Value='"[#tike.exe]" "%1"' />
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<File Name="kmdecomp.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<File Name="kmdecomp.md" KeyPath="yes" Source="..\..\developer\kmdecomp\kmdecomp.md" />
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<File Name="kmcomp.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
|
|
|
|||
28
windows/src/developer/kmdecomp/Makefile
Normal file
28
windows/src/developer/kmdecomp/Makefile
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# kmdecomp Makefile
|
||||
#
|
||||
|
||||
!include ..\..\Defines.mak
|
||||
|
||||
|
||||
build: version.res dirs
|
||||
$(MSBUILD) kmdecomp.sln $(MSBUILD_BUILD) /p:Platform=Win32
|
||||
$(COPY) $(WIN32_TARGET_PATH)\kmdecomp.exe $(PROGRAM)\developer
|
||||
$(COPY) $(WIN32_TARGET_PATH)\kmdecomp.pdb $(DEBUGPATH)\developer
|
||||
|
||||
clean: def-clean
|
||||
$(MSBUILD) kmdecomp.sln $(MSBUILD_CLEAN)
|
||||
|
||||
signcode:
|
||||
$(SIGNCODE) /d "Keyman Developer Decompiler" $(PROGRAM)\developer\kmdecomp.exe
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\kmdecomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmdecomp.exe
|
||||
|
||||
test-manifest:
|
||||
@rem This target needed as dependency for KMDECOMP
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\developer\kmdecomp.exe "$(INSTALLPATH_KEYMANDEVELOPER)\kmdecomp.exe"
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
@ -57,9 +57,9 @@ int main(int argc, char *argv[])
|
|||
|
||||
if(!LoadKeyboard(argv[1], &kbd, &lpBitmap, &cbBitmap)) return 2;
|
||||
|
||||
_splitpath(argv[1], drive, dir, filename, NULL);
|
||||
_makepath(buf, drive, dir, filename, ".kmn");
|
||||
_makepath(buf2, drive, dir, filename, ".bmp");
|
||||
_splitpath_s(argv[1], drive, _MAX_DRIVE, dir, _MAX_DIR, filename, _MAX_FNAME, NULL, 0);
|
||||
_makepath_s(buf, _MAX_PATH, drive, dir, filename, ".kmn");
|
||||
_makepath_s(buf2, _MAX_PATH, drive, dir, filename, ".bmp");
|
||||
|
||||
int n = SaveKeyboardSource(kbd, lpBitmap, cbBitmap, buf, buf2);
|
||||
|
||||
9
windows/src/developer/kmdecomp/kmdecomp.md
Normal file
9
windows/src/developer/kmdecomp/kmdecomp.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# kmdecomp Keyman Decompiler
|
||||
|
||||
This unsupported utility decompiles a Keyman .kmx keyboard.
|
||||
It will produce a .kmn source file and optionally a .ico or
|
||||
.bmp image. The source file may not contain symbols and will
|
||||
include optimisations that were made by the compiler such as
|
||||
expanding `any()` rules in key components into multiple rules.
|
||||
|
||||
Usage: kmdecomp <infile.kmx>
|
||||
|
|
@ -41,11 +41,8 @@
|
|||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\</IntDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Platform)\$(Configuration)\</IntDir>
|
||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\</IntDir>
|
||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
|
||||
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
|
||||
|
|
@ -54,6 +51,13 @@
|
|||
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
||||
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Midl>
|
||||
<TypeLibraryName>.\Debug/kmdecomp.tlb</TypeLibraryName>
|
||||
|
|
@ -64,13 +68,8 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\global\inc;..\..\resellers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderOutputFile>.\Debug/kmdecomp.pch</PrecompiledHeaderOutputFile>
|
||||
<AssemblerListingLocation>.\Debug/</AssemblerListingLocation>
|
||||
<ObjectFileName>.\Debug/</ObjectFileName>
|
||||
<ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
|
|
@ -80,10 +79,8 @@
|
|||
<Culture>0x0c09</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>.\Debug/kmdecomp.exe</OutputFile>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>.\Debug/kmdecomp.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
|
|
@ -107,10 +104,6 @@
|
|||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeaderOutputFile>.\Release/kmdecomp.pch</PrecompiledHeaderOutputFile>
|
||||
<AssemblerListingLocation>.\Release/</AssemblerListingLocation>
|
||||
<ObjectFileName>.\Release/</ObjectFileName>
|
||||
<ProgramDataBaseFileName>.\Release/</ProgramDataBaseFileName>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
|
|
@ -120,9 +113,7 @@
|
|||
<Culture>0x0c09</Culture>
|
||||
</ResourceCompile>
|
||||
<Link>
|
||||
<OutputFile>.\Release/kmdecomp.exe</OutputFile>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<ProgramDatabaseFile>.\Release/kmdecomp.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
|
|
@ -152,6 +143,9 @@
|
|||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
@ -25,4 +25,9 @@
|
|||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -43,9 +43,9 @@ void SaveBitmapFile(LPBYTE lpBitmap, DWORD cbBitmap, char *bmpfile);
|
|||
|
||||
LPKEYBOARD g_kbd;
|
||||
|
||||
PWCHAR wcscat2(PWCHAR c1, const PWCHAR c2)
|
||||
PWCHAR wcscat2(PWCHAR c1, size_t sz, const PWCHAR c2)
|
||||
{
|
||||
wcscat(c1, c2);
|
||||
wcscat_s(c1, sz, c2);
|
||||
return wcschr(c1, 0);
|
||||
}
|
||||
|
||||
|
|
@ -134,23 +134,23 @@ PWCHAR flagstr(int flag)
|
|||
{
|
||||
static WCHAR buf[256];
|
||||
*buf = 0;
|
||||
if(flag & LCTRLFLAG) wcscat2(buf, L"LCTRL ");
|
||||
if(flag & RCTRLFLAG) wcscat2(buf, L"RCTRL ");
|
||||
if(flag & LALTFLAG) wcscat2(buf, L"LALT ");
|
||||
if(flag & RALTFLAG) wcscat2(buf, L"RALT ");
|
||||
if(flag & K_SHIFTFLAG) wcscat2(buf, L"SHIFT ");
|
||||
if(flag & K_CTRLFLAG) wcscat2(buf, L"CTRL ");
|
||||
if(flag & K_ALTFLAG) wcscat2(buf, L"ALT ");
|
||||
if(flag & CAPITALFLAG) wcscat2(buf, L"CAPS ");
|
||||
if(flag & NOTCAPITALFLAG) wcscat2(buf, L"NCAPS ");
|
||||
if(flag & NUMLOCKFLAG) wcscat2(buf, L"NUM ");
|
||||
if(flag & NOTNUMLOCKFLAG) wcscat2(buf, L"NNUM ");
|
||||
if(flag & SCROLLFLAG) wcscat2(buf, L"SCROLL ");
|
||||
if(flag & NOTSCROLLFLAG) wcscat2(buf, L"NSCROLL ");
|
||||
if(flag & LCTRLFLAG) wcscat2(buf, _countof(buf), L"LCTRL ");
|
||||
if(flag & RCTRLFLAG) wcscat2(buf, _countof(buf), L"RCTRL ");
|
||||
if(flag & LALTFLAG) wcscat2(buf, _countof(buf), L"LALT ");
|
||||
if(flag & RALTFLAG) wcscat2(buf, _countof(buf), L"RALT ");
|
||||
if(flag & K_SHIFTFLAG) wcscat2(buf, _countof(buf), L"SHIFT ");
|
||||
if(flag & K_CTRLFLAG) wcscat2(buf, _countof(buf), L"CTRL ");
|
||||
if(flag & K_ALTFLAG) wcscat2(buf, _countof(buf), L"ALT ");
|
||||
if(flag & CAPITALFLAG) wcscat2(buf, _countof(buf), L"CAPS ");
|
||||
if(flag & NOTCAPITALFLAG) wcscat2(buf, _countof(buf), L"NCAPS ");
|
||||
if(flag & NUMLOCKFLAG) wcscat2(buf, _countof(buf), L"NUM ");
|
||||
if(flag & NOTNUMLOCKFLAG) wcscat2(buf, _countof(buf), L"NNUM ");
|
||||
if(flag & SCROLLFLAG) wcscat2(buf, _countof(buf), L"SCROLL ");
|
||||
if(flag & NOTSCROLLFLAG) wcscat2(buf, _countof(buf), L"NSCROLL ");
|
||||
return buf;
|
||||
}
|
||||
|
||||
PWCHAR GetVKeyName(LPKEY key) // I3438
|
||||
PCWCHAR GetVKeyName(LPKEY key) // I3438
|
||||
{
|
||||
static WCHAR buf[100];
|
||||
if(key->Key <= VK__MAX)
|
||||
|
|
@ -187,9 +187,10 @@ PWCHAR ifvalue(WCHAR ch)
|
|||
return L"=";
|
||||
}
|
||||
|
||||
#define BUFSIZE 512
|
||||
PWCHAR ExtString(PWCHAR str)
|
||||
{
|
||||
static WCHAR buf[2][512], bufpointer = 0; // allows for multiple strings in one printf
|
||||
static WCHAR buf[2][BUFSIZE], bufpointer = 0; // allows for multiple strings in one printf
|
||||
// dodgy hack?
|
||||
|
||||
PWCHAR p;
|
||||
|
|
@ -203,7 +204,7 @@ PWCHAR ExtString(PWCHAR str)
|
|||
if(*str == UC_SENTINEL)
|
||||
{
|
||||
str++;
|
||||
if(inquotes) p = wcscat2(p, L"\" ");
|
||||
if(inquotes) p = wcscat2(p, BUFSIZE, L"\" ");
|
||||
inquotes = 0;
|
||||
switch(*str)
|
||||
{
|
||||
|
|
@ -219,10 +220,10 @@ PWCHAR ExtString(PWCHAR str)
|
|||
p = wcschr(p, 0);
|
||||
break;
|
||||
case CODE_CONTEXT:
|
||||
p = wcscat2(p, L"context ");
|
||||
p = wcscat2(p, BUFSIZE, L"context ");
|
||||
break;
|
||||
case CODE_NUL:
|
||||
p = wcscat2(p, L"nul ");
|
||||
p = wcscat2(p, BUFSIZE, L"nul ");
|
||||
break;
|
||||
case CODE_USE:
|
||||
str++;
|
||||
|
|
@ -230,10 +231,10 @@ PWCHAR ExtString(PWCHAR str)
|
|||
p = wcschr(p, 0);
|
||||
break;
|
||||
case CODE_RETURN:
|
||||
p = wcscat2(p, L"return ");
|
||||
p = wcscat2(p, BUFSIZE, L"return ");
|
||||
break;
|
||||
case CODE_BEEP:
|
||||
p = wcscat2(p, L"beep ");
|
||||
p = wcscat2(p, BUFSIZE, L"beep ");
|
||||
break;
|
||||
case CODE_DEADKEY:
|
||||
str++;
|
||||
|
|
@ -257,13 +258,13 @@ PWCHAR ExtString(PWCHAR str)
|
|||
p = wcschr(p, 0);
|
||||
break;
|
||||
case CODE_SWITCH:
|
||||
p = wcscat2(p, L"switch<deprecated> ");
|
||||
p = wcscat2(p, BUFSIZE, L"switch<deprecated> ");
|
||||
break;
|
||||
case CODE_KEY:
|
||||
p = wcscat2(p, L"key<deprecated> ");
|
||||
p = wcscat2(p, BUFSIZE, L"key<deprecated> ");
|
||||
break;
|
||||
case CODE_CLEARCONTEXT:
|
||||
p = wcscat2(p, L"clearcontext ");
|
||||
p = wcscat2(p, BUFSIZE, L"clearcontext ");
|
||||
break;
|
||||
case CODE_CALL:
|
||||
str++;
|
||||
|
|
@ -321,7 +322,7 @@ PWCHAR ExtString(PWCHAR str)
|
|||
break;
|
||||
|
||||
default:
|
||||
p = wcscat2(p, L"unknown() ");
|
||||
p = wcscat2(p, BUFSIZE, L"unknown() ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -329,13 +330,13 @@ PWCHAR ExtString(PWCHAR str)
|
|||
{
|
||||
if(*str == L'"')
|
||||
{
|
||||
if(inquotes) p = wcscat2(p, L"\" ");
|
||||
if(inquotes) p = wcscat2(p, BUFSIZE, L"\" ");
|
||||
inquotes = 0;
|
||||
p = wcscat2(p, L"'\"' ");
|
||||
p = wcscat2(p, BUFSIZE, L"'\"' ");
|
||||
}
|
||||
else if(*str < 32)
|
||||
{
|
||||
if(inquotes) p = wcscat2(p, L"\" ");
|
||||
if(inquotes) p = wcscat2(p, BUFSIZE, L"\" ");
|
||||
inquotes = 0;
|
||||
wsprintfW(p, L"x%x ", *str);
|
||||
p = wcschr(p, 0);
|
||||
|
|
@ -345,7 +346,7 @@ PWCHAR ExtString(PWCHAR str)
|
|||
if(!inquotes)
|
||||
{
|
||||
inquotes = 1;
|
||||
p = wcscat2(p, L"\"");
|
||||
p = wcscat2(p, BUFSIZE, L"\"");
|
||||
}
|
||||
*p++ = *str;
|
||||
*p = 0;
|
||||
|
|
@ -353,7 +354,7 @@ PWCHAR ExtString(PWCHAR str)
|
|||
}
|
||||
}
|
||||
|
||||
if(inquotes) p = wcscat2(p, L"\" ");
|
||||
if(inquotes) p = wcscat2(p, BUFSIZE, L"\" ");
|
||||
return buf[bufpointer];
|
||||
}
|
||||
|
||||
|
|
@ -376,10 +377,12 @@ int SaveKeyboardSource(LPKEYBOARD kbd, LPBYTE lpBitmap, DWORD cbBitmap, char *fi
|
|||
|
||||
g_kbd = kbd;
|
||||
|
||||
fp = fopen(filename, "wb");
|
||||
fwrite(UTF16Sig, 2, 1, fp);
|
||||
if (fopen_s(&fp, filename, "wb") != 0) {
|
||||
Err("Unable to create output file.");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(!fp) { Err("Unable to create output file."); return 3; }
|
||||
fwrite(UTF16Sig, 2, 1, fp);
|
||||
|
||||
wsprintfW(buf, L"c Keyboard created by KMDECOMP\n"); wr(fp, buf);
|
||||
wsprintfW(buf, L"c\n"); wr(fp, buf);
|
||||
|
|
@ -450,7 +453,9 @@ int SaveKeyboardSource(LPKEYBOARD kbd, LPBYTE lpBitmap, DWORD cbBitmap, char *fi
|
|||
|
||||
void SaveBitmapFile(LPBYTE lpBitmap, DWORD cbBitmap, char *bmpfile)
|
||||
{
|
||||
FILE * fp = fopen(bmpfile, "wb");
|
||||
fwrite(lpBitmap, cbBitmap, 1, fp);
|
||||
fclose(fp);
|
||||
FILE *fp;
|
||||
if (fopen_s(&fp, bmpfile, "wb") == 0) {
|
||||
fwrite(lpBitmap, cbBitmap, 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
30
windows/src/developer/kmdecomp/version.in
Normal file
30
windows/src/developer/kmdecomp/version.in
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
1 VERSIONINFO
|
||||
FILEVERSION $VERSIONNUM
|
||||
PRODUCTVERSION $VERSIONNUM
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "0C0904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "SIL International\0"
|
||||
VALUE "FileDescription", "Keyman decompiler\0"
|
||||
VALUE "FileVersion", "$VERSION\0"
|
||||
VALUE "InternalName", "KMDECOMP\0"
|
||||
VALUE "LegalCopyright", "<22> SIL International\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "KMDECOMP.EXE\0"
|
||||
VALUE "ProductName", "Keyman Developer\0"
|
||||
VALUE "ProductVersion", "$VERSION\0"
|
||||
VALUE "Comments", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0xc09, 1252
|
||||
END
|
||||
END
|
||||
Loading…
Add table
Reference in a new issue