#==============================================================================
# ** Game Progress Password System
#------------------------------------------------------------------------------
# * Scripted by: Yeyinde
# * Version 1.0.2
# * Date: 09/28/07
#------------------------------------------------------------------------------
# This is the core for a password saved game. It provides no method of input,
# so that is left up to you as the scripter. This system provides the base
# password tools that you can use to make your own old-school NESesque game.
#
# HOW TO USE:
# 1) Initialize a new instance of Password
# 2) Add switches, self_switches, variables, location, and time to the
# allocation
# 3) Compile the password
# 4) Read the password to restore game data
#------------------------------------------------------------------------------
#==============================================================================
# ** Password
#------------------------------------------------------------------------------
# Holds the data structure that is passwords
#==============================================================================
class Password
# Decryption hash. Change only the characters!
DECRYPT = {
'0' => 0 , '1' => 1 , '2' => 2 , '3' => 3 , '4' => 4 ,
'5' => 5 , '6' => 6 , '7' => 7 , '8' => 8 , '9' => 9 ,
'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14,
'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19,
'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24,
'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29,
'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34,
'Z' => 35, 'a' => 36, 'b' => 37, 'c' => 38, 'd' => 39,
'e' => 40, 'f' => 41, 'g' => 42, 'h' => 43, 'i' => 44,
'j' => 45, 'k' => 46, 'l' => 47, 'm' => 48, 'n' => 49,
'o' => 50, 'p' => 51, 'q' => 52, 'r' => 53, 's' => 54,
't' => 55, 'u' => 56, 'v' => 57, 'w' => 58, 'x' => 59,
'y' => 60, 'z' => 61, '?' => 62, '-' => 63
}
# The encrypt hash. It is the opposite of the decrypt hash.
ENCRYPT = DECRYPT.dup.invert
#--------------------------------------------------------------------------
# * Object initialization
# bytes : how many bytes the password will be
# shift_length : how many bytes to use for encryption
# checksum_length : how many bytes to use for verification
#--------------------------------------------------------------------------
def initialize(bytes, shift_length, checksum_length)
raise('Number of bits must be divisible by 6.') if bytes * 8 % 6 != 0
raise('Not enough bytes for shift and checksum') if shift_length +
checksum_length >= bytes
@total_bytes = bytes
@shift_length = shift_length
@checksum_length = checksum_length
@bytes = Array.new(bytes - shift_length - checksum_length)
@allocation = []
@index = 0
end
#--------------------------------------------------------------------------
# * Return password
#--------------------------------------------------------------------------
def password
return @password || '0' * (@total_bytes * 8 / 6)
end
#--------------------------------------------------------------------------
# * Add Switches
# switches : switch ids to save (maximum of 8)
#--------------------------------------------------------------------------
def add_switches(id1, *other)
@allocation << [0, id1, *other]
end
#--------------------------------------------------------------------------
# * Add Variable
# id : variable id
# bytes : how many bytes to use
# sign = 0 : positive or negative
#--------------------------------------------------------------------------
def add_variable(id, bytes, sign = 0)
@allocation << [1, id, bytes, [[sign, -1].max, 1].min]
end
#--------------------------------------------------------------------------
# * Add Self_Switches
# keys : self_switch keys to save (maximum of 8)
#--------------------------------------------------------------------------
def add_selfswitches(key1, *other)
@allocation << [2, key1, *other]
end
#--------------------------------------------------------------------------
# * Add Location
#--------------------------------------------------------------------------
def add_location
@allocation << [3]
end
#--------------------------------------------------------------------------
# * Add Time
# seconds = false : Use seconds instead of frames
#--------------------------------------------------------------------------
def add_time(seconds = false)
@allocation << [4, seconds]
end
#--------------------------------------------------------------------------
# * Compile Password
#--------------------------------------------------------------------------
def compile
size = allocated_bytes
raise("Allocated bytes do not match initial bytes.
#{size} allocated, #{@total_bytes} initialized.") if size != @total_bytes
@index = 0
@password = nil
@bytes.collect!{nil}
compile_bytes
shift = @shift_length > 0 ? rand(2**(@shift_length * 8 - 1) - 1).to_i : -1
checksum = calculate_checksum(@bytes, shift)
password_bytes = shift_bytes(@bytes, shift, 1)
shift = shift.to_s(2)
shift = '0' + shift while shift.size < @shift_length * 8
@shift_length.times do |i|
password_bytes << shift[(i*8)...(i*8+8)].to_i(2)
end
checksum = checksum.to_s(2)
checksum = '0' + checksum while checksum.size < @checksum_length * 8
@checksum_length.times do |i|
password_bytes << checksum[(i*8)...(i*8+8)].to_i(2)
end
password_string = bit_string(password_bytes, 8)
password = ''
password_string.unpack('a6' * (@total_bytes * 8 / 6)).each do |byte|
byte = byte.to_i(2)
password += ENCRYPT[byte]
end
@password = password
return true
end
#--------------------------------------------------------------------------
# * Read Password
# string : aString to read Must be same amount of characters as a
# generated password
#--------------------------------------------------------------------------
def read(string)
raise("Number of characters do not match.
#{string.size} given, #{@total_bytes * 8 / 6} needed.") if string.size !=
@total_bytes * 8 / 6
password = []
string.unpack('a' * string.size).each do |char|
password << DECRYPT[char]
end
password = bit_string(password, 6).unpack('a8' * @total_bytes)
checksum = ''
@checksum_length.times do
checksum = password.pop + checksum
end
checksum = @checksum_length > 0 ? checksum.to_i(2) : -1
shift = ''
@shift_length.times do
shift = password.pop + shift
end
shift = @shift_length > 0 ? shift.to_i(2) : -1
password.collect!{|byte| byte.to_i(2)}
password = shift_bytes(password, shift, -1)
raise('Invalid Password') if checksum != calculate_checksum(password, shift)
assemble_bytes(password)
return true
end
private
#--------------------------------------------------------------------------
# * Return number of allocated bytes
#--------------------------------------------------------------------------
def allocated_bytes
bytes = @shift_length + @checksum_length
@allocation.each do |data|
case data[0]
when 0
bytes += 1
when 1
bytes += data[2]
when 2
bytes += 1
when 3
bytes += 4
when 4
bytes += 4
end
end
return bytes
end
#--------------------------------------------------------------------------
# * Compile Bytes
#--------------------------------------------------------------------------
def compile_bytes
@allocation.each do |data|
case data[0]
when 0
comp_switches(*data[1...data.size])
when 1
comp_variable(*data[1...data.size])
when 2
comp_selfswitches(*data[1...data.size])
when 3
comp_location
when 4
comp_time(data[1])
end
end
end
#--------------------------------------------------------------------------
# * Assemble Bytes
# bytes : password byte data
#--------------------------------------------------------------------------
def assemble_bytes(bytes)
@allocation.each do |data|
case data[0]
when 0
ass_switches(data[1...data.size], bytes.shift)
when 1
pass_data = []
data[2].times {pass_data << bytes.shift}
ass_variable(data[1...data.size], pass_data)
when 2
ass_selfswitches(data[1...data.size], bytes.shift)
when 3
ass_location(data[1...data.size], [bytes.shift, bytes.shift, bytes.shift,
bytes.shift])
when 4
ass_time(data[1...data.size], [bytes.shift, bytes.shift, bytes.shift,
bytes.shift])
end
end
end
#--------------------------------------------------------------------------
# * Checksum Calculation
# shift : the shift character(s)
#--------------------------------------------------------------------------
def calculate_checksum(bytes, shift)
string = ''
bytes.each {|byte|string += byte.chr}
if shift >= 0
shift_string = shift.to_s(2)
shift_string = '0' + shift_string while shift_string.size < @shift_length * 8
shift_string.unpack('a8' * @shift_length).each do |byte|
string += byte.to_i(2).chr
end
end
order = @checksum_length * 8
poly = 2**order - 1
checksum = CRC.new(order, poly, 0, 0, false)
checksum.update(string)
sum = checksum.final
return @checksum_length > 0 ? sum : -1
end
#--------------------------------------------------------------------------
# * Byte Shift
# bytes : password byte data
# shift : how many times to shift
# dir : direction - negative is left, positive is right
#--------------------------------------------------------------------------
def shift_bytes(bytes, shift, dir)
password_bits = bit_string(bytes, 8)
password_bits = password_bits.unpack('a' * password_bits.size)
shift.times do
if dir == 1
password_bits.unshift password_bits.pop
else
password_bits.push password_bits.shift
end
end
password_bits = password_bits.to_s.unpack('a8' * bytes.size)
password_bits.collect!{|bit| bit.to_i(2)}
return password_bits
end
#--------------------------------------------------------------------------
# * Bit String
# bytes : password byte data
# bits : number of bits per number
#--------------------------------------------------------------------------
def bit_string(bytes, bits)
string = ''
bytes.each do |byte|
byte = byte.to_s(2)
byte = '0' + byte while byte.size < bits
string += byte
end
return string
end
#--------------------------------------------------------------------------
# * Switch compiling
#--------------------------------------------------------------------------
def comp_switches(id1, id2=0, id3=0, id4=0, id5=0, id6=0, id7=0, id8=0)
byte = 0
byte += 1<<7 if $game_switches[id1]
byte += 1<<6 if $game_switches[id2]
byte += 1<<5 if $game_switches[id3]
byte += 1<<4 if $game_switches[id4]
byte += 1<<3 if $game_switches[id5]
byte += 1<<2 if $game_switches[id6]
byte += 1<<1 if $game_switches[id7]
byte += 1<<0 if $game_switches[id8]
@bytes[@index] = byte
@index += 1
end
#--------------------------------------------------------------------------
# * Variable compiling
#--------------------------------------------------------------------------
def comp_variable(id, bytes, sign)
max = sign == 0 ? (2**(bytes*(8-1))-1) : sign == 1 ? 2**(bytes*8) : 0
min = sign == 0 ? -(2**(bytes*(8-1))-1) : sign == -1 ? -2**(bytes*8) : 0
max_bits = bytes * 8
max_bits -= 1 if sign == 0
var = [[$game_variables[id], min].max, max].min
var = var.to_s(2)
var = '0' + var while var.size < max_bits
var = ($game_variables[id] < 0 ? '1' : '0') + var if sign == 0
bytes.times do |i|
@bytes[@index] = var[(i*8)...(i*8+8)].to_i(2)
@index += 1
end
end
#--------------------------------------------------------------------------
# * Self_Switch compiling
#--------------------------------------------------------------------------
def comp_selfswitches(key1, key2=[0,0,'A'], key3=[0,0,'A'], key4=[0,0,'A'],
key5=[0,0,'A'], key6=[0,0,'A'], key7=[0,0,'A'], key8=[0,0,'A'])
byte = 0
byte += 1<<7 if $game_self_switches[key1]
byte += 1<<6 if $game_self_switches[key2]
byte += 1<<5 if $game_self_switches[key3]
byte += 1<<4 if $game_self_switches[key4]
byte += 1<<3 if $game_self_switches[key5]
byte += 1<<2 if $game_self_switches[key6]
byte += 1<<1 if $game_self_switches[key7]
byte += 1<<0 if $game_self_switches[key8]
@bytes[@index] = byte
@index += 1
end
#--------------------------------------------------------------------------
# * Location compiling
#--------------------------------------------------------------------------
def comp_location
map = $game_map.map_id.to_s(2)
x = $game_player.x.to_s(2)
y = $game_player.y.to_s(2)
dir = ($game_player.direction / 2).to_s(2)
map = '0' + map while map.size < 10
x = '0' + x while x.size < 9
y = '0' + y while y.size < 9
dir = '0' + dir while dir.size < 4
bits = map + x + y + dir
4.times do |i|
@bytes[@index] = bits[(i*8)...(i*8+8)].to_i(2)
@index += 1
end
end
#--------------------------------------------------------------------------
# * Time compiling
#--------------------------------------------------------------------------
def comp_time(seconds)
ticks = Graphics.frame_count
ticks %= Graphics.frame_rate if seconds
ticks = [ticks, 2**32-1].min
ticks = ticks.to_s(2)
ticks = '0' + ticks while ticks.size < 32
ticks.reverse!
4.times do |i|
@bytes[@index] = ticks[(i*8)...(i*8+8)].to_i(2)
@index += 1
end
end
#--------------------------------------------------------------------------
# * Switch assembly
#--------------------------------------------------------------------------
def ass_switches(allocation, data)
data = bit_string([data], 8)
allocation.each_with_index do |switch_id, i|
$game_switches[switch_id] = data[i, 1] == '1'
end
$game_map.refresh
end
#--------------------------------------------------------------------------
# * Variable assembly
#--------------------------------------------------------------------------
def ass_variable(allocation, data)
data = bit_string(data, 8)
mult = 1
if allocation[2] == 0
mult = data[0, 1] == '1' ? -1 : 1
data = data[1...data.size]
elsif allocation[2] == -1
mult = -1
end
$game_variables[allocation[0]] = mult * data.to_i(2)
$game_map.refresh
end
#--------------------------------------------------------------------------
# * Self_Switch assembly
#--------------------------------------------------------------------------
def ass_selfswitches(allocation, data)
data = bit_string([data], 8)
allocation.each_with_index do |key, i|
$game_self_switches[key] = data[i, 1] == '1'
end
$game_map.refresh
end
#--------------------------------------------------------------------------
# * Location assembly
#--------------------------------------------------------------------------
def ass_location(allocation, data)
bits = bit_string(data, 8)
map_id = bits[0, 10].to_i(2)
x = bits[10, 9].to_i(2)
y = bits[19, 9].to_i(2)
dir = bits[28, 4].to_i(2) * 2
if map_id == 0
map_id = $data_system.start_map_id
x, y = $data_system.start_x, $data_system.start_y
dir = 2
end
Graphics.freeze
$game_map.setup(map_id)
$game_player.moveto(x, y)
$game_player.direction = dir
$game_map.autoplay
$scene = Scene_Map.new
end
#--------------------------------------------------------------------------
# * Time assembly
#--------------------------------------------------------------------------
def ass_time(allocation, data)
data = bit_string(data, 8).reverse.to_i(2)
data * Graphics.frame_rate if allocation[0]
Graphics.frame_count = data
end
end
class Game_Character
attr_writer :direction
end
#=============================================================================
# ** Cyclic Redundancy Check
#=============================================================================
class CRC
def initialize(order, poly, iv, xor, reflect)
@order = order
@poly = poly
@iv = iv
@xor = xor
@reflect = reflect
if @reflect
@mask = (2 ** (@order - 8)) - 1
end
@lookup = build_lookup
@crc = init
end
def init
crc = @iv
if @reflect
crc = reflect(crc, @order)
end
crc
end
def update(str)
topbit = 1 << (@order - 1)
widthmask = (topbit << 1) - 1
str.each_byte do |byte|
if @reflect
@crc = ((@crc >> 8) & @mask) ^ @lookup[(@crc ^ byte) & 0xFF]
else
@crc = (@crc << 8) ^ @lookup[((@crc >> (@order - 8)) ^ byte) & 0xFF]
end
@crc &= widthmask
end
end
def final
@crc ^= @xor
bytes = (@order + 7) >> 3
@crc
end
private
def build_lookup
lookup = []
topbit = 1 << (@order - 1)
widthmask = (topbit << 1) - 1
for idx in 0..255
v = idx
v = reflect(v, 8) if @reflect
v <<= (@order - 8)
8.times do
if (v & topbit).nonzero?
v = (v << 1) ^ @poly
else
v <<= 1
end
end
v = reflect(v, @order) if @reflect
v &= widthmask
lookup << v
end
lookup
end
def reflect(crc, bits)
base = crc
for idx in 0...bits
bitmask = 1 << ((bits - 1) - idx)
if (base & 1).nonzero?
crc |= bitmask;
else
crc &= ~bitmask;
end
base >>= 1
end
crc
end
end