wip executable patcher

This commit is contained in:
Jill 2023-01-08 01:25:37 +03:00
parent 7ec85e78b0
commit 44f2cc50ef
2 changed files with 159 additions and 43 deletions

View File

@ -26,10 +26,14 @@ require "./lib/creator_points"
require "./lib/versions"
require "./lib/ips"
require "./patch-exe.cr"
if File.exists?(".env")
Dotenv.load
end
include CrystalGauntlet::PatchExe
module CrystalGauntlet
VERSION = "0.1.0"
@ -211,6 +215,8 @@ module CrystalGauntlet
migrate = false
calc_creator_points = false
patch_exe = false
patch_exe_location = nil
parser = OptionParser.new do |parser|
parser.banner = "Usage: crystal-gauntlet [command] [arguments]"
@ -223,6 +229,13 @@ module CrystalGauntlet
calc_creator_points = true
parser.banner = "Usage: crystal-gauntlet calc_creator_points [arguments]"
end
parser.on("patch_exe", "Patch Geometry Dash executables with your server URL (supports #{SUPPORTED_PATCH_PLATFORMS.join(", ")})") do
patch_exe = true
parser.banner = "Usage: crystal-gauntlet patch_exe <file>"
parser.unknown_args do |opt|
patch_exe_location = opt[0]?
end
end
parser.on("-h", "--help", "Show this help") do
puts parser
exit
@ -231,6 +244,17 @@ module CrystalGauntlet
parser.parse
if patch_exe
if !patch_exe_location
puts parser
exit 1
end
check_server_length(true)
LOG.info { "Patching #{patch_exe_location}" }
patch_exe_file(patch_exe_location.not_nil!)
exit
end
migrator = Migrate::Migrator.new(
DATABASE
)
@ -238,7 +262,10 @@ module CrystalGauntlet
if migrate
LOG.info { "Migrating #{ENV["DATABASE_URL"].colorize(:white)}..." }
migrator.to_latest
elsif calc_creator_points
exit
end
if calc_creator_points
LOG.info { "updating creator points" }
DATABASE.query_all("select id, username, creator_points from users", as: {Int32, String, Int32}).each() do |id, username, old_count|
new_count = CreatorPoints.update_creator_points id
@ -246,50 +273,43 @@ module CrystalGauntlet
LOG.info { "#{username}: #{old_count} -> #{new_count}" }
end
end
else
if !migrator.latest?
LOG.fatal { "Database hasn\'t been migrated!! Please run #{"crystal-gauntlet migrate".colorize(:white)}" }
return
end
["songs", "levels", "saves"].each() { |v|
Dir.mkdir_p(DATA_FOLDER / v)
}
server = HTTP::Server.new([
HTTP::LogHandler.new,
HTTP::StaticFileHandler.new("public/", fallthrough: true, directory_listing: false),
HTTP::StaticFileHandler.new((DATA_FOLDER / "songs").to_s, fallthrough: true, directory_listing: false),
CrystalGauntlet::GDHandler.new,
CrystalGauntlet::TemplateHandler.new
])
listen_on = URI.parse(ENV["LISTEN_ON"]? || "http://localhost:8080").normalize
case listen_on.scheme
when "http"
server.bind_tcp(listen_on.hostname.not_nil!, listen_on.port.not_nil!)
when "unix"
server.bind_unix(listen_on.to_s.sub("unix://",""))
end
full_server_path = config_get("general.hostname", "") + "/" + config_get("general.append_path", "")
robtop_server_path = "www.boomlings.com/database/"
if full_server_path.size != robtop_server_path.size
LOG.warn { "i think you made a mistake? length of full server path and default .exe location do not match" }
LOG.warn { " #{full_server_path}" }
LOG.warn { " #{robtop_server_path}" }
min_length = Math.min(full_server_path.size, robtop_server_path.size)
max_length = Math.max(full_server_path.size, robtop_server_path.size)
LOG.warn { " #{" " * min_length}#{"^" * (max_length - min_length)}"}
end
Reupload.init()
@@up_at = Time.utc
LOG.notice { "Listening on #{listen_on.to_s.colorize(:white)}" }
server.listen
exit
end
if !migrator.latest?
LOG.fatal { "Database hasn\'t been migrated!! Please run #{"crystal-gauntlet migrate".colorize(:white)}" }
exit 1
end
["songs", "levels", "saves"].each() { |v|
Dir.mkdir_p(DATA_FOLDER / v)
}
server = HTTP::Server.new([
HTTP::LogHandler.new,
HTTP::StaticFileHandler.new("public/", fallthrough: true, directory_listing: false),
HTTP::StaticFileHandler.new((DATA_FOLDER / "songs").to_s, fallthrough: true, directory_listing: false),
CrystalGauntlet::GDHandler.new,
CrystalGauntlet::TemplateHandler.new
])
listen_on = URI.parse(ENV["LISTEN_ON"]? || "http://localhost:8080").normalize
case listen_on.scheme
when "http"
server.bind_tcp(listen_on.hostname.not_nil!, listen_on.port.not_nil!)
when "unix"
server.bind_unix(listen_on.to_s.sub("unix://",""))
end
check_server_length(false)
Reupload.init()
@@up_at = Time.utc
LOG.notice { "Listening on #{listen_on.to_s.colorize(:white)}" }
server.listen
end
end

96
src/patch-exe.cr Normal file
View File

@ -0,0 +1,96 @@
module CrystalGauntlet::PatchExe
extend self
SUPPORTED_PATCH_PLATFORMS = ["Windows"]
SUPPORTED_EXTENSIONS = ["exe"]
ROBTOP_SERVER_PATH = "http://www.boomlings.com/database"
def robtop_server_path
ROBTOP_SERVER_PATH
end
def full_server_path
"http://" + config_get("general.hostname", "") + "/" + config_get("general.append_path", "").chomp("/")
end
def replace(from : IO, to : IO, search : Array(UInt8), replace : Array(UInt8))
if search.size != replace.size
raise "Search and replacement does not match in size"
end
size = search.size
buffer = [] of UInt8
replacements = 0
from.each_byte do |byte|
if buffer.size >= size
insert_byte = buffer.shift
to.write_byte(insert_byte)
end
buffer << byte
if buffer == search
replace.each() { |b| to.write_byte(b) }
buffer = [] of UInt8
replacements += 1
end
end
buffer.each() { |b| to.write_byte(b) }
replacements
end
def patch_exe_file(location : String)
file_split = location.split(".")
extension = file_split.pop
patched_location = "#{file_split.join(".")}_patched.#{extension}"
File.open("#{location}", "r") do |from|
File.open(patched_location, "w") do |to|
start = Time.monotonic
amt = 0
case extension
when "exe"
gd_temp = File.tempfile("GeometryDash")
LOG.debug { " #{robtop_server_path.colorize(:dark_gray)} ->" }
LOG.debug { " #{full_server_path.colorize(:dark_gray)}" }
File.open(gd_temp.path, "w") do |tmp|
amt += replace(from, tmp, robtop_server_path.bytes, full_server_path.bytes)
end
LOG.debug { " #{Base64.strict_encode(robtop_server_path).colorize(:dark_gray)} ->" }
LOG.debug { " #{Base64.strict_encode(full_server_path).colorize(:dark_gray)}" }
File.open(gd_temp.path, "r") do |tmp|
amt += replace(tmp, to, Base64.strict_encode(robtop_server_path).bytes, Base64.strict_encode(full_server_path).bytes)
end
gd_temp.delete
else
LOG.error { "Unsupported extension #{extension.colorize(:white)} (supported: #{SUPPORTED_EXTENSIONS.join(", ")})" }
end
LOG.info { "Patched #{location} into #{patched_location} successfully" }
LOG.info { "#{amt} replacements done in #{(Time.monotonic - start).total_seconds.humanize(precision: 2, significant: false)}s" }
end
end
end
def check_server_length(exit_if_fail : Bool)
if full_server_path.size != robtop_server_path.size
LOG.warn { "i think you made a mistake? length of full server path and default .exe location do not match" }
LOG.warn { " #{full_server_path}" }
LOG.warn { " #{robtop_server_path}" }
min_length = Math.min(full_server_path.size, robtop_server_path.size)
max_length = Math.max(full_server_path.size, robtop_server_path.size)
LOG.warn { " #{" " * min_length}#{"^" * (max_length - min_length)}"}
if exit_if_fail
exit 1
end
end
end
end