Refactored to use TscFile class and read from dropped directory.

This commit is contained in:
shru 2018-12-14 14:51:03 -05:00
parent 0094c34e51
commit ea1d0720fc
5 changed files with 515 additions and 159 deletions

276
docs/item locations.txt Normal file
View file

@ -0,0 +1,276 @@
---------------------------
- 3-02 Upgrade Locations -
---------------------------
** LIFE CAPSULES **
These will give a set Max HP boost, between 3 and 5, depending on the capsule.
You will have 50 life if you find them all, 55 if you include the bonus
capsule. Some of these are possible to miss.
- First Cave: In plain sight as you descend toward the Polar Star, on the left
wall. +3 HP.
- Mimiga Village: Found in Yamanshita Farm on the top of Mimiga Village. Head
to the far right, it's in the small pool. +3 HP.
- Egg Corridor: At the very beginning, you have to drop down to the left just
as the Basil spark hits the wall then chase it so it extends its loop length.
+3 HP.
- Egg Corridor: Go through Cthulhu's Abode and out the top door. Jump left.
+4 HP.
- Bushlands: Just past where you found Santa's Key, go east to a set of two
horizontal rows of star blocks, jump on them and to the left. +5 HP.
- Bushlands: In the Execution Chamber, the tall building with the skull on it
to the right of Kazuma's shack. +5 HP.
- Sand Zone: East of Curly's House and past the Sun Stones, it's in the top of
the first thick pillar made up of star blocks. Try not to blow up all the star
blocks when fighting the Polishes to create a path and reach it. +5 HP.
- Sand Zone: At the end of the hidden path behind a pawpad block on the far
right wall between the Sun Stones and Jenka's house, behind a line of star
blocks. Found next to a chest containing one of Jenka's dogs. +5 HP.
- Labyrinth: Nestled next to the left wall of the first room of the labyrinth
when you're sent there, a ways up into the room. +5 HP.
- Plantation: Sitting on a platform hanging from the far upper-left ceiling.
+4 HP.
- Plantation: Talk to the puppy that appears on the left platform just under
the red skull signs on the top right section of the Plantation after Momorin's
finished the rocket. +5 HP.
- Sanctuary: Bonus life capsule. Found in plain sight as you make your
initial descent. +5 HP.
** MISSILE EXPANSIONS **
These each give +5 Max Missile Launcher Ammo. Your max ammo carries over to
the Super Missile Launcher. You'll have 30 missile ammo if you find them all,
54(!!) with the bonus.
- Bushlands: To the left of the hut in the air above the shack where Kazuma's
trapped. Jump around the series of platforms around where the Save Point is
between the Power Supply Room and Kazuma's shack to reach it.
- Bushlands: Inside the very hut that the previous expansion's sitting next
to. Opening it will trigger a fight with Kulala.
- Egg Corridor?: When you go back to the Egg Corridor later in the game,
you'll find the eggs have hatched. The dragon where Egg 12 used to be is quite
pissed and will fervently guard the chest containing this expansion.
- Egg Corridor?: In the ruins of the Egg observation Room, in plain sight.
Opening it will trigger a fight with The Sisters.
- Sanctuary: Bonus expansion. Just before you fight the Heavy Press at the
far west end of Blood-Stained Sanctuary B3, just past the pillar with 3 Deletes
covering it, in the room filled with flying Butes and with a hanging platform
with an arrow Bute on either side. Above this platform is a single Star Block
concealing a chest containing this massive expansion of 24 Misisles.
** KEY ITEMS **
- Map System
Allows you to press the - Button on the Wii Remote to bring up a map of the
room you're in. The map is tiny, doesn't show your current position and has a
strange graphical glitch that makes the rightmost section of mose large areas
unviewable, so it's of very little use except collectibility.
- Silver Locket
An item found in the fishing hole to the west of Mimiga Village. It triggers
an event in the warehouse at the bottom right of the village.
- Arthur's Key
Found at Arthur's grave in the Cemetary. Use it to access Arthur's house, at
the bottom-left of Mimiga Village.
- ID Card
Found in Egg #06 in the Egg Corridor. Use it in Egg #01 to open the barrier to
the eastern edge of the Corridor.
- Santa's Key
The key to Santa's house that he dropped to the east in the Bushlands.
- Rusty Key
A key Kazuma found in the room he's stuck in. Gives access to the Power
Control Room in the Bushlands.
- Gum Key
Another key conviniently located in Kazuma's captivity room. It opens up the
gum room in the very upper-right corner of the Bushlands.
- Jellyfish Juice
A jar full of juice that you can find by defeating the huge jellyfish stuck in
the ceiling a bit east of Santa's house that only appears after talking to
Chako. Use it to put out fireplace fires; it's also one of three components
needed to create a bomb. It's the only Key Item you can receive more than
once, but you may only carry one at a time.
- Chako's Rouge
Some red lipstick. Obtained by examining Chako's fireplace before you obtain
Jellyfish Jelly, causing her to walk up to you and explain some things, and
then sleeping in her bed while she's standing there. It has no use, other than
proof that you really, really, REALLY like Mimigas, you scoundrel.
- Charcoal
A lump of charcoal found by putting out the fireplace in Santa's house. It's
one of three components needed to create a bomb.
- Gum Base
A stick of chewing gum found in the room in the very top-right corner of the
Bushlands. It's one of three components needed to create a bomb.
- Bomb
A bomb crafted by Malco, in the Power Generator Room of the Bushlands. Use it
to blow open the door of the building Kazuma's trapped inside.
- Curly's Panties
Found in a hidden alcove behind the wall in the bedroom of Curly Brace's house
in the Sand Zone. They have no use, other than proof that you really, really,
REALLY like robots. If you own both this and Chako's Rouge, you are officially
the most envied man in Cave Story! Okay, I made that up.
- Hajime
A dog that stays in Curly Brace's bedroom in the Sand Zone. One of Jenka's
five missing dogs. He's the leader of the bunch.. or so he claims.
Arf!
- Mick
One of Jenka's dogs. He's supposedly an adept treasure hunter, and loves to
sleep in treasure chests. What he does with the contents is anyone's guess.
Found in a chest at the end of a hidden path between the Sun Stones and Jenka's
house.
Woof woof!
- Shinobu
Another of Jenka's dogs. He has poor vision so he prefers to stay in dark
places. Found in an abandoned house at the end of a hidden road that begins in
a false ceiling along the path to the Sand Zone warehouse.
Bow wow wow!
- Kakeru
Yet another dog of Jenka's. He loves bones and has buried them all over the
place. You can find him at the end of the sand-bottomed section of the path
toward the Sand Zone warehouse.
Woof woof woof woof!
- Nene
A beautiful dog, the only female of the group. She sleeps all the time, and
can be found taking one of her long naps at the very end of the lower path of
the Sand Zone, right next to the warehouse.
Wan...
- Life Pot
Restores all of your HP when used. It can only be used once, and there are
only two in the entire game, so use wisely!
- Turbocharge
If you chose to take the Machine Gun from Curly in the Sand Zone, then you can
receive this for free from the Gaudi shopkeeper, Chaba, in the Labyrinth. It
speeds up the recovery rate of ammo for the Machine Gun.
- Clinic Key
Opens the door to the Labyrinth Clinic, the building with the cross above it
up and west from the Camp. Obtained from Dr. Gero, the physician in the Camp.
- Arms Barrier
Halves weapon EXP lost when you take damage. Found the top part of the Camp,
accessible via a hidden passageway in the ceiling in the large Labyrinth room
from which you can access the normal Camp entrance and the Clinic nearby.
Unless you took the Machine Gun, you'll have to come back to this area with the
Booster to be able to reach it.
- Cure-All
A pill found in the abandoned Clinic in the Labyrinth. It supposedly has the
ability to cure anything. Give it to the Physician in the Labyrinth Camp.
- Booster v0.8
A jetpack that can be equipped and unequipped from the inventory menu. Once
it's equipped, press and hold Jump while airborne to launch upward for a few
moments. Can only be used once in the air until you hit the ground again.
Received in the Labyrinth, past the Clinic and Camp.
- Booster v2.0
A powerful jetpack that allows you to move freely for a time at high speeds in
midair if you press the jump button. If you want this instead of the Booster
v0.8, jump over the large gap in the Labyrinth room where Professor Booster
falls down by leaping just at the red marker on the ground. It's required if
you wish to access the hidden, super-difficult Sanctuary area at the end of the
game, but you may want to go for the normal ending with Booster v0.8 first.
- Tow Rope
A tow cable found in the far lower-right corner of The Core (only appears if
you didn't take the Booster v0.8). It was used by the robots who came to the
island years ago to carry downed comrades.
- Curly's Air Tank
Given to you by Curly Brace after the events at the Core. With it, you're
automatically encased in a bubble whenever you enter water, giving you infinite
air.
- Alien Medal
Reward for defeating Ironhead (the boss of the Waterway) without taking any
damage. It's engraved with an image of Ikachan, the hero of a game named after
himself that was Studio Pixel's previous work before Cave STory.
- Whimsical Star
A trinket that you can receive from Chaba, the Gaudi shopkeep in the Labyrinth,
if you talk to him with the Spur weapon in your posession. It will cause small
stars to float around you as a meagre shield when you charge the Spur to MAX.
- Teleporter Room Key
This key is visible but unobtainable behind a cluster of instant-death spikes
in the pools at the bottom of the Plantation. You'll need to talk to Kanpachi,
the fishing Mimiga, to help you get it. Use it to unlock the room just west of
where it was fished up.
- Sue's Letter
You'll find this stuffed in your inventory after you wake up inside the jail.
It's a leeter from Sue explaining a lot of what's been going on as far as the
story that you've had to just piece together by this point. It also contains
the password to enter the Safe House on the Plantation.
- Mimiga Mask
A head covering that looks just like a Mimiga. Wearing it will allow you to
speak to the Mimiga in the Plantation who aren't allowed to speak with humans.
- Broken Sprinkler
A broken water sprinkler from the Plantation. Useless by itself, but can be
traded to Megane in the Plantation Rest Area.
- Sprinkler
It's new... isn't it? This can be taken back to Momorin in the Plantation Safe
House to help complete her rocket.
- Controller
A technologically impressive device that could only have been made by a master
technician. Too bad he's a big fat coward. Obtained from Itoh after giving
Momorin the Sprinkler.
- Mushroom Badge
Useless item given to you by Ma Pignon in hopes you'll leave him alone. I hope
you know how to answer his questions correctly even if you want this garbage.
- Ma Pignon
Mystical, ultra-rare mushroom that's said to restore people's memories. Has a
smart-aleck personality and is known to be a surprisingly potent combatant, as
well. Found in the small storage room above the Mimiga Cemetar.
- Mister Little
This guy's very, very hard to see and has gotten himself very, very lost. At
the end of the game, you can find him wandering around the Mimiga Cemetary.
It's said he is the proud owner of the world's most splendid gun. Has a
fascination with fine blades.
- Medal of the Red Ogre (aka Clay Figure Medal)
Proof of the warrior who's finally able to put an end to the Red Ogre. It's
extremely heavy despite its size.

71
src/lib/classic.lua Normal file
View file

@ -0,0 +1,71 @@
-- Copyright (c) 2014, rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
local Object = {}
Object.__index = Object
function Object:new()
end
function Object:extend()
local cls = {}
for k, v in pairs(self) do
if k:find("__") == 1 then
cls[k] = v
end
end
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
function Object:implement(...)
for _, cls in pairs({...}) do
for k, v in pairs(cls) do
if self[k] == nil and type(v) == "function" then
self[k] = v
end
end
end
end
function Object:is(T)
local mt = getmetatable(self)
while mt do
if mt == T then
return true
end
mt = getmetatable(mt)
end
return false
end
function Object:__tostring()
return "Object"
end
function Object:__call(...)
local obj = setmetatable({}, self)
obj:new(...)
return obj
end
return Object

View file

@ -2,60 +2,23 @@ io.stdout:setvbuf("no")
require 'lib.strict'
local _ = require 'lib.moses'
local Serpent = require 'lib.serpent'
Class = require 'lib.classic'
_ = require 'lib.moses'
Serpent = require 'lib.serpent'
local lf = love.filesystem
lf = love.filesystem
local MODE_READ_BINARY = 'rb'
local MODE_WRITE_BINARY = 'wb'
function readLittleEndian(file, bytes)
local string = file:read(2)
local bytes = string:byte()
-- print(string, type(bytes), bytes)
return bytes
end
-- https://gist.github.com/fdeitylink/3fded36e9187fe838eb18a412c712800
-- -=~ PXM File Data ~=-
-- maps must be minimum of 21x16
function readPXM(filename)
print('reading PXM: ' .. filename)
local file = assert(io.open(filename, MODE_READ_BINARY))
-- First three bytes are PXM, then 0x10.
assert(file:read(3) == "PXM")
assert(file:read(1):byte() == 0x10)
-- Then 0x_map_length - 2 bytes
-- Then 0x_map_height - 2 bytes
local length = readLittleEndian(file, 2)
local height = readLittleEndian(file, 2)
print('length:', length)
print('height:', height)
-- Then 0x_map_tile_from_tileset for the rest of the file (numbered from 0, and going left to right, top to bottom) - 1 byte
local tiles = {}
for x = 1, length do
tiles[x] = {}
end
for y = 1, height do
for x = 1, length do
tiles[x][y] = file:read(1):byte()
end
end
-- Print result
local XXX = 3
for y = 1, height do
local line = ""
for x = 1, length do
local tile = tiles[x][y]
local len = string.len(tile)
line = line .. string.rep(' ', XXX - len) .. tile
end
print(line)
local LOG_LEVEL = 5
local function _log(level, prefix, text, ...)
if LOG_LEVEL >= level then
print(prefix .. text, ...)
end
end
function logError(...) _log(1, 'ERROR: ', ...) end
function logWarning(...) _log(2, 'WARNING: ', ...) end
function logNotice(...) _log(3, 'NOTICE: ', ...) end
function logInfo(...) _log(4, 'INFO: ', ...) end
function logDebug(...) _log(5, 'DEBUG: ', ...) end
local ITEM_DATA = {
-- Weapons
@ -77,123 +40,40 @@ local ITEM_DATA = {
},
}
function stringReplace(text, needle, replacement)
local i = text:find(needle, 1, true)
if i == nil then
return text
end
local len = needle:len()
local j = i + len - 1
assert((i % 1 == 0) and (i > 0) and (i <= j), tostring(i))
assert((j % 1 == 0), tostring(j))
local a = text:sub(1, i - 1)
local b = text:sub(j + 1)
return a .. replacement .. b
end
local TSC_FILES = {
'Pole.tsc',
}
function readTSC(filename, decodedFilename)
print('reading TSC: ' .. filename)
local file = assert(io.open(filename, MODE_READ_BINARY))
local filesize = file:seek("end")
local encodingCharPosition = math.floor(filesize / 2)
file:seek("set", encodingCharPosition)
local encodingChar = file:read(1):byte()
print(" filesize", filesize)
print(" encoding char:", encodingChar)
print(" encoding char position:", encodingCharPosition)
-- Decode
local chars, len = {}, 0
file:seek("set")
for pos = 0, filesize - 1 do
local byte = file:read(1):byte()
if pos ~= encodingCharPosition then
-- print(pos, encodingCharPosition)
byte = (byte - encodingChar) % 256
-- byte = byte - encodingChar
end
len = len + 1
chars[len] = string.char(byte)
end
local decoded = table.concat(chars)
local t = {}
decoded:gsub(".",function(c) table.insert(t,c) end)
-- for i, c in ipairs(t) do
local near = 10
for i = 1270 - near, 1270 + near do
local c = t[i]
print (i, c, chars[i])
-- print(string.byte(chars[i]) == string.byte(c), string.byte(chars[i]), string.byte(c))
end
-- -- Replace
-- decoded = stringReplace(decoded, ITEM_DATA.wPolar.command, ITEM_DATA.iPanties.command)
-- decoded = stringReplace(decoded, ITEM_DATA.wPolar.getText, ITEM_DATA.iPanties.getText)
-- decoded = stringReplace(decoded, ITEM_DATA.wPolar.displayCmd, ITEM_DATA.iPanties.displayCmd)
-- Write
-- return decoded
local tmpFile, err = io.open(decodedFilename, MODE_WRITE_BINARY)
assert(err == nil, err)
tmpFile:write(decoded)
tmpFile:flush()
tmpFile:close()
end
function writeTSC(filename)
print('writing TSC: ' .. filename)
local file = assert(io.open(filename, MODE_READ_BINARY))
local filesize = file:seek("end")
local encodingCharPosition = math.floor(filesize / 2)
file:seek("set", encodingCharPosition)
local encodingChar = file:read(1):byte()
file:seek("set", encodingCharPosition)
print(file:read(1))
-- local encodingChar = 32
print(" filesize", filesize)
print(" encoding char:", encodingChar)
print(" encoding char position:", encodingCharPosition)
-- Encode
local chars, len = {}, 0
file:seek("set")
for pos = 0, filesize - 1 do
local byte = file:read(1):byte()
if pos ~= encodingCharPosition then
byte = (byte + encodingChar) % 255
end
len = len + 1
chars[len] = string.char(byte)
end
local decoded = table.concat(chars)
-- Write
local tmpFile, err = io.open('TestingEncoded.tsc', MODE_WRITE_BINARY)
assert(err == nil, err)
tmpFile:write(decoded)
tmpFile:flush()
tmpFile:close()
end
function love.load()
-- readPXM('Pole.pxm')
-- readTSC('Pole.tsc', 'Testing.tsc')
-- writeTSC('Testing.tsc')
-- readTSC('TestingEncoded.tsc', 'TestingDecoded.tsc')
end
-- function love.load()
-- -- readPXM('Pole.pxm')
-- -- readTSC('Pole.tsc', 'Testing.tsc')
-- -- writeTSC('Testing.tsc')
-- -- readTSC('TestingEncoded.tsc', 'TestingDecoded.tsc')
-- end
function love.directorydropped(path)
local success = lf.mount(path, 'data')
assert(success)
-- Mount
assert(lf.mount(path, 'data'))
local items = lf.getDirectoryItems('/data')
local containsStage = _.contains(items, 'Stage')
assert(containsStage)
local dirStage = '/data/Stage'
-- local items = lf.getDirectoryItems(dirStage)
-- print(Serpent.block(items))
local tscFiles = {}
for _, filename in ipairs(TSC_FILES) do
local path = dirStage .. '/' .. filename
local TscFile = require 'tsc_file'
tscFiles[filename] = TscFile(path)
-- decoded = stringReplace(decoded, ITEM_DATA.wPolar.command, ITEM_DATA.iPanties.command)
-- decoded = stringReplace(decoded, ITEM_DATA.wPolar.getText, ITEM_DATA.iPanties.getText)
-- decoded = stringReplace(decoded, ITEM_DATA.wPolar.displayCmd, ITEM_DATA.iPanties.displayCmd)
end
tscFiles['Pole.tsc']:writeTo('Testing.tsc')
-- Unmount
assert(lf.unmount(path))
end
function love.keypressed(key)

46
src/pxm_file.lua Normal file
View file

@ -0,0 +1,46 @@
local function readLittleEndian(file, bytes)
local string = file:read(2)
local bytes = string:byte()
-- print(string, type(bytes), bytes)
return bytes
end
-- https://gist.github.com/fdeitylink/3fded36e9187fe838eb18a412c712800
-- -=~ PXM File Data ~=-
-- maps must be minimum of 21x16
local function readPXM(filename)
print('reading PXM: ' .. filename)
local file = assert(io.open(filename, MODE_READ_BINARY))
-- First three bytes are PXM, then 0x10.
assert(file:read(3) == "PXM")
assert(file:read(1):byte() == 0x10)
-- Then 0x_map_length - 2 bytes
-- Then 0x_map_height - 2 bytes
local length = readLittleEndian(file, 2)
local height = readLittleEndian(file, 2)
print('length:', length)
print('height:', height)
-- Then 0x_map_tile_from_tileset for the rest of the file (numbered from 0, and going left to right, top to bottom) - 1 byte
local tiles = {}
for x = 1, length do
tiles[x] = {}
end
for y = 1, height do
for x = 1, length do
tiles[x][y] = file:read(1):byte()
end
end
-- Print result
local XXX = 3
for y = 1, height do
local line = ""
for x = 1, length do
local tile = tiles[x][y]
local len = string.len(tile)
line = line .. string.rep(' ', XXX - len) .. tile
end
print(line)
end
end

83
src/tsc_file.lua Normal file
View file

@ -0,0 +1,83 @@
local C = Class:extend()
-- local MODE_READ_BINARY = 'rb'
local MODE_WRITE_BINARY = 'wb'
function C:new(path)
logInfo('reading TSC: ' .. path)
local file = lf.newFile(path)
assert(file:open('r'))
local contents, size = file:read()
self._text = self:_codec(contents, 'decode')
assert(file:close())
assert(file:release())
end
function C:writeTo(path)
local encoded = self:_codec(self._text, 'encode')
local tmpFile, err = io.open(path, MODE_WRITE_BINARY)
assert(err == nil, err)
tmpFile:write(encoded)
tmpFile:flush()
tmpFile:close()
end
function C:_codec(text, mode)
-- Create array of chars.
local chars = {}
text:gsub(".", function(c) table.insert(chars, c) end)
-- Determine encoding char value
local encodingCharPosition = math.floor(#chars / 2) + 1
local encodingChar = chars[encodingCharPosition]:byte()
if mode == 'decode' then
encodingChar = encodingChar * -1
elseif mode == 'encode' then
-- OK!
else
error('Unknown codec mode: ' .. tostring(mode))
end
logDebug(" filesize", #chars)
logDebug(" encoding char:", encodingChar)
logDebug(" encoding char position:", encodingCharPosition)
-- Encode or decode.
for pos, char in ipairs(chars) do
if pos ~= encodingCharPosition then
local byte = (char:byte() + encodingChar) % 256
chars[pos] = string.char(byte)
end
end
local decoded = table.concat(chars)
-- local t = {}
-- decoded:gsub(".",function(c) table.insert(t,c) end)
-- local near = 7
-- for i = encodingCharPosition - near, encodingCharPosition + near do
-- local c = t[i]
-- logDebug(i, c, c:byte())
-- end
return decoded
end
local function stringReplace(text, needle, replacement)
local i = text:find(needle, 1, true)
if i == nil then
return text
end
local len = needle:len()
local j = i + len - 1
assert((i % 1 == 0) and (i > 0) and (i <= j), tostring(i))
assert((j % 1 == 0), tostring(j))
local a = text:sub(1, i - 1)
local b = text:sub(j + 1)
return a .. replacement .. b
end
return C