first commit
This commit is contained in:
commit
0c107b74b0
364
.gitignore
vendored
Normal file
364
.gitignore
vendored
Normal file
|
@ -0,0 +1,364 @@
|
|||
## my ban
|
||||
*.nmo
|
||||
*.cmo
|
||||
*.nms
|
||||
*.vmo
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
Temp/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*[.json, .xml, .info]
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
6
PyCmo/PyCmo.py
Normal file
6
PyCmo/PyCmo.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
import VirtoolsReader
|
||||
import VirtoolsStruct
|
||||
|
||||
with open("D:\\libcmo21\\PyCmo\\Gameplay.nmo", 'rb') as fs:
|
||||
composition = VirtoolsReader.ReadCKComposition(fs)
|
||||
print(str(composition.Header))
|
50
PyCmo/PyCmo.pyproj
Normal file
50
PyCmo/PyCmo.pyproj
Normal file
|
@ -0,0 +1,50 @@
|
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>adc519e8-5f1b-427d-8e5c-1fabcb6147fb</ProjectGuid>
|
||||
<ProjectHome>.</ProjectHome>
|
||||
<StartupFile>PyCmo.py</StartupFile>
|
||||
<SearchPath>
|
||||
</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>PyCmo</Name>
|
||||
<RootNamespace>PyCmo</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="PyCmo.py" />
|
||||
<Compile Include="PyCmoMisc.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="VirtoolsConstants.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="VirtoolsReader.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="VirtoolsStruct.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="VirtoolsUtils.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
|
||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||
Visual Studio and specify your pre- and post-build commands in
|
||||
the BeforeBuild and AfterBuild targets below. -->
|
||||
<!--<Target Name="CoreCompile" />-->
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
</Project>
|
32
PyCmo/PyCmoMisc.py
Normal file
32
PyCmo/PyCmoMisc.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
def OutputSizeHumanReadable(storage_size: int):
|
||||
probe = storage_size
|
||||
|
||||
# check Bytes
|
||||
if (probe >> 10) == 0:
|
||||
return f"{storage_size:d}Bytes"
|
||||
probe >>= 10
|
||||
|
||||
# check KiB
|
||||
if (probe >> 10) == 0:
|
||||
return f"{(storage_size / (1 << 10)):.2f}KiB"
|
||||
probe >>= 10
|
||||
|
||||
# check MiB
|
||||
if (probe >> 10) == 0:
|
||||
return f"{(storage_size / (1 << 20)):.2f}MiB"
|
||||
probe >>= 10
|
||||
|
||||
# otherwise GiB
|
||||
return f"{(storage_size / (1 << 30)):.2f}GiB"
|
||||
|
||||
def BcdCodeToDecCode(bcd_num: int):
|
||||
result = 0
|
||||
pow = 1
|
||||
|
||||
while bcd_num != 0:
|
||||
result += (bcd_num & 0xf) * pow
|
||||
bcd_num >>= 4
|
||||
pow *= 10
|
||||
|
||||
return result
|
320
PyCmo/VirtoolsConstants.py
Normal file
320
PyCmo/VirtoolsConstants.py
Normal file
|
@ -0,0 +1,320 @@
|
|||
import enum
|
||||
|
||||
class PyEnum(object):
|
||||
@staticmethod
|
||||
def Contain(val: int, probe: int):
|
||||
return bool(val & probe)
|
||||
|
||||
@staticmethod
|
||||
def Add(val: int, data: int):
|
||||
return val | data
|
||||
|
||||
@staticmethod
|
||||
def PrintEnum(val: int, _enum: enum.IntEnum):
|
||||
for i in _enum:
|
||||
if i.value == val:
|
||||
return i.name
|
||||
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def PrintEnumFlag(val: int, _enum: enum.IntEnum):
|
||||
pending = []
|
||||
|
||||
for i in _enum:
|
||||
# if it have exactly same entry, return directly
|
||||
if i.value == val:
|
||||
return i.name
|
||||
|
||||
# check exist
|
||||
if bool(val & i.value):
|
||||
pending.append(i.name)
|
||||
|
||||
return ', '.join(pending)
|
||||
|
||||
class CK_FILE_WRITEMODE(enum.IntEnum):
|
||||
CKFILE_UNCOMPRESSED =0 # Save data uncompressed
|
||||
CKFILE_CHUNKCOMPRESSED_OLD =1 # Obsolete
|
||||
CKFILE_EXTERNALTEXTURES_OLD=2 # Obsolete : use CKContext::SetGlobalImagesSaveOptions instead.
|
||||
CKFILE_FORVIEWER =4 # Don't save Interface Data within the file, the level won't be editable anymore in the interface
|
||||
CKFILE_WHOLECOMPRESSED =8 # Compress the whole file
|
||||
|
||||
class CK_LOAD_FLAGS(enum.IntEnum):
|
||||
CK_LOAD_ANIMATION =1<<0 # Load animations
|
||||
CK_LOAD_GEOMETRY =1<<1 # Load geometry.
|
||||
CK_LOAD_DEFAULT =CK_LOAD_GEOMETRY|CK_LOAD_ANIMATION # Load animations & geometry
|
||||
CK_LOAD_ASCHARACTER =1<<2 # Load all the objects and create a character that contains them all .
|
||||
CK_LOAD_DODIALOG =1<<3 # Check object name unicity and warns the user with a dialog box when duplicate names are found.
|
||||
CK_LOAD_AS_DYNAMIC_OBJECT =1<<4 # Objects loaded from this file may be deleted at run-time or are temporary
|
||||
CK_LOAD_AUTOMATICMODE =1<<5 # Check object name unicity and automatically rename or replace according to the options specified in CKContext::SetAutomaticLoadMode
|
||||
CK_LOAD_CHECKDUPLICATES =1<<6 # Check object name unicity (The list of duplicates is stored in the CKFile class after a OpenFile call
|
||||
CK_LOAD_CHECKDEPENDENCIES =1<<7 # Check if every plugins needed are availables
|
||||
CK_LOAD_ONLYBEHAVIORS =1<<8 #
|
||||
|
||||
CK_CLASSID = int
|
||||
class CKCID(enum.IntEnum):
|
||||
CKCID_OBJECT = 1
|
||||
CKCID_PARAMETERIN = 2
|
||||
CKCID_PARAMETEROPERATION = 4
|
||||
CKCID_STATE = 5
|
||||
CKCID_BEHAVIORLINK = 6
|
||||
CKCID_BEHAVIOR = 8
|
||||
CKCID_BEHAVIORIO = 9
|
||||
CKCID_RENDERCONTEXT = 12
|
||||
CKCID_KINEMATICCHAIN = 13
|
||||
CKCID_SCENEOBJECT = 11
|
||||
CKCID_OBJECTANIMATION = 15
|
||||
CKCID_ANIMATION = 16
|
||||
CKCID_KEYEDANIMATION = 18
|
||||
CKCID_BEOBJECT = 19
|
||||
CKCID_DATAARRAY = 52
|
||||
CKCID_SCENE = 10
|
||||
CKCID_LEVEL = 21
|
||||
CKCID_PLACE = 22
|
||||
CKCID_GROUP = 23
|
||||
CKCID_SOUND = 24
|
||||
CKCID_WAVESOUND = 25
|
||||
CKCID_MIDISOUND = 26
|
||||
CKCID_MATERIAL = 30
|
||||
CKCID_TEXTURE = 31
|
||||
CKCID_MESH = 32
|
||||
CKCID_PATCHMESH = 53
|
||||
CKCID_RENDEROBJECT = 47
|
||||
CKCID_2DENTITY = 27
|
||||
CKCID_SPRITE = 28
|
||||
CKCID_SPRITETEXT = 29
|
||||
CKCID_3DENTITY = 33
|
||||
CKCID_GRID = 50
|
||||
CKCID_CURVEPOINT = 36
|
||||
CKCID_SPRITE3D = 37
|
||||
CKCID_CURVE = 43
|
||||
CKCID_CAMERA = 34
|
||||
CKCID_TARGETCAMERA = 35
|
||||
CKCID_LIGHT = 38
|
||||
CKCID_TARGETLIGHT = 39
|
||||
CKCID_CHARACTER = 40
|
||||
CKCID_3DOBJECT = 41
|
||||
CKCID_BODYPART = 42
|
||||
CKCID_PARAMETER = 46
|
||||
CKCID_PARAMETERLOCAL = 45
|
||||
CKCID_PARAMETERVARIABLE = 55
|
||||
CKCID_PARAMETEROUT = 3
|
||||
CKCID_INTERFACEOBJECTMANAGER = 48
|
||||
CKCID_CRITICALSECTION = 49
|
||||
CKCID_LAYER = 51
|
||||
CKCID_PROGRESSIVEMESH = 54
|
||||
CKCID_SYNCHRO = 20
|
||||
|
||||
CKCID_OBJECTARRAY = 80
|
||||
CKCID_SCENEOBJECTDESC = 81
|
||||
CKCID_ATTRIBUTEMANAGER = 82
|
||||
CKCID_MESSAGEMANAGER = 83
|
||||
CKCID_COLLISIONMANAGER = 84
|
||||
CKCID_OBJECTMANAGER = 85
|
||||
CKCID_FLOORMANAGER = 86
|
||||
CKCID_RENDERMANAGER = 87
|
||||
CKCID_BEHAVIORMANAGER = 88
|
||||
CKCID_INPUTMANAGER = 89
|
||||
CKCID_PARAMETERMANAGER = 90
|
||||
CKCID_GRIDMANAGER = 91
|
||||
CKCID_SOUNDMANAGER = 92
|
||||
CKCID_TIMEMANAGER = 93
|
||||
CKCID_CUIKBEHDATA = -1
|
||||
|
||||
CKCID_MAXCLASSID = 56
|
||||
CKCID_MAXMAXCLASSID = 128
|
||||
|
||||
__ClassHierarchy = {
|
||||
1: ("CKCID_OBJECT", ),
|
||||
2: ("CKCID_OBJECT", "CKCID_PARAMETERIN", ),
|
||||
4: ("CKCID_OBJECT", "CKCID_PARAMETEROPERATION", ),
|
||||
5: ("CKCID_OBJECT", "CKCID_STATE", ),
|
||||
6: ("CKCID_OBJECT", "CKCID_BEHAVIORLINK", ),
|
||||
8: ("CKCID_OBJECT", "CKCID_BEHAVIOR", ),
|
||||
9: ("CKCID_OBJECT", "CKCID_BEHAVIORIO", ),
|
||||
12: ("CKCID_OBJECT", "CKCID_RENDERCONTEXT", ),
|
||||
13: ("CKCID_OBJECT", "CKCID_KINEMATICCHAIN", ),
|
||||
11: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", ),
|
||||
15: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_OBJECTANIMATION", ),
|
||||
16: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_ANIMATION", ),
|
||||
18: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_ANIMATION", "CKCID_KEYEDANIMATION", ),
|
||||
19: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", ),
|
||||
52: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_DATAARRAY", ),
|
||||
10: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SCENE", ),
|
||||
21: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_LEVEL", ),
|
||||
22: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_PLACE", ),
|
||||
23: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_GROUP", ),
|
||||
24: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", ),
|
||||
25: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", "CKCID_WAVESOUND", ),
|
||||
26: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", "CKCID_MIDISOUND", ),
|
||||
30: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MATERIAL", ),
|
||||
31: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_TEXTURE", ),
|
||||
32: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MESH", ),
|
||||
53: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MESH", "CKCID_PATCHMESH", ),
|
||||
47: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", ),
|
||||
27: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", ),
|
||||
28: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", "CKCID_SPRITE", ),
|
||||
29: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", "CKCID_SPRITETEXT", ),
|
||||
33: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", ),
|
||||
50: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_GRID", ),
|
||||
36: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CURVEPOINT", ),
|
||||
37: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_SPRITE3D", ),
|
||||
43: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CURVE", ),
|
||||
34: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CAMERA", ),
|
||||
35: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CAMERA", "CKCID_TARGETCAMERA", ),
|
||||
38: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_LIGHT", ),
|
||||
39: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_LIGHT", "CKCID_TARGETLIGHT", ),
|
||||
40: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CHARACTER", ),
|
||||
41: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_3DOBJECT", ),
|
||||
42: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_3DOBJECT", "CKCID_BODYPART", ),
|
||||
46: ("CKCID_OBJECT", "CKCID_PARAMETER", ),
|
||||
45: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETERLOCAL", ),
|
||||
55: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETERLOCAL", "CKCID_PARAMETERVARIABLE", ),
|
||||
3: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETEROUT", ),
|
||||
48: ("CKCID_OBJECT", "CKCID_INTERFACEOBJECTMANAGER", ),
|
||||
49: ("CKCID_OBJECT", "CKCID_CRITICALSECTION", ),
|
||||
51: ("CKCID_OBJECT", "CKCID_LAYER", ),
|
||||
54: ("CKCID_OBJECT", "CKCID_PROGRESSIVEMESH", ),
|
||||
20: ("CKCID_OBJECT", "CKCID_SYNCHRO", ),
|
||||
80: ("CKCID_OBJECTARRAY", ),
|
||||
81: ("CKCID_SCENEOBJECTDESC", ),
|
||||
82: ("CKCID_ATTRIBUTEMANAGER", ),
|
||||
83: ("CKCID_MESSAGEMANAGER", ),
|
||||
84: ("CKCID_COLLISIONMANAGER", ),
|
||||
85: ("CKCID_OBJECTMANAGER", ),
|
||||
86: ("CKCID_FLOORMANAGER", ),
|
||||
87: ("CKCID_RENDERMANAGER", ),
|
||||
88: ("CKCID_BEHAVIORMANAGER", ),
|
||||
89: ("CKCID_INPUTMANAGER", ),
|
||||
90: ("CKCID_PARAMETERMANAGER", ),
|
||||
91: ("CKCID_GRIDMANAGER", ),
|
||||
92: ("CKCID_SOUNDMANAGER", ),
|
||||
93: ("CKCID_TIMEMANAGER", ),
|
||||
-1: ("CKCID_CUIKBEHDATA", ),
|
||||
56: ("CKCID_MAXCLASSID", ),
|
||||
128: ("CKCID_MAXMAXCLASSID", ),
|
||||
}
|
||||
|
||||
def __init__(self, code: CK_CLASSID):
|
||||
self.code: CK_CLASSID = code
|
||||
def __repr__(self):
|
||||
hierarchy = self.__ClassHierarchy.get(self.code, None)
|
||||
if hierarchy is None:
|
||||
return "[Undefined]"
|
||||
return "{} ({})".format(
|
||||
hierarchy[-1],
|
||||
' -> '.join(hierarchy)
|
||||
)
|
||||
|
||||
|
||||
CKERROR = int
|
||||
class CKERR():
|
||||
CKERR_OK = 0
|
||||
CKERR_INVALIDPARAMETER = -1
|
||||
CKERR_INVALIDPARAMETERTYPE = -2
|
||||
CKERR_INVALIDSIZE = -3
|
||||
CKERR_INVALIDOPERATION = -4
|
||||
CKERR_OPERATIONNOTIMPLEMENTED = -5
|
||||
CKERR_OUTOFMEMORY = -6
|
||||
CKERR_NOTIMPLEMENTED = -7
|
||||
CKERR_NOTFOUND = -11
|
||||
CKERR_NOLEVEL = -13
|
||||
CKERR_CANCREATERENDERCONTEXT = -14
|
||||
CKERR_NOTIFICATIONNOTHANDLED = -16
|
||||
CKERR_ALREADYPRESENT = -17
|
||||
CKERR_INVALIDRENDERCONTEXT = -18
|
||||
CKERR_RENDERCONTEXTINACTIVE = -19
|
||||
CKERR_NOLOADPLUGINS = -20
|
||||
CKERR_NOSAVEPLUGINS = -21
|
||||
CKERR_INVALIDFILE = -22
|
||||
CKERR_INVALIDPLUGIN = -23
|
||||
CKERR_NOTINITIALIZED = -24
|
||||
CKERR_INVALIDMESSAGE = -25
|
||||
CKERR_NODLLFOUND = -29
|
||||
CKERR_ALREADYREGISTREDDLL = -30
|
||||
CKERR_INVALIDDLL = -31
|
||||
CKERR_INVALIDOBJECT = -34
|
||||
CKERR_INVALIDCONDSOLEWINDOW = -35
|
||||
CKERR_INVALIDKINEMATICCHAIN = -36
|
||||
CKERR_NOKEYBOARD = -37
|
||||
CKERR_NOMOUSE = -38
|
||||
CKERR_NOJOYSTICK = -39
|
||||
CKERR_INCOMPATIBLEPARAMETERS = -40
|
||||
CKERR_NORENDERENGINE = -44
|
||||
CKERR_NOCURRENTLEVEL = -45
|
||||
CKERR_SOUNDDISABLED = -46
|
||||
CKERR_DINPUTDISABLED = -47
|
||||
CKERR_INVALIDGUID = -48
|
||||
CKERR_NOTENOUGHDISKPLACE = -49
|
||||
CKERR_CANTWRITETOFILE = -50
|
||||
CKERR_BEHAVIORADDDENIEDBYCB = -51
|
||||
CKERR_INCOMPATIBLECLASSID = -52
|
||||
CKERR_MANAGERALREADYEXISTS = -53
|
||||
CKERR_PAUSED = -54
|
||||
CKERR_PLUGINSMISSING = -55
|
||||
CKERR_OBSOLETEVIRTOOLS = -56
|
||||
CKERR_FILECRCERROR = -57
|
||||
CKERR_ALREADYFULLSCREEN = -58
|
||||
CKERR_CANCELLED = -59
|
||||
CKERR_NOANIMATIONKEY = -121
|
||||
CKERR_INVALIDINDEX = -122
|
||||
CKERR_INVALIDANIMATION = -123
|
||||
|
||||
__ErrorDescription = {
|
||||
0: "Operation successful",
|
||||
-1: "One of the parameter passed to the function was invalid",
|
||||
-2: "One of the parameter passed to the function was invalid",
|
||||
-3: "The parameter size was invalid",
|
||||
-4: "The operation type didn't exist",
|
||||
-5: "The function used to execute the operation is not yet implemented",
|
||||
-6: "There was not enough memory to perform the action",
|
||||
-7: "The function is not yet implemented",
|
||||
-11: "There was an attempt to remove something not present",
|
||||
-13: "There is no level currently created",
|
||||
-14: "There is no level currently created",
|
||||
-16: "The notification message was not used",
|
||||
-17: "Attempt to add an item that was already present",
|
||||
-18: "the render context is not valid",
|
||||
-19: "the render context is not activated for rendering",
|
||||
-20: "there was no plugins to load this kind of file",
|
||||
-21: "there was no plugins to save this kind of file",
|
||||
-22: "attempt to load an invalid file",
|
||||
-23: "attempt to load with an invalid plugin",
|
||||
-24: "attempt use an object that wasnt initialized",
|
||||
-25: "attempt use a message type that wasn't registred",
|
||||
-29: "No dll file found in the parse directory",
|
||||
-30: "this dll has already been registred",
|
||||
-31: "this dll does not contain information to create the prototype",
|
||||
-34: "Invalid Object (attempt to Get an object from an invalid ID)",
|
||||
-35: "Invalid window was provided as console window",
|
||||
-36: "Invalid kinematic chain ( end and start effector may not be part of the same hierarchy )",
|
||||
-37: "Keyboard not attached or not working properly",
|
||||
-38: "Mouse not attached or not working properly",
|
||||
-39: "Joystick not attached or not working properly",
|
||||
-40: "Try to link imcompatible Parameters",
|
||||
-44: "There is no render engine dll",
|
||||
-45: "There is no current level (use CKSetCurrentLevel )",
|
||||
-46: "Sound Management has been disabled",
|
||||
-47: "DirectInput Management has been disabled",
|
||||
-48: "Guid is already in use or invalid",
|
||||
-49: "There was no more free space on disk when trying to save a file",
|
||||
-50: "Impossible to write to file (write-protection ?)",
|
||||
-51: "The behavior cannnot be added to this entity",
|
||||
-52: "The behavior cannnot be added to this entity",
|
||||
-53: "A manager was registered more than once",
|
||||
-54: "CKprocess or TimeManager process while CK is paused will fail",
|
||||
-55: "Some plugins were missing whileloading a file",
|
||||
-56: "Virtools version too old to load this file",
|
||||
-57: "CRC Error while loading file",
|
||||
-58: "A Render context is already in Fullscreen Mode",
|
||||
-59: "Operation was cancelled by user",
|
||||
-121: "there were no animation key at the given index",
|
||||
-122: "attemp to acces an animation key with an invalid index",
|
||||
-123: "the animation is invalid (no entity associated or zero length)",
|
||||
}
|
||||
|
||||
def __init__(self, code: CKERROR):
|
||||
self.code: CKERROR = code
|
||||
def __repr__(self):
|
||||
return self.__ErrorDescription.get(self.code, "[Undefined]")
|
||||
|
47
PyCmo/VirtoolsReader.py
Normal file
47
PyCmo/VirtoolsReader.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
import VirtoolsStruct
|
||||
import VirtoolsUtils
|
||||
import PyCmoMisc
|
||||
import struct, io, datetime, zlib
|
||||
|
||||
g_dword = struct.Struct("<I")
|
||||
|
||||
def ReadCKFileHeader(fs: io.BufferedReader):
|
||||
header = VirtoolsStruct.CKFileHeader()
|
||||
|
||||
# check magic words
|
||||
magic_words = fs.read(8)
|
||||
if magic_words != b'Nemo Fi\0':
|
||||
raise Exception("Fail to read file header magic words")
|
||||
header.Signature = magic_words
|
||||
|
||||
# assign data
|
||||
(header.Crc, raw_date, header.FileVersion, header.FileVersion2, header.SaveFlags, header.PrewHdrPackSize,
|
||||
header.DataPackSize, header.DataUnpackSize, header.ManagerCount, header.ObjectCount, header.MaxIDSaved,
|
||||
header.ProductVersion, header.ProductBuild, header.PrewHdrUnpackSize) = struct.unpack("<14I", fs.read(14 * 4))
|
||||
|
||||
# process date independently
|
||||
# date is in BCD code
|
||||
day = PyCmoMisc.BcdCodeToDecCode((raw_date >> 24) & 0xff)
|
||||
month = PyCmoMisc.BcdCodeToDecCode((raw_date >> 16) & 0xff - 1)
|
||||
month = (month % 12) + 1
|
||||
year = PyCmoMisc.BcdCodeToDecCode(raw_date & 0xffff)
|
||||
header.Timestamp = datetime.date(year, month, day)
|
||||
|
||||
if header.FileVersion >= 8:
|
||||
# check crc
|
||||
gotten_crc = zlib.adler32(b'Nemo Fi\0', 0)
|
||||
gotten_crc = zlib.adler32(struct.pack("<6I", 0, raw_date, header.FileVersion, header.FileVersion2, header.SaveFlags, header.PrewHdrPackSize), gotten_crc) # reset crc as zero
|
||||
gotten_crc = zlib.adler32(struct.pack("<8I", header.DataPackSize, header.DataUnpackSize, header.ManagerCount, header.ObjectCount, header.MaxIDSaved, header.ProductVersion, header.ProductBuild, header.PrewHdrUnpackSize), gotten_crc)
|
||||
gotten_crc = zlib.adler32(fs.read(header.PrewHdrPackSize), gotten_crc)
|
||||
gotten_crc = zlib.adler32(fs.read(header.DataPackSize), gotten_crc)
|
||||
if gotten_crc != header.Crc:
|
||||
raise Exception("Crc Error")
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def ReadCKComposition(fs: io.BufferedReader):
|
||||
composition = VirtoolsStruct.CKComposition()
|
||||
|
||||
composition.Header = ReadCKFileHeader(fs)
|
||||
return composition
|
43
PyCmo/VirtoolsStruct.py
Normal file
43
PyCmo/VirtoolsStruct.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import VirtoolsConstants
|
||||
import datetime
|
||||
import PyCmoMisc
|
||||
|
||||
class CKFileHeader:
|
||||
def __init__(self):
|
||||
self.Signature: bytes = b'Nemo Fi\0'
|
||||
self.Crc: int = 0
|
||||
self.Timestamp: datetime.date = datetime.date.today()
|
||||
self.FileVersion: int = 0
|
||||
self.FileVersion2: int = 0
|
||||
self.SaveFlags: int = 0
|
||||
self.PrewHdrPackSize: int = 0
|
||||
|
||||
self.DataPackSize: int = 0
|
||||
self.DataUnpackSize: int = 0
|
||||
self.ManagerCount: int = 0
|
||||
self.ObjectCount: int = 0
|
||||
self.MaxIDSaved: int = 0
|
||||
self.ProductVersion: int = 0
|
||||
self.ProductBuild: int = 0
|
||||
self.PrewHdrUnpackSize: int = 0
|
||||
|
||||
def __str__(self):
|
||||
return f"""File Version: {self.FileVersion:d} / {self.FileVersion2:d}
|
||||
Production (Version / Build): {self.ProductVersion:d} / {(self.ProductBuild >> 24) & 0xff:d}.{(self.ProductBuild >> 16) & 0xff:d}.{(self.ProductBuild >> 8) & 0xff:d}.{self.ProductBuild & 0xff:d}
|
||||
Crc: 0x{self.Crc:08X}
|
||||
Timestamp: {str(self.Timestamp)}
|
||||
Save Flags: {VirtoolsConstants.PyEnum.PrintEnumFlag(self.SaveFlags, VirtoolsConstants.CK_FILE_WRITEMODE)}
|
||||
|
||||
Preview Header (Pack / Unpack): {PyCmoMisc.OutputSizeHumanReadable(self.PrewHdrPackSize)} / {PyCmoMisc.OutputSizeHumanReadable(self.PrewHdrUnpackSize)}
|
||||
Data (Pack / Unpack): {PyCmoMisc.OutputSizeHumanReadable(self.DataPackSize)} / {PyCmoMisc.OutputSizeHumanReadable(self.DataUnpackSize)}
|
||||
|
||||
Manager Count: {self.ManagerCount:d}
|
||||
Object Count: {self.ObjectCount:d}
|
||||
Max ID Saved: {self.MaxIDSaved:d}
|
||||
"""
|
||||
|
||||
|
||||
class CKComposition(object):
|
||||
def __init__(self):
|
||||
self.Header: CKFileHeader = None
|
||||
|
48
PyCmo/VirtoolsUtils.py
Normal file
48
PyCmo/VirtoolsUtils.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
import zlib
|
||||
import io
|
||||
|
||||
class ZlibDecompressBuffer(object):
|
||||
def __init__(self, _fs: io.BufferedReader, _len: int, _is_compressed: bool):
|
||||
self.__fs: io.BufferedReader = _fs
|
||||
self.__len: int = _len
|
||||
self.__compressed: bool = _is_compressed
|
||||
|
||||
self.__pos: int = 0
|
||||
self.__parser: zlib._Decompress = zlib.decompressobj()
|
||||
self.__cache: bytes = b''
|
||||
self.__cachelen: int = 0
|
||||
|
||||
def __ParseOnce(self) -> bytes:
|
||||
# check remain
|
||||
remain: int = self.__len - self.__pos
|
||||
if remain <= 0:
|
||||
return None
|
||||
|
||||
# read it and increase pos
|
||||
read_count: int = min(remain, 1024)
|
||||
gotten_uncompressed: bytes = self.__parser.decompress(self.__fs.read(read_count))
|
||||
self.__pos += read_count
|
||||
|
||||
# everything has done, no more data, flush it and get it remained data
|
||||
if self.__pos >= self.__len:
|
||||
gotten_uncompressed += self.__parser.flush()
|
||||
|
||||
return gotten_uncompressed
|
||||
|
||||
def Read(self, expected: int):
|
||||
# try enrich cache
|
||||
while self.__cachelen < expected:
|
||||
new_data = self.__ParseOnce()
|
||||
if new_data is None:
|
||||
# no more data
|
||||
raise Exception("No more data.")
|
||||
else:
|
||||
self.__cache += new_data
|
||||
self.__cachelen += len(new_data)
|
||||
|
||||
# change data
|
||||
returned_data = self.__cache[:expected]
|
||||
self.__cache = self.__cache[expected:]
|
||||
self.__cachelen -= expected
|
||||
|
||||
return returned_data
|
9
README.md
Normal file
9
README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# libcmo21
|
||||
|
||||
The aim of this project is creating a universal library which can read / write CMO files or any other Virtools files without any Virtools dependencies.
|
||||
This project will not link any original Virtools dynamic library. So this project can run on both of x64 and x86 platform.
|
||||
This project only involving specific Virtools version, 2.1. Other Virtools version are not considered by this project.
|
||||
|
||||
This project is based on reverse work of CK2.dll, VxMath.dll and CK2_3D.dll. The program [unvirt](https://aluigi.altervista.org/papers.htm#unvirt) created by Luigi Auriemma, which is licensed by GPL-v2, also help my work.
|
||||
|
||||
For some personal reason, this project will keep a clean room with all doyaGu's projects. All variables and logic are based on my understanding and reversed code. This is my personal behavior, please forgive my decision if you dislike it.
|
23
libcmo21.sln
Normal file
23
libcmo21.sln
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31702.278
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "PyCmo", "PyCmo\PyCmo.pyproj", "{ADC519E8-5F1B-427D-8E5C-1FABCB6147FB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ADC519E8-5F1B-427D-8E5C-1FABCB6147FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ADC519E8-5F1B-427D-8E5C-1FABCB6147FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7C36D9F6-B130-4675-A20E-CE3523231D08}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
Loading…
Reference in New Issue
Block a user