From d9c717de6f766e6e2844398f452a53d4d4f01a8b Mon Sep 17 00:00:00 2001 From: Yuchen Ji Date: Tue, 5 Apr 2022 01:55:32 +0800 Subject: [PATCH] Initial commit --- .idea/.idea.Embedio-test/.idea/.gitignore | 13 + .../.idea.Embedio-test/.idea/dataSources.xml | 29 + .idea/.idea.Embedio-test/.idea/encodings.xml | 4 + .../.idea.Embedio-test/.idea/indexLayout.xml | 8 + .../.idea/.gitignore | 13 + .../.idea/dataSources.xml | 17 + .../.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 8 + .../.idea/vcs.xml | 6 + GC-local-server-rewrite.sln | 16 + GC-local-server-rewrite.sln.DotSettings.user | 6 + GC-local-server-rewrite/.gitignore | 437 +++++++++++++++ .../GC-local-server-rewrite.csproj | 25 + .../GC-local-server-rewrite.exe.config.xml | 11 + GC-local-server-rewrite/Program.cs | 85 +++ .../backports/CustomResponseSerializer.cs | 74 +++ .../common/CertificateHelper.cs | 234 ++++++++ GC-local-server-rewrite/common/Configs.cs | 140 +++++ .../common/DatabaseHelper.cs | 40 ++ GC-local-server-rewrite/common/PathHelper.cs | 39 ++ .../controllers/AliveController.cs | 34 ++ .../controllers/CardServiceController.cs | 529 ++++++++++++++++++ .../controllers/IncomServiceController.cs | 32 ++ .../controllers/RankController.cs | 109 ++++ .../controllers/ResponeServiceController.cs | 21 + .../controllers/ServerController.cs | 138 +++++ .../controllers/UploadServiceController.cs | 22 + GC-local-server-rewrite/db/card.db3 | Bin 0 -> 20480 bytes GC-local-server-rewrite/db/music.db3 | Bin 0 -> 73728 bytes GC-local-server-rewrite/models/Avatar.cs | 34 ++ GC-local-server-rewrite/models/Card.cs | 60 ++ GC-local-server-rewrite/models/CardBData.cs | 30 + GC-local-server-rewrite/models/CardDetail.cs | 98 ++++ .../models/CardDetailReadData.cs | 20 + GC-local-server-rewrite/models/Coin.cs | 24 + .../models/ICardIdModel.cs | 6 + GC-local-server-rewrite/models/IIdModel.cs | 6 + GC-local-server-rewrite/models/Item.cs | 37 ++ GC-local-server-rewrite/models/Music.cs | 37 ++ GC-local-server-rewrite/models/MusicAou.cs | 37 ++ GC-local-server-rewrite/models/MusicExtra.cs | 17 + GC-local-server-rewrite/models/Navigator.cs | 34 ++ GC-local-server-rewrite/models/RankStatus.cs | 21 + GC-local-server-rewrite/models/Record.cs | 10 + GC-local-server-rewrite/models/Session.cs | 21 + GC-local-server-rewrite/models/Skin.cs | 34 ++ GC-local-server-rewrite/models/SoundEffect.cs | 34 ++ GC-local-server-rewrite/models/Title.cs | 34 ++ GC-local-server-rewrite/models/TotalTrophy.cs | 12 + .../models/UnlockKeynum.cs | 33 ++ .../models/UnlockReward.cs | 57 ++ GC-local-server-rewrite/server/Server.cs | 65 +++ GC-local-server-rewrite/static/favicon.ico | Bin 0 -> 15406 bytes GC-local-server-rewrite/static/index.html | 10 + GC-local-server-rewrite/static/news.png | Bin 0 -> 818 bytes 55 files changed, 2865 insertions(+) create mode 100644 .idea/.idea.Embedio-test/.idea/.gitignore create mode 100644 .idea/.idea.Embedio-test/.idea/dataSources.xml create mode 100644 .idea/.idea.Embedio-test/.idea/encodings.xml create mode 100644 .idea/.idea.Embedio-test/.idea/indexLayout.xml create mode 100644 .idea/.idea.GC-local-server-rewrite/.idea/.gitignore create mode 100644 .idea/.idea.GC-local-server-rewrite/.idea/dataSources.xml create mode 100644 .idea/.idea.GC-local-server-rewrite/.idea/encodings.xml create mode 100644 .idea/.idea.GC-local-server-rewrite/.idea/indexLayout.xml create mode 100644 .idea/.idea.GC-local-server-rewrite/.idea/vcs.xml create mode 100644 GC-local-server-rewrite.sln create mode 100644 GC-local-server-rewrite.sln.DotSettings.user create mode 100644 GC-local-server-rewrite/.gitignore create mode 100644 GC-local-server-rewrite/GC-local-server-rewrite.csproj create mode 100644 GC-local-server-rewrite/GC-local-server-rewrite.exe.config.xml create mode 100644 GC-local-server-rewrite/Program.cs create mode 100644 GC-local-server-rewrite/backports/CustomResponseSerializer.cs create mode 100644 GC-local-server-rewrite/common/CertificateHelper.cs create mode 100644 GC-local-server-rewrite/common/Configs.cs create mode 100644 GC-local-server-rewrite/common/DatabaseHelper.cs create mode 100644 GC-local-server-rewrite/common/PathHelper.cs create mode 100644 GC-local-server-rewrite/controllers/AliveController.cs create mode 100644 GC-local-server-rewrite/controllers/CardServiceController.cs create mode 100644 GC-local-server-rewrite/controllers/IncomServiceController.cs create mode 100644 GC-local-server-rewrite/controllers/RankController.cs create mode 100644 GC-local-server-rewrite/controllers/ResponeServiceController.cs create mode 100644 GC-local-server-rewrite/controllers/ServerController.cs create mode 100644 GC-local-server-rewrite/controllers/UploadServiceController.cs create mode 100644 GC-local-server-rewrite/db/card.db3 create mode 100644 GC-local-server-rewrite/db/music.db3 create mode 100644 GC-local-server-rewrite/models/Avatar.cs create mode 100644 GC-local-server-rewrite/models/Card.cs create mode 100644 GC-local-server-rewrite/models/CardBData.cs create mode 100644 GC-local-server-rewrite/models/CardDetail.cs create mode 100644 GC-local-server-rewrite/models/CardDetailReadData.cs create mode 100644 GC-local-server-rewrite/models/Coin.cs create mode 100644 GC-local-server-rewrite/models/ICardIdModel.cs create mode 100644 GC-local-server-rewrite/models/IIdModel.cs create mode 100644 GC-local-server-rewrite/models/Item.cs create mode 100644 GC-local-server-rewrite/models/Music.cs create mode 100644 GC-local-server-rewrite/models/MusicAou.cs create mode 100644 GC-local-server-rewrite/models/MusicExtra.cs create mode 100644 GC-local-server-rewrite/models/Navigator.cs create mode 100644 GC-local-server-rewrite/models/RankStatus.cs create mode 100644 GC-local-server-rewrite/models/Record.cs create mode 100644 GC-local-server-rewrite/models/Session.cs create mode 100644 GC-local-server-rewrite/models/Skin.cs create mode 100644 GC-local-server-rewrite/models/SoundEffect.cs create mode 100644 GC-local-server-rewrite/models/Title.cs create mode 100644 GC-local-server-rewrite/models/TotalTrophy.cs create mode 100644 GC-local-server-rewrite/models/UnlockKeynum.cs create mode 100644 GC-local-server-rewrite/models/UnlockReward.cs create mode 100644 GC-local-server-rewrite/server/Server.cs create mode 100644 GC-local-server-rewrite/static/favicon.ico create mode 100644 GC-local-server-rewrite/static/index.html create mode 100644 GC-local-server-rewrite/static/news.png diff --git a/.idea/.idea.Embedio-test/.idea/.gitignore b/.idea/.idea.Embedio-test/.idea/.gitignore new file mode 100644 index 0000000..0d0330a --- /dev/null +++ b/.idea/.idea.Embedio-test/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.Embedio-test.iml +/modules.xml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.Embedio-test/.idea/dataSources.xml b/.idea/.idea.Embedio-test/.idea/dataSources.xml new file mode 100644 index 0000000..336bd4f --- /dev/null +++ b/.idea/.idea.Embedio-test/.idea/dataSources.xml @@ -0,0 +1,29 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:C:\Users\10614\RiderProjects\Embedio-test\GC-local-server-rewrite\db\card.db3 + $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.36.0.3/sqlite-jdbc-3.36.0.3.jar + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:C:\Users\10614\RiderProjects\Embedio-test\GC-local-server-rewrite\db\music.db3 + $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.36.0.3/sqlite-jdbc-3.36.0.3.jar + + + + + \ No newline at end of file diff --git a/.idea/.idea.Embedio-test/.idea/encodings.xml b/.idea/.idea.Embedio-test/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.Embedio-test/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.Embedio-test/.idea/indexLayout.xml b/.idea/.idea.Embedio-test/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.Embedio-test/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.GC-local-server-rewrite/.idea/.gitignore b/.idea/.idea.GC-local-server-rewrite/.idea/.gitignore new file mode 100644 index 0000000..1d36e00 --- /dev/null +++ b/.idea/.idea.GC-local-server-rewrite/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/.idea.GC-local-server-rewrite.iml +/projectSettingsUpdater.xml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.GC-local-server-rewrite/.idea/dataSources.xml b/.idea/.idea.GC-local-server-rewrite/.idea/dataSources.xml new file mode 100644 index 0000000..7aefeff --- /dev/null +++ b/.idea/.idea.GC-local-server-rewrite/.idea/dataSources.xml @@ -0,0 +1,17 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:C:\Users\10614\RiderProjects\GC-local-server-rewrite\GC-local-server-rewrite\db\card.db3 + $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.36.0.3/sqlite-jdbc-3.36.0.3.jar + + + + + \ No newline at end of file diff --git a/.idea/.idea.GC-local-server-rewrite/.idea/encodings.xml b/.idea/.idea.GC-local-server-rewrite/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.GC-local-server-rewrite/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.GC-local-server-rewrite/.idea/indexLayout.xml b/.idea/.idea.GC-local-server-rewrite/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.GC-local-server-rewrite/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.GC-local-server-rewrite/.idea/vcs.xml b/.idea/.idea.GC-local-server-rewrite/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.GC-local-server-rewrite/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/GC-local-server-rewrite.sln b/GC-local-server-rewrite.sln new file mode 100644 index 0000000..23ac0c6 --- /dev/null +++ b/GC-local-server-rewrite.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GC-local-server-rewrite", "GC-local-server-rewrite\GC-local-server-rewrite.csproj", "{D5FFF2E8-6591-4967-8883-A28F453F0524}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5FFF2E8-6591-4967-8883-A28F453F0524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5FFF2E8-6591-4967-8883-A28F453F0524}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5FFF2E8-6591-4967-8883-A28F453F0524}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5FFF2E8-6591-4967-8883-A28F453F0524}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/GC-local-server-rewrite.sln.DotSettings.user b/GC-local-server-rewrite.sln.DotSettings.user new file mode 100644 index 0000000..df95ebc --- /dev/null +++ b/GC-local-server-rewrite.sln.DotSettings.user @@ -0,0 +1,6 @@ + + True + True + True + True + True \ No newline at end of file diff --git a/GC-local-server-rewrite/.gitignore b/GC-local-server-rewrite/.gitignore new file mode 100644 index 0000000..c32c614 --- /dev/null +++ b/GC-local-server-rewrite/.gitignore @@ -0,0 +1,437 @@ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### VisualStudio template +## 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/ + +# 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 +coverage*.xml +coverage*.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/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + diff --git a/GC-local-server-rewrite/GC-local-server-rewrite.csproj b/GC-local-server-rewrite/GC-local-server-rewrite.csproj new file mode 100644 index 0000000..b2a88c3 --- /dev/null +++ b/GC-local-server-rewrite/GC-local-server-rewrite.csproj @@ -0,0 +1,25 @@ + + + + Exe + net6.0 + GCLocalServerRewrite + enable + enable + + + + + + + + + + + + + + + + + diff --git a/GC-local-server-rewrite/GC-local-server-rewrite.exe.config.xml b/GC-local-server-rewrite/GC-local-server-rewrite.exe.config.xml new file mode 100644 index 0000000..b272d74 --- /dev/null +++ b/GC-local-server-rewrite/GC-local-server-rewrite.exe.config.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/GC-local-server-rewrite/Program.cs b/GC-local-server-rewrite/Program.cs new file mode 100644 index 0000000..fe04695 --- /dev/null +++ b/GC-local-server-rewrite/Program.cs @@ -0,0 +1,85 @@ +using GCLocalServerRewrite.common; +using GCLocalServerRewrite.server; +using SQLitePCL; +using Swan; +using Swan.Logging; + +namespace GCLocalServerRewrite; + +internal class Program +{ + private static void Main(string[] args) + { + Batteries_V2.Init(); + + InitializeLogging(); + + var urlPrefixes = args.Length > 0 ? args : new[] { "http://*:80", "https://*:443" }; + + using (var cts = new CancellationTokenSource()) + { + Task.WaitAll( + RunWebServerAsync(urlPrefixes, cts.Token), + Task.CompletedTask, + WaitForUserBreakAsync(cts.Cancel)); + } + + // Clean up + "Bye".Info(nameof(Program)); + Terminal.Flush(); + + Console.WriteLine("Press any key to exit."); + WaitForKeypress(); + } + + private static void InitializeLogging() + { + if (!Directory.Exists(PathHelper.LogRootPath)) + { + Directory.CreateDirectory(PathHelper.LogRootPath); + } + + Logger.RegisterLogger(new FileLogger(PathHelper.LogRootPath, true)); + } + + + /// + /// Create and run a web server. + /// + /// + /// + private static async Task RunWebServerAsync(string[] urlPrefixes, CancellationToken cancellationToken) + { + using var server = Server.CreateWebServer(urlPrefixes); + await server.RunAsync(cancellationToken).ConfigureAwait(false); + } + + /// + /// Prompt the user to press any key; + /// when a key is next pressed, call the specified action to cancel operations. + /// + /// Cancel Action to call + private static async Task WaitForUserBreakAsync(Action cancel) + { + // Be sure to run in parallel. + await Task.Yield(); + + "Press any key to stop the web server.".Info(nameof(Program)); + WaitForKeypress(); + "Stopping...".Info(nameof(Program)); + cancel(); + } + + /// + /// Clear the console input buffer and wait for a keypress + /// + private static void WaitForKeypress() + { + while (Console.KeyAvailable) + { + Console.ReadKey(true); + } + + Console.ReadKey(true); + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/backports/CustomResponseSerializer.cs b/GC-local-server-rewrite/backports/CustomResponseSerializer.cs new file mode 100644 index 0000000..d6de2ab --- /dev/null +++ b/GC-local-server-rewrite/backports/CustomResponseSerializer.cs @@ -0,0 +1,74 @@ +using EmbedIO; + +namespace GCLocalServerRewrite.backports; + +/// +/// Provides custom response serializer callbacks. +/// +public static class CustomResponseSerializer +{ + private static readonly ResponseSerializerCallback CHUNKED_ENCODING_BASE_SERIALIZER = GetBaseSerializer(false); + private static readonly ResponseSerializerCallback BUFFERING_BASE_SERIALIZER = GetBaseSerializer(true); + + /// + /// Sends data in a HTTP response without serialization. + /// + /// + /// to write the response body to a memory buffer first, + /// then send it all together with a Content-Length header; to use chunked + /// transfer encoding. + /// + /// A that can be used to serialize data to a HTTP response. + /// + /// + /// s and one-dimensional arrays of s + /// are sent to the client unchanged; every other type is converted to a string. + /// + /// + /// The ContentType set on the response is used to negotiate + /// a compression method, according to request headers. + /// + /// + /// Strings (and other types converted to strings) are sent with the encoding specified by + /// . + /// + /// + public static ResponseSerializerCallback None(bool bufferResponse) + { + return bufferResponse ? BUFFERING_BASE_SERIALIZER : CHUNKED_ENCODING_BASE_SERIALIZER; + } + + private static ResponseSerializerCallback GetBaseSerializer(bool bufferResponse) + { + return async (context, data) => + { + if (data is null) + { + return; + } + + var isBinaryResponse = data is byte[]; + + if (!context.TryDetermineCompression(context.Response.ContentType, out var preferCompression)) + { + preferCompression = true; + } + + preferCompression = false; + + if (isBinaryResponse) + { + var responseBytes = (byte[])data; + using var stream = context.OpenResponseStream(bufferResponse, preferCompression); + await stream.WriteAsync(responseBytes).ConfigureAwait(false); + } + else + { + var responseString = data is string stringData ? stringData : data.ToString() ?? string.Empty; + await using var text = context.OpenResponseText(context.Response.ContentEncoding, bufferResponse, + preferCompression); + await text.WriteAsync(responseString).ConfigureAwait(false); + } + }; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/common/CertificateHelper.cs b/GC-local-server-rewrite/common/CertificateHelper.cs new file mode 100644 index 0000000..261fbba --- /dev/null +++ b/GC-local-server-rewrite/common/CertificateHelper.cs @@ -0,0 +1,234 @@ +using System.Diagnostics; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using CertificateManager; +using CertificateManager.Models; +using Microsoft.Extensions.DependencyInjection; +using Swan; +using Swan.Logging; + +namespace GCLocalServerRewrite.common; + +public static class CertificateHelper +{ + private const X509KeyUsageFlags ROOT_CA_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.KeyCertSign | + X509KeyUsageFlags.DataEncipherment | + X509KeyUsageFlags.KeyEncipherment | + X509KeyUsageFlags.DigitalSignature; + + private const X509KeyStorageFlags X509_KEY_STORAGE_FLAGS_MACHINE = X509KeyStorageFlags.PersistKeySet + | X509KeyStorageFlags.MachineKeySet; + + private const X509KeyUsageFlags CERT_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.DataEncipherment | + X509KeyUsageFlags.KeyEncipherment | + X509KeyUsageFlags.DigitalSignature; + + private static readonly DistinguishedName ROOT_CA_DISTINGUISHED_NAME = new() + { + CommonName = Configs.ROOT_CA_CN + }; + + private static readonly DistinguishedName CERT_DISTINGUISHED_NAME = new() + { + CommonName = Configs.CERT_CN + }; + + private static readonly BasicConstraints ROOT_CA_BASIC_CONSTRAINTS = new() + { + CertificateAuthority = true, + HasPathLengthConstraint = true, + PathLengthConstraint = 3, + Critical = true + }; + + public static readonly BasicConstraints CERT_BASIC_CONSTRAINTS = new() + { + CertificateAuthority = false, + HasPathLengthConstraint = false, + PathLengthConstraint = 0, + Critical = true, + }; + + private static readonly SubjectAlternativeName SUBJECT_ALTERNATIVE_NAME = new() + { + DnsName = new List + { + "localhost" + } + }; + + private static readonly ValidityPeriod VALIDITY_PERIOD = new() + { + ValidFrom = DateTime.UtcNow, + ValidTo = DateTime.UtcNow.AddYears(3) + }; + + private static readonly OidCollection OID_COLLECTION = new() + { + OidLookup.AnyPurpose + }; + + public static X509Certificate2 InitializeCertificate() + { + if (CertificateExists()) + { + var existingCert = GetCertificate(StoreName.My, StoreLocation.LocalMachine, Configs.CERT_CN); + + if (existingCert != null) + { + return existingCert; + } + } + + RemovePreviousCert(StoreName.My, StoreLocation.LocalMachine); + RemovePreviousCert(StoreName.Root, StoreLocation.LocalMachine); + + var serviceProvider = new ServiceCollection() + .AddCertificateManager().BuildServiceProvider(); + + var createCertificates = serviceProvider.GetService(); + + if (createCertificates == null) + { + throw SelfCheck.Failure("Cannot initialize CreateCertificates service!"); + } + + var rootCa = createCertificates.NewRsaSelfSignedCertificate( + ROOT_CA_DISTINGUISHED_NAME, + ROOT_CA_BASIC_CONSTRAINTS, + VALIDITY_PERIOD, + SUBJECT_ALTERNATIVE_NAME, + OID_COLLECTION, + ROOT_CA_X509_KEY_USAGE_FLAGS, + new RsaConfiguration() + ); + + var cert = createCertificates.NewRsaChainedCertificate( + CERT_DISTINGUISHED_NAME, + CERT_BASIC_CONSTRAINTS, + VALIDITY_PERIOD, + SUBJECT_ALTERNATIVE_NAME, + rootCa, + OID_COLLECTION, + CERT_X509_KEY_USAGE_FLAGS, + new RsaConfiguration() + ); + + var exportService = serviceProvider.GetService(); + + if (exportService == null) + { + throw SelfCheck.Failure("Cannot initialize ImportExportCertificate service!"); + } + + var rootCaPfxBytes = exportService.ExportRootPfx(null, rootCa); + var certPfxBytes = exportService.ExportChainedCertificatePfx(null, cert, rootCa); + + var rootCaWithPrivateKey = new X509Certificate2(rootCaPfxBytes, (string)null!, + X509_KEY_STORAGE_FLAGS_MACHINE); + + var certWithPrivateKey = new X509Certificate2(certPfxBytes, (string)null!, + X509_KEY_STORAGE_FLAGS_MACHINE); + + AddCertToStore(rootCaWithPrivateKey, StoreName.My, StoreLocation.LocalMachine); + AddCertToStore(rootCaWithPrivateKey, StoreName.Root, StoreLocation.LocalMachine); + AddCertToStore(certWithPrivateKey, StoreName.My, StoreLocation.LocalMachine); + + return certWithPrivateKey; + } + + private static void AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation) + { + try + { + var store = new X509Store(storeName, storeLocation); + store.Open(OpenFlags.ReadWrite); + store.Add(cert); + + store.Close(); + } + catch (Exception e) + { + e.Error(e.Source ?? "", e.Message); + } + } + + private static void RemovePreviousCert(StoreName storeName, StoreLocation storeLocation) + { + try + { + var store = new X509Store(storeName, storeLocation); + store.Open(OpenFlags.ReadWrite); + var result = store.Certificates.Find(X509FindType.FindByIssuerName, Configs.ROOT_CA_CN, true); + + if (result.Any()) + { + store.RemoveRange(result); + "Removed previous certs!".Info(); + } + + store.Close(); + } + catch (Exception e) + { + e.Error(e.Source ?? "", e.Message); + } + } + + private static bool CertificateExists() + { + try + { + var store = new X509Store(StoreName.My, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadOnly); + var result = store.Certificates.Find(X509FindType.FindByIssuerName, Configs.ROOT_CA_CN, true); + + if (result.Count == 2) + { + "Certificate exists!".Info(); + + return true; + } + + store.Close(); + "Certificate not found! Will generate new certs...".Info(); + + return false; + } + catch (Exception e) + { + e.Error(e.Source ?? "", e.Message); + + return false; + } + } + + private static X509Certificate2? GetCertificate(StoreName storeName, StoreLocation storeLocation, string commonName) + { + try + { + var store = new X509Store(storeName, storeLocation); + store.Open(OpenFlags.ReadWrite); + var result = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, + $"CN={commonName}", true); + + if (result.Any()) + { + $"Certificate CN={commonName} found!".Info(); + + return result.First(); + } + + store.Close(); + + return null; + } + catch (Exception e) + { + e.Error(e.Source ?? "", e.Message); + + return null; + } + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/common/Configs.cs b/GC-local-server-rewrite/common/Configs.cs new file mode 100644 index 0000000..ac6b612 --- /dev/null +++ b/GC-local-server-rewrite/common/Configs.cs @@ -0,0 +1,140 @@ +using System.Configuration; + +namespace GCLocalServerRewrite.common; + +public static class Configs +{ + public const bool USE_FILE_CACHE = true; + + public const string ROOT_CA_CN = "Taito Arcade Machine CA"; + + public const string CERT_CN = "GC local server"; + + public const string DB_FOLDER = "db"; + + public const string LOG_FOLDER = "log"; + + public const string LOG_BASE_NAME = "log"; + + public const string STATIC_FOLDER = "static"; + + public const string CARD_SERVICE_BASE_ROUTE = "/service/card"; + + public const string UPLOAD_SERVICE_BASE_ROUTE = "/service/upload"; + + public const string RESPONE_SERVICE_BASE_ROUTE = "/service/respone"; + + public const string INCOM_SERVICE_BASE_ROUTE = "/service/incom"; + + public const string RANK_BASE_ROUTE = "/ranking"; + + public const string ALIVE_BASE_ROUTE = "/alive"; + + public const string SERVER_BASE_ROUTE = "/server"; + + public const string STATIC_BASE_ROUTE = "/static"; + + public const string CARD_DB_NAME = "card.db3"; + + public const string MUSIC_DB_NAME = "music.db3"; + + public const string ROOT_XPATH = "/root"; + + public const string DATA_XPATH = $"{ROOT_XPATH}/data"; + + public const string CARD = "card"; + + public const string CARD_XPATH = $"{ROOT_XPATH}/{CARD}"; + + public const string CARD_DETAIL = "card_detail"; + + public const string CARD_DETAIL_XPATH = $"{ROOT_XPATH}/{CARD_DETAIL}"; + + public const string CARD_DETAIL_RECORD_XPATH = $"{CARD_DETAIL_XPATH}/record"; + + public const string CARD_BDATA = "card_bdata"; + + public const string CARD_BDATA_XPATH = $"{ROOT_XPATH}/{CARD_BDATA}"; + + public const string MUSIC = "music"; + + public const string MUSIC_XPATH = $"{ROOT_XPATH}/{MUSIC}/record"; + + public const string MUSIC_EXTRA = "music_extra"; + + public const string MUSIC_EXTRA_XPATH = $"{ROOT_XPATH}/{MUSIC_EXTRA}/record"; + + public const string MUSIC_AOU = "music_aou"; + + public const string MUSIC_AOU_XPATH = $"{ROOT_XPATH}/{MUSIC_AOU}"; + + public const string ITEM = "item"; + + public const string ITEM_XPATH = $"{ROOT_XPATH}/{ITEM}/record"; + + public const string AVATAR = "avatar"; + + public const string AVATAR_XPATH = $"{ROOT_XPATH}/{AVATAR}/record"; + + public const string SKIN = "skin"; + + public const string SKIN_XPATH = $"{ROOT_XPATH}/{SKIN}/record"; + + public const string TITLE = "title"; + + public const string TITLE_XPATH = $"{ROOT_XPATH}/{TITLE}/record"; + + public const string NAVIGATOR = "navigator"; + + public const string NAVIGATOR_XPATH = $"{ROOT_XPATH}/{NAVIGATOR}/record"; + + public const string COIN = "coin"; + + public const string COIN_XPATH = $"{ROOT_XPATH}/{COIN}"; + + public const string UNLOCK_REWARD = "unlock_reward"; + + public const string UNLOCK_REWARD_XPATH = $"{ROOT_XPATH}/{UNLOCK_REWARD}/record"; + + public const string UNLOCK_KEYNUM = "unlock_keynum"; + + public const string UNLOCK_KEYNUM_XPATH = $"{ROOT_XPATH}/{UNLOCK_KEYNUM}/record"; + + public const string SOUND_EFFECT = "sound_effect"; + + public const string SE_XPATH = $"{ROOT_XPATH}/{SOUND_EFFECT}/record"; + + public const string GET_MESSAGE = "get_message"; + + public const string TOTAL_TROPHY = "total_trophy"; + + public const string TOTAL_TROPHY_XPATH = $"{ROOT_XPATH}/{TOTAL_TROPHY}"; + + public const string EVENT_REWARD = "event_reward"; + + public const string COND = "cond"; + + public const string SESSION_XPATH = $"{ROOT_XPATH}/session"; + + public const string RANK_STATUS_XPATH = $"{ROOT_XPATH}/ranking_status"; + + public const int GC4_EX_GID = 303801; + + public static readonly int AVATAR_COUNT = int.Parse( + ConfigurationManager.AppSettings.Get("AvatarCount") ?? "294"); + + public static readonly int NAVIGATOR_COUNT = int.Parse( + ConfigurationManager.AppSettings.Get("NavigatorCount") ?? "71"); + + public static readonly int ITEM_COUNT = int.Parse( + ConfigurationManager.AppSettings.Get("ItemCount") ?? "21"); + + public static readonly int TITLE_COUNT = int.Parse( + ConfigurationManager.AppSettings.Get("TitleCount") ?? "4942"); + + public static readonly int SKIN_COUNT = int.Parse( + ConfigurationManager.AppSettings.Get("SkinCount") ?? "21"); + + public static readonly int SE_COUNT = int.Parse( + ConfigurationManager.AppSettings.Get("SeCount") ?? "25"); +} \ No newline at end of file diff --git a/GC-local-server-rewrite/common/DatabaseHelper.cs b/GC-local-server-rewrite/common/DatabaseHelper.cs new file mode 100644 index 0000000..2499ee3 --- /dev/null +++ b/GC-local-server-rewrite/common/DatabaseHelper.cs @@ -0,0 +1,40 @@ +using SQLite.Net2; + +namespace GCLocalServerRewrite.common; + +public class DatabaseHelper +{ + /// + /// Static method to allow local data services to initialise their associated database conveniently. + /// + /// The SQLite database name + /// The SQLite database tables to create (if required) + /// An initialised SQLite database connection + public static SQLiteConnection InitializeLocalDatabase(string databaseName, params Type[] tables) + { + if (!Directory.Exists(PathHelper.DataBaseRootPath)) + { + Directory.CreateDirectory(PathHelper.DataBaseRootPath); + } + + var databasePath = Path.Combine(PathHelper.DataBaseRootPath, databaseName); + + var database = new SQLiteConnection(databasePath); + + foreach (var table in tables) + { + database.CreateTable(table); + } + + return database; + } + + public static SQLiteConnection ConnectDatabase(string databaseName) + { + var databasePath = Path.Combine(PathHelper.DataBaseRootPath, databaseName); + + var database = new SQLiteConnection(databasePath); + + return database; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/common/PathHelper.cs b/GC-local-server-rewrite/common/PathHelper.cs new file mode 100644 index 0000000..5026d69 --- /dev/null +++ b/GC-local-server-rewrite/common/PathHelper.cs @@ -0,0 +1,39 @@ +using System.Diagnostics; + +namespace GCLocalServerRewrite.common; + +public class PathHelper +{ + /// + /// Gets the local path of html/static files. + /// + public static string HtmlRootPath => Path.Combine(BasePath, Configs.STATIC_FOLDER); + + /// + /// Root path for database, when debug, it's under source root, when release, it's the exe dir + /// + public static string DataBaseRootPath => Path.Combine(BasePath, Configs.DB_FOLDER); + + public static string LogRootPath => Path.Combine(BasePath, Configs.LOG_FOLDER, Configs.LOG_BASE_NAME); + + private static string BasePath + { + get + { + var assemblyPath = Path.GetDirectoryName(typeof(Program).Assembly.Location); + +#if DEBUG + + Debug.Assert(assemblyPath != null, $"{nameof(assemblyPath)} != null"); + + var parentFullName = Directory.GetParent(assemblyPath)?.Parent?.Parent?.FullName; + + Debug.Assert(parentFullName != null, $"{nameof(parentFullName)} != null"); + + return parentFullName; +#else + return assemblyPath; +#endif + } + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/AliveController.cs b/GC-local-server-rewrite/controllers/AliveController.cs new file mode 100644 index 0000000..977697c --- /dev/null +++ b/GC-local-server-rewrite/controllers/AliveController.cs @@ -0,0 +1,34 @@ +using System.Net; +using System.Net.Mime; +using System.Text; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; + +namespace GCLocalServerRewrite.controllers; + +public class AliveController : WebApiController +{ + [Route(HttpVerbs.Get, "/i.php")] + // ReSharper disable once UnusedMember.Global + public string Check() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Html; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "REMOTE ADDRESS: 127.0.0.1\n" + + "SERVER NAME:nesys.home\n" + + "SERVER ADDR:239.1.1.1"; + } + + [Route(HttpVerbs.Get, "/{id}/Alive.txt")] + // ReSharper disable once UnusedMember.Global + public void AliveFile() + { + HttpContext.Response.SetEmptyResponse((int)HttpStatusCode.OK); + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/CardServiceController.cs b/GC-local-server-rewrite/controllers/CardServiceController.cs new file mode 100644 index 0000000..4b5b3c8 --- /dev/null +++ b/GC-local-server-rewrite/controllers/CardServiceController.cs @@ -0,0 +1,529 @@ +using System.Data; +using System.Net.Mime; +using System.Text; +using System.Xml.Linq; +using ChoETL; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; +using GCLocalServerRewrite.common; +using GCLocalServerRewrite.models; +using SQLite.Net2; +using Swan.Logging; + +namespace GCLocalServerRewrite.controllers; + +public class CardServiceController : WebApiController +{ + private readonly SQLiteConnection cardSqLiteConnection; + private readonly SQLiteConnection musicSqLiteConnection; + + public CardServiceController() + { + cardSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.CARD_DB_NAME); + musicSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.MUSIC_DB_NAME); + } + + [Route(HttpVerbs.Post, "/cardn.cgi")] + // ReSharper disable once UnusedMember.Global + public string CardService([FormField] int gid, [FormField("mac_addr")] string mac, [FormField] int type, + [FormField("card_no")] long cardId, [FormField("data")] string xmlData) + { + HttpContext.Response.ContentType = MediaTypeNames.Application.Octet; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + if (gid != Configs.GC4_EX_GID) + { + throw new ArgumentOutOfRangeException(nameof(gid)); + } + + if (!Enum.IsDefined(typeof(CardRequestType), type)) + { + throw new ArgumentOutOfRangeException(nameof(type)); + } + + var requestType = (CardRequestType)type; + + switch (requestType) + { + #region ReadRequests + + case CardRequestType.ReadCard: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(Card(cardId)); + } + case CardRequestType.ReadCardDetail: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(CardDetail(cardId, xmlData)); + } + case CardRequestType.ReadCardDetails: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(CardDetails(cardId)); + } + case CardRequestType.ReadCardBData: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(CardBData(cardId)); + } + case CardRequestType.ReadAvatar: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GetStaticCount(cardId, Configs.AVATAR_COUNT, Configs.AVATAR_XPATH)); + } + case CardRequestType.ReadItem: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GetStaticCount(cardId, Configs.ITEM_COUNT, Configs.ITEM_XPATH)); + } + case CardRequestType.ReadSkin: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GetStaticCount(cardId, Configs.SKIN_COUNT, Configs.SKIN_XPATH)); + } + case CardRequestType.ReadTitle: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GetStaticCount(cardId, Configs.TITLE_COUNT, Configs.TITLE_XPATH)); + } + case CardRequestType.ReadMusic: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(MusicUnlock()); + } + case CardRequestType.ReadEventReward: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GenerateEmptyXML(Configs.EVENT_REWARD)); + } + case CardRequestType.ReadNavigator: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GetStaticCount<Navigator>(cardId, Configs.NAVIGATOR_COUNT, Configs.NAVIGATOR_XPATH)); + } + case CardRequestType.ReadMusicExtra: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(MusicExtra()); + } + case CardRequestType.ReadMusicAou: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(GenerateEmptyXML(Configs.MUSIC_AOU)); + } + case CardRequestType.ReadCoin: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(Coin(cardId)); + } + case CardRequestType.ReadUnlockReward: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(UnlockReward(cardId)); + } + case CardRequestType.ReadUnlockKeynum: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(UnlockKeynum(cardId)); + } + case CardRequestType.ReadSoundEffect: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse( + GetStaticCount<SoundEffect>(cardId, Configs.SE_COUNT, Configs.SE_XPATH)); + } + case CardRequestType.ReadGetMessage: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(GenerateEmptyXML(Configs.GET_MESSAGE)); + } + case CardRequestType.ReadCond: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(GenerateEmptyXML(Configs.COND)); + } + case CardRequestType.ReadTotalTrophy: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(TotalTrophy(cardId)); + } + case CardRequestType.SessionStart: + case CardRequestType.SessionGet: + { + $"Getting read request, type is {requestType}".Info(); + + return ConstructResponse(GetSession(cardId, mac)); + } + + #endregion + + #region WriteRequests + + case CardRequestType.WriteCardDetail: + { + $"Getting write request, type is {requestType}\n Data is {xmlData}".Info(); + Write<CardDetail>(cardId, xmlData); + + return ConstructResponse(xmlData); + } + case CardRequestType.WriteCardBData: + { + $"Getting write request, type is {requestType}\n Data is {xmlData}".Info(); + Write<CardBData>(cardId, xmlData); + + return ConstructResponse(xmlData); + } + case CardRequestType.WriteCard: + { + $"Getting write request, type is {requestType}\n Data is {xmlData}".Info(); + Write<Card>(cardId, xmlData); + + return ConstructResponse(xmlData); + } + case CardRequestType.WriteAvatar: + case CardRequestType.WriteItem: + case CardRequestType.WriteTitle: + case CardRequestType.WriteMusicDetail: + case CardRequestType.WriteNavigator: + case CardRequestType.WriteCoin: + case CardRequestType.WriteSkin: + case CardRequestType.WriteUnlockKeynum: + case CardRequestType.WriteSoundEffect: + { + $"Getting write request, type is {requestType}\n Data is {xmlData}".Info(); + + return ConstructResponse(xmlData); + } + + #endregion + + default: +#pragma warning disable CA2208 + throw new ArgumentOutOfRangeException(nameof(requestType)); +#pragma warning restore CA2208 + } + } + + #region ReadImplementation + + private string Card(long cardId) + { + var result = cardSqLiteConnection.Table<Card>().Where(card => card.CardId == cardId); + + if (!result.Any()) + { + return GenerateEmptyXML(Configs.CARD); + } + + var card = result.First(); + + return GenerateSingleXml(card, Configs.CARD_XPATH); + } + + private string CardDetail(long cardId, string xmlData) + { + var reader = new ChoXmlReader<CardDetailReadData>(new StringReader(xmlData)); + var data = reader.Read(); + + var result = cardSqLiteConnection.Table<CardDetail>() + .Where(detail => detail.CardId == cardId && + detail.Pcol1 == data.Pcol1 && detail.Pcol2 == data.Pcol2 && + detail.Pcol3 == data.Pcol3); + + if (!result.Any()) + { + return GenerateEmptyXML(Configs.CARD_DETAIL); + } + + var cardDetail = result.First(); + + return GenerateSingleXml(cardDetail, Configs.CARD_DETAIL_RECORD_XPATH); + } + + private string CardDetails(long cardId) + { + var result = cardSqLiteConnection.Table<CardDetail>() + .Where(detail => detail.CardId == cardId); + + if (!result.Any()) + { + return GenerateEmptyXML(Configs.CARD_DETAIL); + } + + var cardDetails = result.ToList(); + + return GenerateRecordsXml(cardDetails, Configs.CARD_DETAIL_XPATH); + } + + private string CardBData(long cardId) + { + var result = cardSqLiteConnection.Table<CardBData>() + .Where(detail => detail.CardId == cardId); + + if (!result.Any()) + { + return GenerateEmptyXML(Configs.CARD_BDATA); + } + var cardBData = result.First(); + + return GenerateSingleXml(cardBData, Configs.CARD_BDATA_XPATH); + } + + private static string GetStaticCount<T>(long cardId, int count, string xpath) + where T : Record, IIdModel, ICardIdModel, new() + { + var models = new List<T>(); + + for (var id = 1; id <= count; id++) + { + var model = new T(); + model.SetId(id); + model.SetCardId(cardId); + models.Add(model); + } + + return GenerateRecordsXml(models, xpath); + } + + private static string GetSession(long cardId, string mac) + { + var session = new Session + { + CardId = cardId, + Mac = mac, + SessionId = "12345678901234567890123456789012", + Expires = 9999, + PlayerId = 1 + }; + + return GenerateSingleXml(session, Configs.SESSION_XPATH); + } + + private static string TotalTrophy(long cardId) + { + var trophy = new TotalTrophy + { + CardId = cardId, + TrophyNum = 9 + }; + + return GenerateSingleXml(trophy, Configs.TOTAL_TROPHY_XPATH); + } + + private static string Coin(long cardId) + { + var coin = new Coin + { + CardId = cardId, + CurrentCoins = 999999, + TotalCoins = 999999, + MonthlyCoins = 999999 + }; + + return GenerateSingleXml(coin, Configs.COIN_XPATH); + } + + private static string UnlockReward(long cardId) + { + var unlockRewards = new List<UnlockReward> + { + new() + { + CardId = cardId, + RewardType = 1, + RewardId = 1, + TargetId = 1, + TargetNum = 1, + KeyNum = 3 + } + }; + + return GenerateRecordsXml(unlockRewards, Configs.UNLOCK_REWARD_XPATH); + } + + private static string UnlockKeynum(long cardId) + { + var unlockKeynums = new List<UnlockKeynum> + { + new() + { + CardId = cardId, + RewardId = 1, + KeyNum = 0, + RewardCount = 1 + } + }; + + return GenerateRecordsXml(unlockKeynums, Configs.UNLOCK_KEYNUM_XPATH); + } + + private string MusicUnlock() + { + var result = musicSqLiteConnection.Table<Music>().ToList(); + + return GenerateRecordsXml(result, Configs.MUSIC_XPATH); + } + + private string MusicExtra() + { + var result = musicSqLiteConnection.Table<MusicExtra>().ToList(); + + return GenerateRecordsXml(result, Configs.MUSIC_EXTRA_XPATH); + } + + #endregion + + #region HelperMethods + + private static string ConstructResponse(string xml) + { + return "1\n" + + "10,10\n" + + xml; + } + + private static string GenerateEmptyXML(string fieldName) + { + var xml = new XDocument(new XElement("root", + new XElement(fieldName))); + xml.Declaration = new XDeclaration("1.0", "UTF-8", null); + + return xml.ToString(); + } + + private static string GenerateSingleXml<T>(T obj, string xpath) where T: class + { + var sb = new StringBuilder(); + + using (var writer = new ChoXmlWriter<T>(sb)) + { + writer.Configuration.OmitXmlDeclaration = false; + writer.Configuration.UseXmlSerialization = true; + writer.WithXPath(xpath); + + writer.Write(obj); + } + + return sb.ToString(); + } + + private static string GenerateRecordsXml<T>(IReadOnlyList<T> list, string xpath) where T : Record + { + var stringBuilder = new StringBuilder(); + + using (var writer = new ChoXmlWriter<T>(stringBuilder)) + { + writer.Configuration.OmitXmlDeclaration = false; + writer.Configuration.UseXmlSerialization = true; + writer.WithXPath(xpath); + + for (var i = 0; i < list.Count; i++) + { + var obj = list[i]; + obj.RecordId = i + 1; + writer.Write(obj); + } + } + + return stringBuilder.ToString(); + } + + #endregion + + #region WriteImplementation + + private void Write<T>(long cardId, string xmlData) where T : class, ICardIdModel + { + var reader = new ChoXmlReader<T>(new StringReader(xmlData)).WithXPath(Configs.DATA_XPATH); + var writeObject = reader.Read(); + + if (writeObject == null) + { + throw new HttpRequestException(); + } + + writeObject.SetCardId(cardId); + var rowsAffected = cardSqLiteConnection.InsertOrReplace(writeObject, typeof(T)); + + if (rowsAffected == 0) + { + throw new ApplicationException("Update database failed!"); + } + + $"Updated {typeof(T)}".Info(); + } + + #endregion + + private enum CardRequestType + { + // Read data + ReadCard = 259, + ReadCardDetail = 260, + ReadCardDetails = 261, + ReadCardBData = 264, + ReadAvatar = 418, + ReadItem = 420, + ReadSkin = 422, + ReadTitle = 424, + ReadMusic = 428, + ReadEventReward = 441, + ReadNavigator = 443, + ReadMusicExtra = 465, + ReadMusicAou = 467, + ReadCoin = 468, + ReadUnlockReward = 507, + ReadUnlockKeynum = 509, + ReadSoundEffect = 8458, + ReadGetMessage = 8461, + ReadCond = 8465, + ReadTotalTrophy = 8468, + + // Sessions + SessionGet = 401, + SessionStart = 402, + + // Write data + WriteCard = 771, + WriteCardDetail = 772, + WriteCardBData = 776, + WriteAvatar = 929, + WriteItem = 931, + WriteTitle = 935, + WriteMusicDetail = 94, + WriteNavigator = 954, + WriteCoin = 980, + WriteSkin = 933, + WriteUnlockKeynum = 1020, + WriteSoundEffect = 8969 + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/IncomServiceController.cs b/GC-local-server-rewrite/controllers/IncomServiceController.cs new file mode 100644 index 0000000..4f17fd0 --- /dev/null +++ b/GC-local-server-rewrite/controllers/IncomServiceController.cs @@ -0,0 +1,32 @@ +using System.Net.Mime; +using System.Text; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; + +namespace GCLocalServerRewrite.controllers; + +public class IncomServiceController : WebApiController +{ + [Route(HttpVerbs.Post, "/incom.php")] + // ReSharper disable once UnusedMember.Global + public string IncomService() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "1+1"; + } + + [Route(HttpVerbs.Post, "/incomALL.php")] + // ReSharper disable once UnusedMember.Global + public string IncomAllService() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "1+1"; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/RankController.cs b/GC-local-server-rewrite/controllers/RankController.cs new file mode 100644 index 0000000..85883fc --- /dev/null +++ b/GC-local-server-rewrite/controllers/RankController.cs @@ -0,0 +1,109 @@ +using System.Net.Mime; +using System.Text; +using System.Xml; +using ChoETL; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; +using GCLocalServerRewrite.common; +using GCLocalServerRewrite.models; +using Swan; +using Swan.Logging; +// ReSharper disable UnusedMember.Global + +namespace GCLocalServerRewrite.controllers; + +public class RankController : WebApiController +{ + [Route(HttpVerbs.Get, "/ranking.php")] + public string Rank([QueryField("cmd_type")] int type) + { + HttpContext.Response.ContentType = MediaTypeNames.Application.Octet; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + if (!Enum.IsDefined(typeof(RankType), type)) + { + throw new ArgumentOutOfRangeException(nameof(type)); + } + + var requestType = (RankType)type; + + switch (requestType) + { + case RankType.GlobalRank: + case RankType.UnknownRank1: + case RankType.UnknownRank2: + case RankType.UnknownRank3: + $"Getting rank request, type is {requestType}".Info(); + + return ConstructResponse(RankTemp("score_rank")); + case RankType.PlayNumRank: + $"Getting rank request, type is {requestType}".Info(); + + return ConstructResponse(RankTemp("play_num_rank")); + case RankType.EventRank: + $"Getting rank request, type is {requestType}".Info(); + + return ConstructResponse(RankTemp("event_rank")); + default: +#pragma warning disable CA2208 + throw new ArgumentOutOfRangeException(nameof(requestType)); +#pragma warning restore CA2208 + } + } + + private static string ConstructResponse(string xml) + { + return "1\n" + + xml; + } + + // TODO: Add proper rank support + private static string RankTemp(string rankType) + { + var rankStatus = new RankStatus + { + Rows = 0, + Status = 0 + }; + + var sb = new StringBuilder(); + + using (var writer = new ChoXmlWriter<RankStatus>(sb)) + { + writer.Configuration.OmitXmlDeclaration = false; + writer.Configuration.UseXmlSerialization = true; + writer.WithXPath(Configs.RANK_STATUS_XPATH); + + writer.Write(rankStatus); + } + + var document = new XmlDocument(); + document.LoadXml(sb.ToString()); + var root = document.DocumentElement; + + if (root == null) + { + throw SelfCheck.Failure("Internal XML error!"); + } + + root.AppendChild(document.CreateElement(rankType)); + + var stringWriter = new StringWriter(); + var xmlTextWriter = new XmlTextWriter(stringWriter); + document.WriteTo(xmlTextWriter); + + return stringWriter.ToString(); + } + + private enum RankType + { + GlobalRank = 4119, + PlayNumRank = 6657, + EventRank = 6661, + UnknownRank1 = 6666, + UnknownRank2 = 6667, + UnknownRank3 = 4098 + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/ResponeServiceController.cs b/GC-local-server-rewrite/controllers/ResponeServiceController.cs new file mode 100644 index 0000000..edfedc7 --- /dev/null +++ b/GC-local-server-rewrite/controllers/ResponeServiceController.cs @@ -0,0 +1,21 @@ +using System.Net.Mime; +using System.Text; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; + +namespace GCLocalServerRewrite.controllers; + +public class ResponeServiceController : WebApiController +{ + [Route(HttpVerbs.Post, "/respone.php")] + // ReSharper disable once UnusedMember.Global + public string ResponeService() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "1"; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/ServerController.cs b/GC-local-server-rewrite/controllers/ServerController.cs new file mode 100644 index 0000000..a5a4eba --- /dev/null +++ b/GC-local-server-rewrite/controllers/ServerController.cs @@ -0,0 +1,138 @@ +using System.Collections.Specialized; +using System.Net.Mime; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; +using GCLocalServerRewrite.common; +using Swan.Logging; + +// ReSharper disable UnusedMember.Global + +namespace GCLocalServerRewrite.controllers; + +public class ServerController : WebApiController +{ + [Route(HttpVerbs.Get, "/certify.php")] + public string Certify([QueryData] NameValueCollection parameters) + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + var gid = parameters["gid"]; + var mac = parameters["mac"]; + var random = parameters["r"]; + var hash = parameters["md"]; + + if (gid == null) + { + return QuitWithError(ErrorCode.ErrorNoGid); + } + + if (mac == null) + { + return QuitWithError(ErrorCode.ErrorNoMac); + } + + if (random == null) + { + return QuitWithError(ErrorCode.ErrorNoRandom); + } + + if (hash == null) + { + return QuitWithError(ErrorCode.ErrorNoHash); + } + + if (!MacValid(mac)) + { + return QuitWithError(ErrorCode.ErrorInvalidMac); + } + + if (!Md5Valid(hash)) + { + return QuitWithError(ErrorCode.ErrorInvalidHash); + } + + using var md5 = MD5.Create(); + + var ticket = string.Join(string.Empty, + md5.ComputeHash(Encoding.UTF8.GetBytes(gid)).Select(b => b.ToString("x2"))); + + + var response = "host=card_id=7020392000147361\n" + + "no=1337\n" + + "name=123\n" + + "pref=nesys\n" + + "addr=nesys@home\n" + + "x-next-time=15\n" + + $"x-img=http://localhost{Configs.STATIC_BASE_ROUTE}/news.png\n" + + $"x-ranking=http://localhost{Configs.RANK_BASE_ROUTE}/ranking.php\n" + + $"ticket={ticket}"; + + return response; + } + + [Route(HttpVerbs.Get, "/cursel.php")] + public string Cursel() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "1\n"; + } + + [Route(HttpVerbs.Get, "/data.php")] + public string Data() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "count=1\n" + + "nexttime=0\n"; + } + + [Route(HttpVerbs.Get, "/gameinfo.php")] + public string GameInfo() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "0\n" + + "3\n" + + "301000,test1\n" + + "302000,test2\n" + + "303000,test3"; + } + + private static bool MacValid(string mac) + { + return Regex.IsMatch(mac, "^[a-fA-F0-9]{12}$"); + } + + private static bool Md5Valid(string md5) + { + return Regex.IsMatch(md5, "^[a-fA-F0-9]{32}$"); + } + + private static string QuitWithError(ErrorCode errorCode) + { + return $"error={(int)errorCode}"; + } + + private enum ErrorCode + { + ErrorNoGid, + ErrorNoMac, + ErrorNoRandom, + ErrorNoHash, + ErrorInvalidMac, + ErrorInvalidHash + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/controllers/UploadServiceController.cs b/GC-local-server-rewrite/controllers/UploadServiceController.cs new file mode 100644 index 0000000..acaf1dc --- /dev/null +++ b/GC-local-server-rewrite/controllers/UploadServiceController.cs @@ -0,0 +1,22 @@ +using System.Net.Mime; +using System.Text; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; + +namespace GCLocalServerRewrite.controllers; + +public class UploadServiceController : WebApiController +{ + [Route(HttpVerbs.Post, "/upload.php")] + // ReSharper disable once UnusedMember.Global + public string UploadService() + { + HttpContext.Response.ContentType = MediaTypeNames.Text.Plain; + HttpContext.Response.ContentEncoding = Encoding.Default; + HttpContext.Response.KeepAlive = true; + + return "1\n" + + "OK"; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/db/card.db3 b/GC-local-server-rewrite/db/card.db3 new file mode 100644 index 0000000000000000000000000000000000000000..cf62926b7a305678fbadfd79d91dba6f25d7e3c1 GIT binary patch literal 20480 zcmeI&!A{#S7zc2Nb`%x0<JKc?DkQ4GBq;1WhI&9kLSx)5H!+*FGzp1==%gKnIPg?F z1P{V95WAqbC8>~htNxLiI{w`F^Y8P;Qh$6ul}gadC>e7_2V{dd4*5zcA*7anKIEU* zOF!3NAM$U<YF9OD<n!6zR(p%o*B?mxvAw(g(0ZyGjD83}00Izz00bZa0SG`~k-*(< zt<mZAocpfgqd<5(@tH3amx0x+AD<78E{62t=-cU#x>m%c+q60DkiJW0sKif^P#Q*x zhFK6$N_U!W>_x%e!s!0O=%6y1dQl>nv|m1*avI1MMC>VNe`8NK8#%BNnT;G)M!r-; zMkTogJXI_X_=G7D#u3w|O5CV?E(3Wv2U+yKU5y_5b|vQ`7xPc-TA7!`i5&A}LVt@1 z-8Kc5{*I}ex$m3%px-~NuXav)4w0cRZqlnDKl(AAsmMGtYiF=M>p>p^*Yr00=dLt5 z-L7-{vDj`SpDS(#n^vu4vzfsA_S(x(Tg`N_RQ?h3r6^l<zkmD5X>>L>ox80f$uXCq z$x$~}N>bGQ@5@lvS0pUt<2)Zb&&tjG|7GXiFDb4COBGjHIy<eG2(HB3PRruFDs!FX zS}k4I4SmA}0SG_<0uX=z1Rwwb2tWV=5P-n43gG;|td+&OAOHafKmY;|fB*y_009U< z00M;o&i{o4S`dH$1Rwwb2tWV=5P$##AOL}76=>>(;s5{3T3)OR0uX=z1Rwwb2tWV= M5P$##AYcTZ0Y(8-&j0`b literal 0 HcmV?d00001 diff --git a/GC-local-server-rewrite/db/music.db3 b/GC-local-server-rewrite/db/music.db3 new file mode 100644 index 0000000000000000000000000000000000000000..2067cc649e5d8145470e1d21d0803efeaf73b2bd GIT binary patch literal 73728 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 literal 0 HcmV?d00001 diff --git a/GC-local-server-rewrite/models/Avatar.cs b/GC-local-server-rewrite/models/Avatar.cs new file mode 100644 index 0000000..75de876 --- /dev/null +++ b/GC-local-server-rewrite/models/Avatar.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Avatar : Record , IIdModel, ICardIdModel +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "avatar_id")] + public int AvatarId { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; + + [XmlElement("new_flag")] + public int NewFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + public void SetId(int id) + { + AvatarId = id; + } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Card.cs b/GC-local-server-rewrite/models/Card.cs new file mode 100644 index 0000000..4faafc2 --- /dev/null +++ b/GC-local-server-rewrite/models/Card.cs @@ -0,0 +1,60 @@ +using System.Xml.Serialization; +using ChoETL; +using SQLite.Net2; + +namespace GCLocalServerRewrite.models; + +[Table("card_main")] +public class Card : ICardIdModel +{ + [PrimaryKey] + [Column("card_id")] + [ChoXmlElementRecordField(FieldName = "card_id")] + [XmlElement(ElementName = "card_id")] + public long? CardId { get; set; } + + [Column("player_name")] + [ChoXmlElementRecordField(FieldName = "player_name")] + [XmlElement(ElementName = "player_name")] + public string? PlayerName { get; set; } + + [Column("score_i1")] + [ChoXmlElementRecordField(FieldName = "score_i1")] + [XmlElement("score_i1")] + public int Score { get; set; } + + [Column("fcol1")] + [ChoXmlElementRecordField(FieldName = "fcol1")] + [XmlElement("fcol1")] + public int Fcol1 { get; set; } + + [Column("fcol2")] + [ChoXmlElementRecordField(FieldName = "fcol2")] + [XmlElement("fcol2")] + public int Fcol2 { get; set; } + + [Column("fcol3")] + [ChoXmlElementRecordField(FieldName = "fcol3")] + [XmlElement("fcol3")] + public int Fcol3 { get; set; } + + [Column("achieve_status")] + [ChoXmlElementRecordField(FieldName = "achieve_status")] + [XmlElement("achieve_status")] + public string? AchieveStatus { get; set; } = string.Empty; + + [Column("created")] + [ChoXmlElementRecordField(FieldName = "created")] + [XmlElement("created")] + public string Created { get; set; } = "1"; + + [Column("updated")] + [ChoXmlElementRecordField(FieldName = "updated")] + [XmlElement("updated")] + public string Updated { get; set; } = "1"; + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/CardBData.cs b/GC-local-server-rewrite/models/CardBData.cs new file mode 100644 index 0000000..e7b098a --- /dev/null +++ b/GC-local-server-rewrite/models/CardBData.cs @@ -0,0 +1,30 @@ +using System.Xml.Serialization; +using ChoETL; +using SQLite.Net2; + +namespace GCLocalServerRewrite.models; + +[Table("card_bdata")] +public class CardBData : ICardIdModel +{ + [PrimaryKey] + [Column("card_id")] + [ChoXmlElementRecordField(FieldName = "card_id")] + [XmlElement(ElementName = "card_id")] + public long? CardId { get; set; } + + [Column("bdata")] + [ChoXmlElementRecordField(FieldName = "bdata")] + [XmlElement(ElementName = "bdata")] + public string BData { get; set; } = string.Empty; + + [Column("bdata_size")] + [ChoXmlElementRecordField(FieldName = "bdata_size")] + [XmlElement(ElementName = "bdata_size")] + public int BDataSize { get; set; } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/CardDetail.cs b/GC-local-server-rewrite/models/CardDetail.cs new file mode 100644 index 0000000..ad2cd56 --- /dev/null +++ b/GC-local-server-rewrite/models/CardDetail.cs @@ -0,0 +1,98 @@ +using System.Xml.Serialization; +using ChoETL; +using SQLite.Net2; + +namespace GCLocalServerRewrite.models; + +[Table("card_detail")] +public class CardDetail : Record, ICardIdModel +{ + [PrimaryKey] + [Column("card_id")] + [ChoXmlElementRecordField(FieldName = "card_id")] + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [PrimaryKey] + [Column("pcol1")] + [ChoXmlElementRecordField(FieldName = "pcol1")] + [XmlElement(ElementName = "pcol1")] + public int Pcol1 { get; set; } + + [PrimaryKey] + [Column("pcol2")] + [ChoXmlElementRecordField(FieldName = "pcol2")] + [XmlElement(ElementName = "pcol2")] + public int Pcol2 { get; set; } + + [PrimaryKey] + [Column("pcol3")] + [ChoXmlElementRecordField(FieldName = "pcol3")] + [XmlElement(ElementName = "pcol3")] + public int Pcol3 { get; set; } + + [Column("score_i1")] + [ChoXmlElementRecordField(FieldName = "score_i1")] + [XmlElement(ElementName = "score_i1")] + public long ScoreI1 { get; set; } + + [Column("score_ui1")] + [ChoXmlElementRecordField(FieldName = "score_ui1")] + [XmlElement(ElementName = "score_ui1")] + public long ScoreUi1 { get; set; } + + [Column("score_ui2")] + [ChoXmlElementRecordField(FieldName = "score_ui2")] + [XmlElement(ElementName = "score_ui2")] + public long ScoreUi2 { get; set; } + + [Column("score_ui3")] + [ChoXmlElementRecordField(FieldName = "score_ui3")] + [XmlElement(ElementName = "score_ui3")] + public long ScoreUi3 { get; set; } + + [Column("score_ui4")] + [ChoXmlElementRecordField(FieldName = "score_ui4")] + [XmlElement(ElementName = "score_ui4")] + public long ScoreUi4 { get; set; } + + [Column("score_ui5")] + [ChoXmlElementRecordField(FieldName = "score_ui5")] + [XmlElement(ElementName = "score_ui5")] + public long ScoreUi5 { get; set; } + + [Column("score_ui6")] + [ChoXmlElementRecordField(FieldName = "score_ui6")] + [XmlElement(ElementName = "score_ui6")] + public long ScoreUi6 { get; set; } + + [Column("score_bi1")] + [ChoXmlElementRecordField(FieldName = "score_bi1")] + [XmlElement(ElementName = "score_bi1")] + public long ScoreBi1 { get; set; } + + [Column("last_play_tenpo_id")] + [ChoXmlElementRecordField(FieldName = "last_play_tenpo_id")] + [XmlElement(ElementName = "last_play_tenpo_id")] + public string LastPlayShopId { get; set; } = "1337"; + + [Column("fcol1")] + [ChoXmlElementRecordField(FieldName = "fcol1")] + [XmlElement("fcol1")] + public int Fcol1 { get; set; } + + [Column("fcol2")] + [ChoXmlElementRecordField(FieldName = "fcol2")] + [XmlElement("fcol2")] + public int Fcol2 { get; set; } + + [Column("fcol3")] + [ChoXmlElementRecordField(FieldName = "fcol3")] + [XmlElement("fcol3")] + public int Fcol3 { get; set; } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/CardDetailReadData.cs b/GC-local-server-rewrite/models/CardDetailReadData.cs new file mode 100644 index 0000000..395f6bb --- /dev/null +++ b/GC-local-server-rewrite/models/CardDetailReadData.cs @@ -0,0 +1,20 @@ +using System.Xml.Serialization; +using ChoETL; + +namespace GCLocalServerRewrite.models; + +[ChoXmlRecordObject(XPath = "/root/data")] +public class CardDetailReadData +{ + [XmlElement(ElementName = "pcol1")] + [ChoXmlElementRecordField(FieldName = "pcol1")] + public int Pcol1 { get; set; } + + [XmlElement(ElementName = "pcol2")] + [ChoXmlElementRecordField(FieldName = "pcol2")] + public int Pcol2 { get; set; } + + [XmlElement(ElementName = "pcol3")] + [ChoXmlElementRecordField(FieldName = "pcol3")] + public int Pcol3 { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Coin.cs b/GC-local-server-rewrite/models/Coin.cs new file mode 100644 index 0000000..c501202 --- /dev/null +++ b/GC-local-server-rewrite/models/Coin.cs @@ -0,0 +1,24 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Coin +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "current")] + public int CurrentCoins { get; set; } + + [XmlElement(ElementName = "total")] + public int TotalCoins { get; set; } + + [XmlElement("monthly")] + public int MonthlyCoins { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/ICardIdModel.cs b/GC-local-server-rewrite/models/ICardIdModel.cs new file mode 100644 index 0000000..a8080b7 --- /dev/null +++ b/GC-local-server-rewrite/models/ICardIdModel.cs @@ -0,0 +1,6 @@ +namespace GCLocalServerRewrite.models; + +public interface ICardIdModel +{ + public void SetCardId(long cardId); +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/IIdModel.cs b/GC-local-server-rewrite/models/IIdModel.cs new file mode 100644 index 0000000..4e065b6 --- /dev/null +++ b/GC-local-server-rewrite/models/IIdModel.cs @@ -0,0 +1,6 @@ +namespace GCLocalServerRewrite.models; + +public interface IIdModel +{ + public void SetId(int id); +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Item.cs b/GC-local-server-rewrite/models/Item.cs new file mode 100644 index 0000000..7eac5f1 --- /dev/null +++ b/GC-local-server-rewrite/models/Item.cs @@ -0,0 +1,37 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Item : Record, IIdModel, ICardIdModel +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "item_id")] + public int ItemId { get; set; } + + [XmlElement(ElementName = "item_num")] + public int ItemNum { get; set; } = 90; + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; + + [XmlElement("new_flag")] + public int NewFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + public void SetId(int id) + { + ItemId = id; + } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Music.cs b/GC-local-server-rewrite/models/Music.cs new file mode 100644 index 0000000..1eb2c9e --- /dev/null +++ b/GC-local-server-rewrite/models/Music.cs @@ -0,0 +1,37 @@ +using System.Xml.Serialization; +using SQLite.Net2; + +namespace GCLocalServerRewrite.models; + +[Table("music_unlock")] +public class Music : Record +{ + [PrimaryKey] + [Column("music_id")] + [XmlElement("music_id")] + public int MusicId { get; set; } + + [Column("title")] + [XmlElement(ElementName = "title", IsNullable = true)] + public string? Title { get; set; } + + [Column("artist")] + [XmlElement(ElementName = "artist", IsNullable = true)] + public string? Artist { get; set; } + + [Column("release_date")] + [XmlElement(ElementName = "release_date", IsNullable = true)] + public string? ReleaseDate { get; set; } + + [Column("end_date")] + [XmlElement(ElementName = "end_date", IsNullable = true)] + public string? EndDate { get; set; } + + [Column("new_flag")] + [XmlElement("new_flag")] + public int NewFlag { get; set; } + + [Column("use_flag")] + [XmlElement("use_flag")] + public int UseFlag { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/MusicAou.cs b/GC-local-server-rewrite/models/MusicAou.cs new file mode 100644 index 0000000..d54e7dc --- /dev/null +++ b/GC-local-server-rewrite/models/MusicAou.cs @@ -0,0 +1,37 @@ +using System.Xml.Serialization; +using SQLite.Net2; + +namespace GCLocalServerRewrite.models; + +[Table("music_aou")] +public class MusicAou : Record +{ + [PrimaryKey] + [Column("music_id")] + [XmlElement("music_id")] + public int MusicId { get; set; } + + [Column("title")] + [XmlElement(ElementName = "title", IsNullable = true)] + public string? Title { get; set; } + + [Column("artist")] + [XmlElement(ElementName = "artist", IsNullable = true)] + public string? Artist { get; set; } + + [Column("release_date")] + [XmlElement(ElementName = "release_date", IsNullable = true)] + public string? ReleaseDate { get; set; } + + [Column("end_date")] + [XmlElement(ElementName = "end_date", IsNullable = true)] + public string? EndDate { get; set; } + + [Column("new_flag")] + [XmlElement("new_flag")] + public int NewFlag { get; set; } + + [Column("use_flag")] + [XmlElement("use_flag")] + public int UseFlag { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/MusicExtra.cs b/GC-local-server-rewrite/models/MusicExtra.cs new file mode 100644 index 0000000..c4e1d1e --- /dev/null +++ b/GC-local-server-rewrite/models/MusicExtra.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using SQLite.Net2; + +namespace GCLocalServerRewrite.models; + +[Table("music_extra")] +public class MusicExtra : Record +{ + [PrimaryKey] + [Column("music_id")] + [XmlElement("music_id")] + public int MusicId { get; set; } + + [Column("use_flag")] + [XmlElement("use_flag")] + public int UseFlag { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Navigator.cs b/GC-local-server-rewrite/models/Navigator.cs new file mode 100644 index 0000000..f1f933c --- /dev/null +++ b/GC-local-server-rewrite/models/Navigator.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Navigator : Record, IIdModel, ICardIdModel +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "navigator_id")] + public int NavigatorId { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; + + [XmlElement("new_flag")] + public int NewFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + public void SetId(int id) + { + NavigatorId = id; + } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/RankStatus.cs b/GC-local-server-rewrite/models/RankStatus.cs new file mode 100644 index 0000000..8b0fd67 --- /dev/null +++ b/GC-local-server-rewrite/models/RankStatus.cs @@ -0,0 +1,21 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class RankStatus +{ + [XmlElement("table_name")] + public string? TableName { get; set; } + + [XmlElement("start_date")] + public string StartDate { get; set; } = "2017-05-30"; + + [XmlElement("end_date")] + public string EndDate { get; set; } = "2018-05-30"; + + [XmlElement("status")] + public int Status { get; set; } + + [XmlElement("rows")] + public int Rows { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Record.cs b/GC-local-server-rewrite/models/Record.cs new file mode 100644 index 0000000..0ea53ef --- /dev/null +++ b/GC-local-server-rewrite/models/Record.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; +using ChoETL; + +namespace GCLocalServerRewrite.models; + +public class Record +{ + [XmlAttribute(AttributeName = "id")] + public int RecordId { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Session.cs b/GC-local-server-rewrite/models/Session.cs new file mode 100644 index 0000000..f0d9b41 --- /dev/null +++ b/GC-local-server-rewrite/models/Session.cs @@ -0,0 +1,21 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Session +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "mac_addr")] + public string? Mac { get; set; } + + [XmlElement(ElementName = "session_id")] + public string? SessionId { get; set; } + + [XmlElement(ElementName = "expires")] + public int Expires { get; set; } + + [XmlElement(ElementName = "player_id")] + public int PlayerId { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Skin.cs b/GC-local-server-rewrite/models/Skin.cs new file mode 100644 index 0000000..84351d5 --- /dev/null +++ b/GC-local-server-rewrite/models/Skin.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Skin : Record, IIdModel, ICardIdModel +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "skin_id")] + public int SkinId { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; + + [XmlElement("new_flag")] + public int NewFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + public void SetId(int id) + { + SkinId = id; + } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/SoundEffect.cs b/GC-local-server-rewrite/models/SoundEffect.cs new file mode 100644 index 0000000..e673953 --- /dev/null +++ b/GC-local-server-rewrite/models/SoundEffect.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class SoundEffect : Record, IIdModel, ICardIdModel +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "sound_effect_id")] + public int SeId { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; + + [XmlElement("new_flag")] + public int NewFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + public void SetId(int id) + { + SeId = id; + } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/Title.cs b/GC-local-server-rewrite/models/Title.cs new file mode 100644 index 0000000..6a0cd0a --- /dev/null +++ b/GC-local-server-rewrite/models/Title.cs @@ -0,0 +1,34 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class Title : Record, IIdModel, ICardIdModel +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "title_id")] + public int TitleId { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; + + [XmlElement("new_flag")] + public int NewFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + public void SetId(int id) + { + TitleId = id; + } + + public void SetCardId(long cardId) + { + CardId = cardId; + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/TotalTrophy.cs b/GC-local-server-rewrite/models/TotalTrophy.cs new file mode 100644 index 0000000..8ecc5aa --- /dev/null +++ b/GC-local-server-rewrite/models/TotalTrophy.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class TotalTrophy +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "total_trophy_num")] + public int TrophyNum { get; set; } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/UnlockKeynum.cs b/GC-local-server-rewrite/models/UnlockKeynum.cs new file mode 100644 index 0000000..8510f66 --- /dev/null +++ b/GC-local-server-rewrite/models/UnlockKeynum.cs @@ -0,0 +1,33 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class UnlockKeynum : Record +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "reward_id")] + public int RewardId { get; set; } + + [XmlElement(ElementName = "key_num")] + public int KeyNum { get; set; } + + [XmlElement(ElementName = "reward_count")] + public int RewardCount { get; set; } + + [XmlElement("expired_flag")] + public int ExpiredFlag { get; set; } + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + [XmlElement("cash_flag")] + public int CashFlag { get; set; } + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; +} \ No newline at end of file diff --git a/GC-local-server-rewrite/models/UnlockReward.cs b/GC-local-server-rewrite/models/UnlockReward.cs new file mode 100644 index 0000000..226b61e --- /dev/null +++ b/GC-local-server-rewrite/models/UnlockReward.cs @@ -0,0 +1,57 @@ +using System.Xml.Serialization; + +namespace GCLocalServerRewrite.models; + +public class UnlockReward : Record +{ + [XmlElement(ElementName = "card_id")] + public long CardId { get; set; } + + [XmlElement(ElementName = "reward_id")] + public int RewardId { get; set; } + + [XmlElement(ElementName = "reward_type")] + public int RewardType { get; set; } + + [XmlElement(ElementName = "open_date")] + public string? OpenDate { get; set; } = "2021-05-30"; + + [XmlElement(ElementName = "close_date")] + public string? CloseDate { get; set; } = "2030-05-30"; + + [XmlElement(ElementName = "open_time")] + public string? OpenTime { get; set; } = "00:00:01"; + + [XmlElement(ElementName = "close_time")] + public string? CloseTime { get; set; } = "23:59:59"; + + [XmlElement(ElementName = "target_id")] + public int TargetId { get; set; } + + [XmlElement(ElementName = "target_num")] + public int TargetNum { get; set; } + + [XmlElement(ElementName = "key_num")] + public int KeyNum { get; set; } + + [XmlElement("display_flag")] + public int DisplayFlag { get; set; } = 1; + + [XmlElement("use_flag")] + public int UseFlag { get; set; } = 1; + + [XmlElement("limited_flag")] + public int LimitedFlag { get; set; } + + [XmlElement("open_unixtime")] + public long OpenUnixTime { get; set; } = 1622304001; + + [XmlElement("close_unixtime")] + public long CloseUnixTime { get; set; } = 1906387199; + + [XmlElement("created")] + public string? Created { get; set; } = "1"; + + [XmlElement("modified")] + public string? Modified { get; set; } = "1"; +} \ No newline at end of file diff --git a/GC-local-server-rewrite/server/Server.cs b/GC-local-server-rewrite/server/Server.cs new file mode 100644 index 0000000..46889e3 --- /dev/null +++ b/GC-local-server-rewrite/server/Server.cs @@ -0,0 +1,65 @@ +using EmbedIO; +using EmbedIO.Actions; +using EmbedIO.Files; +using EmbedIO.WebApi; +using GCLocalServerRewrite.backports; +using GCLocalServerRewrite.common; +using GCLocalServerRewrite.controllers; +using GCLocalServerRewrite.models; +using Swan.Logging; + +namespace GCLocalServerRewrite.server; + +public class Server +{ + public static WebServer CreateWebServer(string[] urlPrefixes) + { + InitializeDatabase(); + var cert = CertificateHelper.InitializeCertificate(); + + var server = new WebServer(webServerOptions => webServerOptions + .WithUrlPrefixes(urlPrefixes) + .WithCertificate(cert) + .WithMode(HttpListenerMode.EmbedIO)) + .WithLocalSessionManager() + .WithCors( + "http://unosquare.github.io,http://run.plnkr.co", + "content-type, accept", + "post") + .WithWebApi(Configs.CARD_SERVICE_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<CardServiceController>()) + .WithWebApi(Configs.UPLOAD_SERVICE_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<UploadServiceController>()) + .WithWebApi(Configs.RESPONE_SERVICE_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<ResponeServiceController>()) + .WithWebApi(Configs.INCOM_SERVICE_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<IncomServiceController>()) + .WithWebApi(Configs.ALIVE_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<AliveController>()) + .WithWebApi(Configs.SERVER_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<ServerController>()) + .WithWebApi(Configs.RANK_BASE_ROUTE, CustomResponseSerializer.None(true), + module => module.WithController<RankController>()) + .WithStaticFolder(Configs.STATIC_BASE_ROUTE, PathHelper.HtmlRootPath, true, m => m + .WithContentCaching(Configs.USE_FILE_CACHE)) + + // Add static files after other modules to avoid conflicts + .WithStaticFolder("/", PathHelper.HtmlRootPath, true, m => m + .WithContentCaching(Configs.USE_FILE_CACHE)) + .WithModule(new ActionModule("/", HttpVerbs.Any, + ctx => ctx.SendDataAsync(new { Message = "Error" }))); + + // Listen for state changes. + server.StateChanged += (_, e) => $"WebServer New State - {e.NewState}".Info(); + + return server; + } + + private static void InitializeDatabase() + { + DatabaseHelper.InitializeLocalDatabase(Configs.CARD_DB_NAME, + typeof(Card), typeof(CardBData), typeof(CardDetail)); + DatabaseHelper.InitializeLocalDatabase(Configs.MUSIC_DB_NAME, + typeof(Music), typeof(MusicAou), typeof(MusicExtra)); + } +} \ No newline at end of file diff --git a/GC-local-server-rewrite/static/favicon.ico b/GC-local-server-rewrite/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..993e925688fc1639129b252666a11086034f1b5b GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/GC-local-server-rewrite/static/index.html b/GC-local-server-rewrite/static/index.html new file mode 100644 index 0000000..5ece4b5 --- /dev/null +++ b/GC-local-server-rewrite/static/index.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title + + +Hello world! + + \ No newline at end of file diff --git a/GC-local-server-rewrite/static/news.png b/GC-local-server-rewrite/static/news.png new file mode 100644 index 0000000000000000000000000000000000000000..f7208594bc09cb5a18550486f1300c2910010dfd GIT binary patch literal 818 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&w@L)Zt|+CxDb>iEBhjaDG}zd16s2gJVj5 zQmTSyZen_BP-y?a~42pceQbHDC5)qjmdynf;cUac54jG%yG-FgY-A zFfb}Gtn0DcapUM=e>JK1ZVXHU4Gb&_42&EOU?xyi!Xt)H=8h1hEMR>JO$^tTb3qh< z)G9E5#lN!@{JnV~!Th7OB1C`*XhB2$U5-_H_wVO;e;%(0N+3dYDd(|#ZQ^`)CFHjd zHfbh-1AiGs-m|C)f9h2r&F%+B8FT!*?E8^p4(?QpprTa3=2CbABg1TE7bg`3QzMXR Mp00i_>zopr0EX$D4gdfE literal 0 HcmV?d00001