mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2025-12-15 16:52:34 +00:00
Added digest-crc gem, added Mixer class to read and write Renegade MIX1 files
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -4,4 +4,5 @@ gem "cyberarm_engine"
|
||||
gem "launchy"
|
||||
gem "i18n"
|
||||
gem "rexml"
|
||||
gem "digest-crc"
|
||||
# gem "async-websocket"
|
||||
|
||||
170
lib/mixer.rb
Normal file
170
lib/mixer.rb
Normal file
@@ -0,0 +1,170 @@
|
||||
require "digest"
|
||||
|
||||
class W3DHub
|
||||
|
||||
# https://github.com/TheUnstoppable/MixLibrary used for reference
|
||||
class Mixer
|
||||
class MixParserException < RuntimeError; end
|
||||
class MixFormatException < RuntimeError; end
|
||||
|
||||
class Reader
|
||||
attr_reader :package
|
||||
|
||||
def initialize(file_path:, ignore_crc_mismatches: false)
|
||||
@package = Package.new
|
||||
@file = File.open(file_path)
|
||||
|
||||
@file.pos = 0
|
||||
|
||||
# Valid header
|
||||
if read_i32 == 0x3158494D
|
||||
file_data_offset = read_i32
|
||||
file_names_offset = read_i32
|
||||
|
||||
@file.pos = file_names_offset
|
||||
file_count = read_i32
|
||||
|
||||
file_count.times do
|
||||
@package.files << Package::File.new(name: read_string)
|
||||
end
|
||||
|
||||
@file.pos = file_data_offset
|
||||
_file_count = read_i32
|
||||
|
||||
file_count.times do |i|
|
||||
file = @package.files[i]
|
||||
|
||||
file.mix_crc = read_u32.to_s(16)
|
||||
file.content_offset = read_u32
|
||||
file.content_length = read_u32
|
||||
|
||||
if file.mix_crc != file.file_crc && !ignore_crc_mismatches
|
||||
raise MixParserException, "CRC mismatch for #{file.name}. #{file.mix_crc.inspect} != #{file.file_crc.inspect}"
|
||||
end
|
||||
|
||||
pos = @file.pos
|
||||
@file.pos = file.content_offset
|
||||
file.data = @file.read(file.content_length)
|
||||
@file.pos = pos
|
||||
end
|
||||
else
|
||||
raise MixParserException, "Invalid MIX file"
|
||||
end
|
||||
|
||||
ensure
|
||||
@file&.close
|
||||
end
|
||||
|
||||
def read_i32
|
||||
@file.read(4).unpack1("l")
|
||||
end
|
||||
|
||||
def read_u32
|
||||
@file.read(4).unpack1("L")
|
||||
end
|
||||
|
||||
def read_string
|
||||
buffer = ""
|
||||
|
||||
length = @file.readbyte
|
||||
|
||||
length.times do
|
||||
buffer << @file.readbyte
|
||||
end
|
||||
|
||||
buffer.strip
|
||||
end
|
||||
end
|
||||
|
||||
class Writer
|
||||
attr_reader :package
|
||||
|
||||
def initialize(file_path:, package:)
|
||||
@package = package
|
||||
|
||||
@file = File.open(file_path, "wb")
|
||||
@file.pos = 0
|
||||
|
||||
@file.write("MIX1")
|
||||
|
||||
files = @package.files.sort { |a, b| a.file_crc <=> b.file_crc }
|
||||
|
||||
@file.pos = 16
|
||||
|
||||
files.each do |file|
|
||||
file.content_offset = @file.pos
|
||||
file.content_length = file.data.length
|
||||
@file.write(file.data)
|
||||
|
||||
@file.pos += -@file.pos & 7
|
||||
end
|
||||
|
||||
file_data_offset = @file.pos
|
||||
write_i32(files.count)
|
||||
|
||||
files.each do |file|
|
||||
write_u32(file.file_crc.to_i(16))
|
||||
write_u32(file.content_offset)
|
||||
write_u32(file.content_length)
|
||||
end
|
||||
|
||||
file_name_offset = @file.pos
|
||||
write_i32(files.count)
|
||||
|
||||
files.each do |file|
|
||||
write_byte(file.name.length + 1)
|
||||
@file.write("#{file.name}\0")
|
||||
end
|
||||
|
||||
@file.pos = 4
|
||||
write_i32(file_data_offset)
|
||||
write_i32(file_name_offset)
|
||||
|
||||
@file.pos = 0
|
||||
@file.flush
|
||||
ensure
|
||||
@file&.close
|
||||
end
|
||||
|
||||
def write_i32(int)
|
||||
@file.write([int].pack("l"))
|
||||
end
|
||||
|
||||
def write_u32(uint)
|
||||
@file.write([uint].pack("L"))
|
||||
end
|
||||
|
||||
def write_byte(byte)
|
||||
@file.write([byte].pack("c"))
|
||||
end
|
||||
end
|
||||
|
||||
class Package
|
||||
attr_reader :files
|
||||
|
||||
def initialize(files: [])
|
||||
@files = files
|
||||
end
|
||||
|
||||
class File
|
||||
attr_accessor :name, :mix_crc, :content_offset, :content_length, :data
|
||||
|
||||
def initialize(name:, mix_crc: nil, content_offset: nil, content_length: nil, data: nil)
|
||||
@name = name
|
||||
@mix_crc = mix_crc
|
||||
@content_offset = content_offset
|
||||
@content_length = content_length
|
||||
@data = data
|
||||
end
|
||||
|
||||
def file_crc
|
||||
Digest::CRC32.hexdigest(@name.upcase)
|
||||
end
|
||||
|
||||
def data_crc
|
||||
Digest::CRC32.hexdigest(@data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -32,6 +32,7 @@ require_relative "lib/store"
|
||||
require_relative "lib/window"
|
||||
require_relative "lib/cache"
|
||||
require_relative "lib/settings"
|
||||
require_relative "lib/mixer"
|
||||
require_relative "lib/application_manager"
|
||||
require_relative "lib/application_manager/manifest"
|
||||
require_relative "lib/application_manager/task"
|
||||
|
||||
Reference in New Issue
Block a user