mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-11-25 07:02:58 +00:00
CaveStory+ data files fixes
This commit is contained in:
parent
c8c847396b
commit
ae45e5513c
|
@ -13,6 +13,8 @@ byteorder = "1.3"
|
|||
ggez = { git = "https://github.com/ggez/ggez", rev = "4f4bdebff463881c36325c7e10520c9a4fd8f75c" }
|
||||
imgui = "0.4.0"
|
||||
imgui-ext = "0.3.0"
|
||||
image = {version = "0.22", default-features = false, features = ["png_codec", "pnm", "bmp"] }
|
||||
itertools = "0.9.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
maplit = "1.0.2"
|
||||
|
|
|
@ -5,13 +5,13 @@ Later plans might involve turning it into a fully-featured modding tool with liv
|
|||
|
||||
**The project is still in a very early state and nowhere near being playable. Expect lots of breaking changes and bugs**
|
||||
|
||||
[Join the Discord server](https://discord.gg/k8fYBDS)
|
||||
[Join the Discord server](https://discord.gg/fbRsNNB)
|
||||
|
||||
#### Data files
|
||||
|
||||
doukutsu-rs project does not re-distribute any copyrighted files.
|
||||
|
||||
The engine should work fine with [CSE2](https://github.com/Clownacy/CSE2)/[NXEngine](https://github.com/nxengine/nxengine-evo) modified freeware data files and [Cave Story+](https://www.nicalis.com/games/cavestory+) (Nicalis commercial release, partial loading is supported but note we're not aiming to fully support it's features) data files.
|
||||
The engine should work fine with [CSE2](https://github.com/Clownacy/CSE2) or [NXEngine](https://github.com/nxengine/nxengine-evo) modified freeware data files and [Cave Story+](https://www.nicalis.com/games/cavestory+) (Nicalis commercial release, loading is supported but note we're not aiming to fully support it's features) data files.
|
||||
|
||||
#### Roadmap
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||
use fpa::I23F9;
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
//c.bench_function("fpa lib", |b| b.iter(|| fpa_lib()));
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
|
@ -1,3 +1,4 @@
|
|||
use log::info;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use maplit::hashmap;
|
||||
|
@ -46,6 +47,7 @@ pub struct MyCharConsts {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct EngineConstants {
|
||||
pub is_cs_plus: bool,
|
||||
pub my_char: MyCharConsts,
|
||||
pub booster: BoosterConsts,
|
||||
pub tex_sizes: HashMap<String, (usize, usize)>,
|
||||
|
@ -54,6 +56,7 @@ pub struct EngineConstants {
|
|||
impl Clone for EngineConstants {
|
||||
fn clone(&self) -> Self {
|
||||
EngineConstants {
|
||||
is_cs_plus: self.is_cs_plus,
|
||||
my_char: self.my_char,
|
||||
booster: self.booster,
|
||||
tex_sizes: self.tex_sizes.clone(),
|
||||
|
@ -62,8 +65,9 @@ impl Clone for EngineConstants {
|
|||
}
|
||||
|
||||
impl EngineConstants {
|
||||
pub fn defaults() -> EngineConstants {
|
||||
pub fn defaults() -> Self {
|
||||
EngineConstants {
|
||||
is_cs_plus: false,
|
||||
my_char: MyCharConsts {
|
||||
cond: 0x80,
|
||||
flags: 0,
|
||||
|
@ -248,4 +252,11 @@ impl EngineConstants {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_csplus_patches(&mut self) {
|
||||
info!("Applying Cave Story+ constants patches...");
|
||||
|
||||
self.is_cs_plus = true;
|
||||
self.tex_sizes.insert(str!("MyChar"), (200, 384));
|
||||
}
|
||||
}
|
||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -5,7 +5,7 @@ extern crate strum_macros;
|
|||
use std::{env, mem};
|
||||
use std::path;
|
||||
|
||||
use ggez::{Context, ContextBuilder, event, GameResult};
|
||||
use ggez::{Context, ContextBuilder, event, GameResult, filesystem};
|
||||
use ggez::conf::{WindowMode, WindowSetup};
|
||||
use ggez::event::{KeyCode, KeyMods};
|
||||
use ggez::event::winit_event::{ElementState, Event, KeyboardInput, WindowEvent};
|
||||
|
@ -97,6 +97,18 @@ impl Game {
|
|||
let scale = 2.0;
|
||||
let screen_size = graphics::drawable_size(ctx);
|
||||
let canvas_size = (screen_size.0 / scale, screen_size.1 / scale);
|
||||
let mut constants = EngineConstants::defaults();
|
||||
let mut base_path = "/";
|
||||
|
||||
if filesystem::exists(ctx, "/base/Nicalis.bmp") {
|
||||
info!("Cave Story+ data files detected.");
|
||||
constants.apply_csplus_patches();
|
||||
base_path = "/base/";
|
||||
} else if filesystem::exists(ctx, "/mrmap.bin") || filesystem::exists(ctx, "/Font/font") {
|
||||
info!("CSE2E data files detected.");
|
||||
} else if filesystem::exists(ctx, "/stage.dat") || filesystem::exists(ctx, "/sprites.sif") {
|
||||
info!("NXEngine-evo data files detected.");
|
||||
}
|
||||
|
||||
let s = Game {
|
||||
scene: None,
|
||||
|
@ -108,11 +120,11 @@ impl Game {
|
|||
flags: GameFlags(0),
|
||||
key_state: KeyState(0),
|
||||
key_trigger: KeyState(0),
|
||||
texture_set: TextureSet::new("/"),
|
||||
base_path: "/".to_string(),
|
||||
texture_set: TextureSet::new(base_path),
|
||||
base_path: str!(base_path),
|
||||
stages: Vec::new(),
|
||||
sound_manager: SoundManager::new(),
|
||||
constants: EngineConstants::defaults(),
|
||||
constants,
|
||||
scale,
|
||||
screen_size,
|
||||
canvas_size,
|
||||
|
|
|
@ -11,7 +11,7 @@ pub struct Map {
|
|||
}
|
||||
|
||||
impl Map {
|
||||
pub fn load_from<R: io::Read>(mut map_data: R, mut attrib_data: R) -> io::Result<Map> {
|
||||
pub fn load_from<R: io::Read>(mut map_data: R, mut attrib_data: R) -> io::Result<Self> {
|
||||
let mut magic = [0; 3];
|
||||
|
||||
map_data.read_exact(&mut magic)?;
|
||||
|
@ -28,7 +28,7 @@ impl Map {
|
|||
let mut attrib = [0u8; 0x100];
|
||||
|
||||
map_data.read_exact(&mut tiles)?;
|
||||
attrib_data.read_exact(&mut attrib);
|
||||
attrib_data.read_exact(&mut attrib)?;
|
||||
|
||||
let map = Map {
|
||||
width,
|
||||
|
|
|
@ -107,7 +107,7 @@ impl Player {
|
|||
let constants = &state.constants;
|
||||
|
||||
let tex_player_name = str!("MyChar");
|
||||
state.texture_set.ensure_texture_loaded(ctx, &tex_player_name)?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, constants, &tex_player_name)?;
|
||||
|
||||
Ok(Player {
|
||||
x: 0,
|
||||
|
@ -580,19 +580,15 @@ impl GameEntity for Player {
|
|||
|
||||
// todo draw weapon
|
||||
|
||||
let sb = state.texture_set.tex_map.get_mut(&self.tex_player_name);
|
||||
if sb.is_none() {
|
||||
return Ok(());
|
||||
if let Some(batch) = state.texture_set.tex_map.get_mut(&self.tex_player_name) {
|
||||
batch.add_rect(
|
||||
(((self.x - self.view.left as isize) / 0x200) - (frame.x / 0x200)) as f32,
|
||||
(((self.y - self.view.top as isize) / 0x200) - (frame.y / 0x200)) as f32,
|
||||
&self.anim_rect,
|
||||
);
|
||||
batch.draw(ctx)?;
|
||||
}
|
||||
|
||||
let batch = sb.unwrap();
|
||||
batch.add_rect(
|
||||
(((self.x - self.view.left as isize) / 0x200) - (frame.x / 0x200)) as f32,
|
||||
(((self.y - self.view.top as isize) / 0x200) - (frame.y / 0x200)) as f32,
|
||||
&self.anim_rect,
|
||||
);
|
||||
batch.draw(ctx)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use ggez::{Context, GameResult};
|
||||
use ggez::nalgebra::clamp;
|
||||
use log::info;
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::entity::GameEntity;
|
||||
|
@ -10,7 +11,6 @@ use crate::scene::Scene;
|
|||
use crate::SharedGameState;
|
||||
use crate::stage::{BackgroundType, Stage};
|
||||
use crate::str;
|
||||
use num_traits::abs;
|
||||
|
||||
pub struct GameScene {
|
||||
pub tick: usize,
|
||||
|
@ -38,8 +38,8 @@ pub enum Alignment {
|
|||
}
|
||||
|
||||
impl GameScene {
|
||||
pub fn new(state: &mut SharedGameState, ctx: &mut Context, id: usize) -> GameResult<GameScene> {
|
||||
let stage = Stage::load(ctx, &state.stages[id])?;
|
||||
pub fn new(state: &mut SharedGameState, ctx: &mut Context, id: usize) -> GameResult<Self> {
|
||||
let stage = Stage::load(ctx, &state.base_path, &state.stages[id])?;
|
||||
info!("Loaded stage: {}", stage.data.name);
|
||||
info!("Map size: {}x{}", stage.map.width, stage.map.height);
|
||||
|
||||
|
@ -47,11 +47,11 @@ impl GameScene {
|
|||
let tex_background_name = stage.data.background.filename();
|
||||
let tex_hud_name = str!("TextBox");
|
||||
|
||||
state.texture_set.ensure_texture_loaded(ctx, &tex_tileset_name)?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, &tex_background_name)?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, &tex_hud_name)?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, &state.constants, &tex_tileset_name)?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, &state.constants, &tex_background_name)?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, &state.constants, &tex_hud_name)?;
|
||||
|
||||
Ok(GameScene {
|
||||
Ok(Self {
|
||||
tick: 0,
|
||||
stage,
|
||||
player: Player::new(state, ctx)?,
|
||||
|
@ -197,50 +197,46 @@ impl GameScene {
|
|||
}
|
||||
|
||||
fn draw_tiles(&self, state: &mut SharedGameState, ctx: &mut Context, layer: TileLayer) -> GameResult {
|
||||
let set = state.texture_set.tex_map.get_mut(&self.tex_tileset_name);
|
||||
if set.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(batch) = state.texture_set.tex_map.get_mut(&self.tex_tileset_name) {
|
||||
let mut rect = Rect::<usize>::new(0, 0, 16, 16);
|
||||
|
||||
let mut rect = Rect::<usize>::new(0, 0, 16, 16);
|
||||
let batch = set.unwrap();
|
||||
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize;
|
||||
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as isize) as usize;
|
||||
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize;
|
||||
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as isize) / 16 + 1, 0, self.stage.map.height as isize) as usize;
|
||||
|
||||
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize;
|
||||
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as isize) as usize;
|
||||
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize;
|
||||
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as isize) / 16 + 1, 0, self.stage.map.height as isize) as usize;
|
||||
for y in tile_start_y..tile_end_y {
|
||||
for x in tile_start_x..tile_end_x {
|
||||
let tile = *self.stage.map.tiles
|
||||
.get((y * self.stage.map.width) + x)
|
||||
.unwrap();
|
||||
|
||||
for y in tile_start_y..tile_end_y {
|
||||
for x in tile_start_x..tile_end_x {
|
||||
let tile = *self.stage.map.tiles
|
||||
.get((y * self.stage.map.width) + x)
|
||||
.unwrap();
|
||||
|
||||
match layer {
|
||||
TileLayer::Background => {
|
||||
if self.stage.map.attrib[tile as usize] >= 0x20 {
|
||||
continue;
|
||||
match layer {
|
||||
TileLayer::Background => {
|
||||
if self.stage.map.attrib[tile as usize] >= 0x20 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
TileLayer::Foreground => {
|
||||
let attr = self.stage.map.attrib[tile as usize];
|
||||
if attr < 0x40 || attr >= 0x80 {
|
||||
continue;
|
||||
TileLayer::Foreground => {
|
||||
let attr = self.stage.map.attrib[tile as usize];
|
||||
if attr < 0x40 || attr >= 0x80 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
rect.left = (tile as usize % 16) * 16;
|
||||
rect.top = (tile as usize / 16) * 16;
|
||||
rect.right = rect.left + 16;
|
||||
rect.bottom = rect.top + 16;
|
||||
|
||||
batch.add_rect((x as f32 * 16.0 - 8.0) - (self.frame.x / 0x200) as f32, (y as f32 * 16.0 - 8.0) - (self.frame.y / 0x200) as f32, &rect);
|
||||
}
|
||||
|
||||
rect.left = (tile as usize % 16) * 16;
|
||||
rect.top = (tile as usize / 16) * 16;
|
||||
rect.right = rect.left + 16;
|
||||
rect.bottom = rect.top + 16;
|
||||
|
||||
batch.add_rect((x as f32 * 16.0 - 8.0) - (self.frame.x / 0x200) as f32, (y as f32 * 16.0 - 8.0) - (self.frame.y / 0x200) as f32, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
batch.draw(ctx)?;
|
||||
batch.draw(ctx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
use ggez::{Context, GameResult};
|
||||
|
||||
use crate::player::Player;
|
||||
use crate::stage::StageData;
|
||||
use crate::scene::game_scene::GameScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::StageData;
|
||||
|
||||
pub struct LoadingScene {
|
||||
tick: usize,
|
||||
}
|
||||
|
||||
impl LoadingScene {
|
||||
pub fn new() -> LoadingScene {
|
||||
LoadingScene {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tick: 0,
|
||||
}
|
||||
}
|
||||
|
@ -20,14 +19,14 @@ impl LoadingScene {
|
|||
|
||||
impl Scene for LoadingScene {
|
||||
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
state.texture_set.ensure_texture_loaded(ctx, "Loading")?;
|
||||
state.texture_set.ensure_texture_loaded(ctx, &state.constants, "Loading")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
// deferred to let the loading image draw
|
||||
if self.tick == 1 {
|
||||
let stages = StageData::load_stage_table(ctx, "/")?;
|
||||
let stages = StageData::load_stage_table(ctx, &state.base_path)?;
|
||||
state.stages = stages;
|
||||
state.next_scene = Some(Box::new(GameScene::new(state, ctx, 53)?));
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@ impl SoundManager {
|
|||
}
|
||||
|
||||
pub fn play_song(&mut self, ctx: &mut Context) -> GameResult {
|
||||
self.intro.get_mut().clear();
|
||||
/*self.intro.get_mut().clear();
|
||||
ggez::filesystem::open(ctx, "/Soundtracks/Arranged/oside_intro.ogg")?.read_to_end(self.intro.get_mut())?;
|
||||
|
||||
let sink = rodio::play_once(ctx.audio_context.device(), self.intro.clone())?;
|
||||
sink.detach();
|
||||
sink.detach();*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -24,34 +24,8 @@ lazy_static! {
|
|||
};
|
||||
}
|
||||
|
||||
pub struct PixToneData {
|
||||
|
||||
}
|
||||
pub struct PixToneData {}
|
||||
|
||||
pub struct PixTone {}
|
||||
|
||||
impl PixTone {
|
||||
|
||||
}
|
||||
|
||||
fn dupa() -> [[i8; 0x100]; 6] {
|
||||
let mut seed = 0;
|
||||
let mut sine = [0i8; 0x100];
|
||||
let mut triangle = [0i8; 0x100];
|
||||
let mut saw_up = [0i8; 0x100];
|
||||
let mut saw_down = [0i8; 0x100];
|
||||
let mut square = [0i8; 0x100];
|
||||
let mut random = [0i8; 0x100];
|
||||
|
||||
for i in 0..255 {
|
||||
seed = (seed * 214013) + 2531011;
|
||||
sine[i] = (64.0 * (i as f32 * std::f32::consts::PI).sin()) as i8;
|
||||
triangle[i] = (if (0x40 + i as isize) & 0x80 != 0 { 0x80 - i as isize } else { i as isize }) as i8;
|
||||
saw_up[i] = (-0x40 + i as isize / 2) as i8;
|
||||
saw_down[i] = (0x40 - i as isize / 2) as i8;
|
||||
square[i] = (0x40 - (i as isize & 0x80)) as i8;
|
||||
random[i] = ((seed >> 16) / 2) as i8;
|
||||
}
|
||||
|
||||
[sine, triangle, saw_up, saw_down, square, random]
|
||||
}
|
||||
impl PixTone {}
|
||||
|
|
109
src/stage.rs
109
src/stage.rs
|
@ -1,7 +1,7 @@
|
|||
use std::io::{Cursor, Read};
|
||||
use std::str::from_utf8;
|
||||
|
||||
use byteorder::{LE, ReadBytesExt};
|
||||
use byteorder::{BE, LE, ReadBytesExt};
|
||||
use ggez::{Context, filesystem, GameResult};
|
||||
use ggez::GameError::ResourceLoadError;
|
||||
use log::info;
|
||||
|
@ -63,15 +63,15 @@ pub struct Tileset {
|
|||
|
||||
impl Clone for Tileset {
|
||||
fn clone(&self) -> Self {
|
||||
Tileset {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tileset {
|
||||
pub fn new(name: &str) -> Tileset {
|
||||
Tileset {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
}
|
||||
}
|
||||
|
@ -97,15 +97,15 @@ pub struct Background {
|
|||
|
||||
impl Clone for Background {
|
||||
fn clone(&self) -> Self {
|
||||
Background {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Background {
|
||||
pub fn new(name: &str) -> Background {
|
||||
Background {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
}
|
||||
}
|
||||
|
@ -129,17 +129,17 @@ pub enum BackgroundType {
|
|||
}
|
||||
|
||||
impl BackgroundType {
|
||||
pub fn new(id: u8) -> BackgroundType {
|
||||
pub fn new(id: usize) -> Self {
|
||||
match id {
|
||||
0 => { BackgroundType::Stationary }
|
||||
1 => { BackgroundType::MoveDistant }
|
||||
2 => { BackgroundType::MoveNear }
|
||||
3 => { BackgroundType::Water }
|
||||
4 => { BackgroundType::Black }
|
||||
5 => { BackgroundType::Autoscroll }
|
||||
6 => { BackgroundType::OutsideWind }
|
||||
7 => { BackgroundType::Outside }
|
||||
_ => { BackgroundType::Stationary }
|
||||
0 => { Self::Stationary }
|
||||
1 => { Self::MoveDistant }
|
||||
2 => { Self::MoveNear }
|
||||
3 => { Self::Water }
|
||||
4 => { Self::Black }
|
||||
5 => { Self::Autoscroll }
|
||||
6 => { Self::OutsideWind }
|
||||
7 => { Self::Outside }
|
||||
_ => { Self::Black }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,11 +172,13 @@ impl Clone for StageData {
|
|||
}
|
||||
|
||||
impl StageData {
|
||||
pub fn load_stage_table(ctx: &mut Context, root: &str) -> GameResult<Vec<StageData>> {
|
||||
// todo: refactor to make it less repetitive.
|
||||
pub fn load_stage_table(ctx: &mut Context, root: &str) -> GameResult<Vec<Self>> {
|
||||
let stage_tbl_path = [root, "stage.tbl"].join("");
|
||||
let mrmap_bin_path = [root, "mrmap.bin"].join("");
|
||||
|
||||
if filesystem::exists(ctx, &stage_tbl_path) {
|
||||
// Cave Story+ stage table. Probably one of most awful formats out there.
|
||||
let mut stages = Vec::new();
|
||||
|
||||
info!("Loading stage table from {}", &stage_tbl_path);
|
||||
|
@ -186,10 +188,59 @@ impl StageData {
|
|||
|
||||
let count = data.len() / 0xe5;
|
||||
let mut f = Cursor::new(data);
|
||||
for i in 0..count {}
|
||||
for _ in 0..count {
|
||||
let mut ts_buf = vec![0u8; 0x20];
|
||||
let mut map_buf = vec![0u8; 0x20];
|
||||
let mut back_buf = vec![0u8; 0x20];
|
||||
let mut npc_buf = vec![0u8; 0x20];
|
||||
let mut boss_buf = vec![0u8; 0x20];
|
||||
let mut name_jap_buf = vec![0u8; 0x20];
|
||||
let mut name_buf = vec![0u8; 0x20];
|
||||
|
||||
f.read_exact(&mut ts_buf)?;
|
||||
f.read_exact(&mut map_buf)?;
|
||||
let bg_type = f.read_u32::<LE>()? as usize;
|
||||
f.read_exact(&mut back_buf)?;
|
||||
f.read_exact(&mut npc_buf)?;
|
||||
f.read_exact(&mut boss_buf)?;
|
||||
let boss_no = f.read_u8()? as usize;
|
||||
f.read_exact(&mut name_jap_buf)?;
|
||||
f.read_exact(&mut name_buf)?;
|
||||
|
||||
let tileset = from_utf8(&ts_buf)
|
||||
.map_err(|_| ResourceLoadError("UTF-8 error in tileset field".to_string()))?
|
||||
.trim_matches('\0').to_owned();
|
||||
let map = from_utf8(&map_buf)
|
||||
.map_err(|_| ResourceLoadError("UTF-8 error in map field".to_string()))?
|
||||
.trim_matches('\0').to_owned();
|
||||
let background = from_utf8(&back_buf)
|
||||
.map_err(|_| ResourceLoadError("UTF-8 error in background field".to_string()))?
|
||||
.trim_matches('\0').to_owned();
|
||||
let boss = from_utf8(&boss_buf)
|
||||
.map_err(|_| ResourceLoadError("UTF-8 error in boss field".to_string()))?
|
||||
.trim_matches('\0').to_owned();
|
||||
let name = from_utf8(&name_buf)
|
||||
.map_err(|_| ResourceLoadError("UTF-8 error in name field".to_string()))?
|
||||
.trim_matches('\0').to_owned();
|
||||
|
||||
println!("bg type: {}", bg_type);
|
||||
|
||||
let stage = Self {
|
||||
name: name.clone(),
|
||||
map: map.clone(),
|
||||
boss: boss.clone(),
|
||||
boss_no,
|
||||
tileset: Tileset::new(&tileset),
|
||||
background: Background::new(&background),
|
||||
background_type: BackgroundType::new(bg_type),
|
||||
npc: NpcType::Zero,
|
||||
};
|
||||
stages.push(stage);
|
||||
}
|
||||
|
||||
return Ok(stages);
|
||||
} else if filesystem::exists(ctx, &mrmap_bin_path) {
|
||||
// CSE2 stage table
|
||||
let mut stages = Vec::new();
|
||||
|
||||
info!("Loading stage table from {}", &mrmap_bin_path);
|
||||
|
@ -205,18 +256,17 @@ impl StageData {
|
|||
}
|
||||
|
||||
let mut f = Cursor::new(data);
|
||||
|
||||
for _ in 0..count {
|
||||
let mut map_buf = Box::new(vec![0u8; 0x10]);
|
||||
let mut boss_buf = Box::new(vec![0u8; 0x10]);
|
||||
let mut name_buf = Box::new(vec![0u8; 0x22]);
|
||||
let mut ts_buf = vec![0u8; 0x10];
|
||||
let mut map_buf = vec![0u8; 0x10];
|
||||
let mut back_buf = vec![0u8; 0x10];
|
||||
let mut npc_buf = vec![0u8; 0x10];
|
||||
let mut boss_buf = vec![0u8; 0x10];
|
||||
let mut name_buf = vec![0u8; 0x22];
|
||||
|
||||
f.read_exact(&mut ts_buf)?;
|
||||
f.read_exact(&mut map_buf)?;
|
||||
let bg_type = f.read_u8()?;
|
||||
let bg_type = f.read_u8()? as usize;
|
||||
f.read_exact(&mut back_buf)?;
|
||||
f.read_exact(&mut npc_buf)?;
|
||||
f.read_exact(&mut boss_buf)?;
|
||||
|
@ -239,7 +289,7 @@ impl StageData {
|
|||
.map_err(|_| ResourceLoadError("UTF-8 error in name field".to_string()))?
|
||||
.trim_matches('\0').to_owned();
|
||||
|
||||
let stage = StageData {
|
||||
let stage = Self {
|
||||
name: name.clone(),
|
||||
map: map.clone(),
|
||||
boss: boss.clone(),
|
||||
|
@ -254,6 +304,7 @@ impl StageData {
|
|||
|
||||
return Ok(stages);
|
||||
}
|
||||
// todo: NXEngine stage.dat support?
|
||||
|
||||
Err(ResourceLoadError("No stage table found.".to_string()))
|
||||
}
|
||||
|
@ -265,12 +316,12 @@ pub struct Stage {
|
|||
}
|
||||
|
||||
impl Stage {
|
||||
pub fn load(ctx: &mut Context, data: &StageData) -> GameResult<Stage> {
|
||||
let map_file = filesystem::open(ctx, ["/Stage/", &data.map, ".pxm"].join(""))?;
|
||||
let attrib_file = filesystem::open(ctx, ["/Stage/", &data.tileset.name, ".pxa"].join(""))?;
|
||||
pub fn load(ctx: &mut Context, root: &str, data: &StageData) -> GameResult<Self> {
|
||||
let map_file = filesystem::open(ctx, [root, "Stage/", &data.map, ".pxm"].join(""))?;
|
||||
let attrib_file = filesystem::open(ctx, [root, "Stage/", &data.tileset.name, ".pxa"].join(""))?;
|
||||
let map = Map::load_from(map_file, attrib_file)?;
|
||||
|
||||
let stage = Stage {
|
||||
let stage = Self {
|
||||
map,
|
||||
data: data.clone(),
|
||||
};
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
|
||||
use ggez::{Context, GameError, GameResult};
|
||||
use ggez::filesystem;
|
||||
use ggez::graphics::{Drawable, DrawParam, FilterMode, Image, Rect};
|
||||
use ggez::graphics::spritebatch::SpriteBatch;
|
||||
use ggez::nalgebra::{Point2, Vector2};
|
||||
use itertools::Itertools;
|
||||
use log::info;
|
||||
|
||||
use crate::common;
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::str;
|
||||
|
||||
pub struct SizedBatch {
|
||||
|
@ -88,25 +91,50 @@ impl TextureSet {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_texture(&self, ctx: &mut Context, path: &str) -> GameResult<SizedBatch> {
|
||||
fn load_image(&self, ctx: &mut Context, constants: &EngineConstants, path: &str) -> GameResult<Image> {
|
||||
let img = {
|
||||
let mut buf = Vec::new();
|
||||
let mut reader = filesystem::open(ctx, path)?;
|
||||
let _ = reader.read_to_end(&mut buf)?;
|
||||
let mut rgba = image::load_from_memory(&buf)?.to_rgba();
|
||||
|
||||
// Cave Story+ data files don't have an alpha channel, therefore they need a special treatment.
|
||||
if constants.is_cs_plus {
|
||||
for (r, g, b, a) in rgba.iter_mut().tuples() {
|
||||
if *r == 0 && *g == 0 && *b == 0 {
|
||||
*a = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rgba
|
||||
};
|
||||
let (width, height) = img.dimensions();
|
||||
|
||||
Image::from_rgba8(ctx, width as u16, height as u16, img.as_ref())
|
||||
}
|
||||
|
||||
pub fn load_texture(&self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult<SizedBatch> {
|
||||
let path = FILE_TYPES
|
||||
.iter()
|
||||
.map(|ext| [&self.base_path, path, ext].join(""))
|
||||
.map(|ext| [&self.base_path, name, ext].join(""))
|
||||
.find(|path| filesystem::exists(ctx, path))
|
||||
.ok_or_else(|| GameError::ResourceLoadError(format!("Texture {:?} does not exist.", path)))?;
|
||||
.ok_or_else(|| GameError::ResourceLoadError(format!("Texture {:?} does not exist.", name)))?;
|
||||
|
||||
info!("Loading texture: {}", path);
|
||||
|
||||
let image = Image::new(ctx, path)?;
|
||||
let image = self.load_image(ctx, constants, &path)?;
|
||||
let size = image.dimensions();
|
||||
|
||||
assert_ne!(size.w, 0.0, "size.w == 0");
|
||||
assert_ne!(size.h, 0.0, "size.h == 0");
|
||||
|
||||
let scale_x = 1.0;
|
||||
let scale_y = 1.0;
|
||||
let width = (size.w / scale_x) as usize;
|
||||
let height = (size.h / scale_y) as usize;
|
||||
let dim = (size.w as usize, size.h as usize);
|
||||
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &dim);
|
||||
let scale_x = orig_dimensions.0 as f32 / size.w;
|
||||
let scale_y = orig_dimensions.1 as f32 / size.h;
|
||||
let width = (size.w * scale_x) as usize;
|
||||
let height = (size.h * scale_y) as usize;
|
||||
|
||||
Ok(SizedBatch {
|
||||
batch: SpriteBatch::new(image),
|
||||
|
@ -119,12 +147,12 @@ impl TextureSet {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn ensure_texture_loaded(&mut self, ctx: &mut Context, name: &str) -> GameResult {
|
||||
pub fn ensure_texture_loaded(&mut self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult {
|
||||
if self.tex_map.contains_key(name) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let batch = self.load_texture(ctx, name)?;
|
||||
let batch = self.load_texture(ctx, constants, name)?;
|
||||
self.tex_map.insert(str!(name), batch);
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue