remove most uses of std::io

This commit is contained in:
Alula
2024-05-29 08:38:00 +02:00
parent 79e35a00ee
commit 115f6eef01
37 changed files with 1217 additions and 487 deletions
+8
View File
@@ -0,0 +1,8 @@
---
ColumnLimit: '120'
FixNamespaceComments: 'true'
IndentWidth: '4'
Language: Cpp
PointerAlignment: Left
TabWidth: '4'
UseTab: ForIndentation
+2 -1
View File
@@ -2,7 +2,6 @@
name = "doukutsu-rs"
description = "A re-implementation of Cave Story (Doukutsu Monogatari) engine"
version = "1.0.0"
authors = ["Alula", "dawnDus"]
edition = "2021"
rust-version = "1.65"
@@ -52,6 +51,8 @@ exe = []
android = []
[dependencies]
drs-framework = { path = "./drs-framework" }
#glutin = { path = "./3rdparty/glutin/glutin", optional = true }
#winit = { path = "./3rdparty/winit", optional = true, default_features = false, features = ["x11"] }
#sdl2 = { path = "./3rdparty/rust-sdl2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "drs-framework"
version = "1.0.0"
edition = "2021"
rust-version = "1.71"
[lib]
crate-type = ["lib"]
[dependencies]
cxx = "1.0"
[build-dependencies]
cxx-build = "1.0"
+107
View File
@@ -0,0 +1,107 @@
use core::fmt;
use alloc::{
format,
string::{FromUtf8Error, String},
};
/// An enum containing all kinds of game framework errors.
#[derive(Debug, Clone)]
pub enum GameError {
/// An error in the filesystem layout
FilesystemError(String),
/// An error in the config file
ConfigError(String),
/// An error in the event loop
EventLoopError(String),
/// An error trying to load a resource, such as getting an invalid image file
ResourceLoadError(String),
/// Unable to find a resource
ResourceNotFound(String),
/// Something went wrong in the renderer
RenderError(String),
/// Something went wrong in the audio playback
AudioError(String),
/// Something went wrong trying to set or get window properties
WindowError(String),
/// Something went wrong trying to read from a file
IOError(IOErrorKind),
/// Something went wrong trying to load/render a font
FontError(String),
/// Something went wrong applying video settings
VideoError(String),
/// Something went wrong compiling shaders
ShaderProgramError(String),
/// Something went wrong while parsing something
ParseError(String),
/// Something went wrong while converting a value
InvalidValue(String),
/// Something went wrong while executing a debug command line command
CommandLineError(String),
/// Something went wrong while initializing logger
LoggerError(String),
/// We ran out of memory
AllocationError,
}
impl fmt::Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
GameError::ConfigError(ref s) => write!(f, "Config error: {}", s),
GameError::ResourceLoadError(ref s) => write!(f, "Error loading resource: {}", s),
GameError::ResourceNotFound(ref s) => {
write!(f, "Resource not found: {}", s)
}
GameError::WindowError(ref e) => write!(f, "Window creation error: {}", e),
_ => write!(f, "GameError {:?}", self),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for GameError {
fn cause(&self) -> Option<&dyn Error> {
match self {
_ => None,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum IOErrorKind {
UnexpectedEof,
InvalidUtf8Data,
WriteZero,
PermissionDenied,
InvalidInput,
Unknown,
}
impl fmt::Display for IOErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
IOErrorKind::UnexpectedEof => write!(f, "Unexpected EOF"),
IOErrorKind::InvalidUtf8Data => write!(f, "Invalid UTF-8 data"),
IOErrorKind::WriteZero => write!(f, "No bytes written"),
IOErrorKind::PermissionDenied => write!(f, "Permission denied"),
IOErrorKind::InvalidInput => write!(f, "Invalid input"),
IOErrorKind::Unknown => write!(f, "Unknown IO error"),
}
}
}
/// A convenient result type consisting of a return type and a `GameError`
pub type GameResult<T = ()> = Result<T, GameError>;
impl From<alloc::collections::TryReserveError> for GameError {
fn from(_: alloc::collections::TryReserveError) -> GameError {
GameError::AllocationError
}
}
impl From<alloc::string::FromUtf8Error> for GameError {
fn from(e: FromUtf8Error) -> Self {
let errstr = format!("UTF-8 decoding error: {:?}", e);
GameError::ConfigError(errstr)
}
}
View File
+114
View File
@@ -0,0 +1,114 @@
use crate::{error::{GameError, GameResult, IOErrorKind}, io};
use super::{Read, SeekFrom};
// The following source code has been adapted from https://github.com/rust-lang/rust/blob/1.78.0/library/std/src/io/mod.rs
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Cursor<T> {
inner: T,
pos: u64,
}
impl<T> Cursor<T> {
pub const fn new(inner: T) -> Cursor<T> {
Cursor { pos: 0, inner }
}
pub fn into_inner(self) -> T {
self.inner
}
pub const fn get_ref(&self) -> &T {
&self.inner
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
pub const fn position(&self) -> u64 {
self.pos
}
pub fn set_position(&mut self, pos: u64) {
self.pos = pos;
}
}
impl<T> Cursor<T>
where
T: AsRef<[u8]>,
{
pub fn remaining_slice(&self) -> &[u8] {
let len = self.pos.min(self.inner.as_ref().len() as u64);
&self.inner.as_ref()[(len as usize)..]
}
pub fn is_empty(&self) -> bool {
self.pos >= self.inner.as_ref().len() as u64
}
}
impl<T> Clone for Cursor<T>
where
T: Clone,
{
#[inline]
fn clone(&self) -> Self {
Cursor { inner: self.inner.clone(), pos: self.pos }
}
#[inline]
fn clone_from(&mut self, other: &Self) {
self.inner.clone_from(&other.inner);
self.pos = other.pos;
}
}
impl<T> io::Seek for Cursor<T>
where
T: AsRef<[u8]>,
{
fn seek(&mut self, style: SeekFrom) -> GameResult<u64> {
let (base_pos, offset) = match style {
SeekFrom::Start(n) => {
self.pos = n;
return Ok(n);
}
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
SeekFrom::Current(n) => (self.pos, n),
};
match base_pos.checked_add_signed(offset) {
Some(n) => {
self.pos = n;
Ok(self.pos)
}
None => {
// Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid seek to a negative or overflowing position",))
Err(GameError::IOError(IOErrorKind::InvalidInput))
}
}
}
fn stream_position(&mut self) -> GameResult<u64> {
Ok(self.pos)
}
}
impl<T> Read for Cursor<T>
where
T: AsRef<[u8]>,
{
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize> {
let n = Read::read(&mut self.remaining_slice(), buf)?;
self.pos += n as u64;
Ok(n)
}
fn read_exact(&mut self, buf: &mut [u8]) -> GameResult<()> {
let n = buf.len();
Read::read_exact(&mut self.remaining_slice(), buf)?;
self.pos += n as u64;
Ok(())
}
}
+480
View File
@@ -0,0 +1,480 @@
use core::cmp;
use alloc::{string::String, vec::Vec};
use crate::error::{GameError, GameResult, IOErrorKind};
mod cursor;
pub use cursor::*;
// The following source code has been adapted from https://github.com/rust-lang/rust/blob/1.78.0/library/std/src/io/mod.rs
struct Guard<'a> {
buf: &'a mut Vec<u8>,
len: usize,
}
impl Drop for Guard<'_> {
fn drop(&mut self) {
unsafe {
self.buf.set_len(self.len);
}
}
}
pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> GameResult<usize>
where
F: FnOnce(&mut Vec<u8>) -> GameResult<usize>,
{
let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() };
let ret = f(g.buf);
if alloc::str::from_utf8(&g.buf[g.len..]).is_err() {
ret.and_then(|_| Err(GameError::IOError(IOErrorKind::InvalidUtf8Data)))
} else {
g.len = g.buf.len();
ret
}
}
pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [u8]) -> GameResult<()> {
while !buf.is_empty() {
match this.read(buf) {
Ok(0) => break,
Ok(n) => {
buf = &mut buf[n..];
}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(GameError::IOError(IOErrorKind::UnexpectedEof))
} else {
Ok(())
}
}
const DEFAULT_BUF_SIZE: usize = 4 * 1024;
pub(crate) fn default_read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> GameResult<usize> {
let start_len = buf.len();
let mut read_buf = [0u8; DEFAULT_BUF_SIZE];
loop {
match r.read(&mut read_buf) {
Ok(0) => {
return Ok(buf.len() - start_len);
}
Ok(n) => buf.extend_from_slice(&read_buf[..n]),
Err(e) => return Err(e),
}
}
}
pub(crate) fn default_read_to_string<R: Read + ?Sized>(r: &mut R, buf: &mut String) -> GameResult<usize> {
// Note that we do *not* call `r.read_to_end()` here. We are passing
// `&mut Vec<u8>` (the raw contents of `buf`) into the `read_to_end`
// method to fill it up. An arbitrary implementation could overwrite the
// entire contents of the vector, not just append to it (which is what
// we are expecting).
//
// To prevent extraneously checking the UTF-8-ness of the entire buffer
// we pass it to our hardcoded `default_read_to_end` implementation which
// we know is guaranteed to only read data into the end of the buffer.
unsafe { append_to_string(buf, |b| default_read_to_end(r, b)) }
}
pub trait Read {
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize>;
fn read_exact(&mut self, buf: &mut [u8]) -> GameResult<()> {
default_read_exact(self, buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> GameResult<usize> {
default_read_to_end(self, buf)
}
fn read_to_string(&mut self, buf: &mut String) -> GameResult<usize> {
default_read_to_string(self, buf)
}
fn by_ref(&mut self) -> &mut Self
where
Self: Sized,
{
self
}
#[inline]
fn read_u8(&mut self) -> GameResult<u8> {
let mut buf = [0; 1];
self.read_exact(&mut buf)?;
Ok(buf[0])
}
#[inline]
fn read_i8(&mut self) -> GameResult<i8> {
let mut buf = [0; 1];
self.read_exact(&mut buf)?;
Ok(buf[0] as i8)
}
#[inline]
fn read_u16_le(&mut self) -> GameResult<u16> {
let mut buf = [0; 2];
self.read_exact(&mut buf)?;
Ok(u16::from_le_bytes(buf))
}
#[inline]
fn read_u16_be(&mut self) -> GameResult<u16> {
let mut buf = [0; 2];
self.read_exact(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}
#[inline]
fn read_i16_le(&mut self) -> GameResult<i16> {
let mut buf = [0; 2];
self.read_exact(&mut buf)?;
Ok(i16::from_le_bytes(buf))
}
#[inline]
fn read_i16_be(&mut self) -> GameResult<i16> {
let mut buf = [0; 2];
self.read_exact(&mut buf)?;
Ok(i16::from_be_bytes(buf))
}
#[inline]
fn read_u32_le(&mut self) -> GameResult<u32> {
let mut buf = [0; 4];
self.read_exact(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
#[inline]
fn read_u32_be(&mut self) -> GameResult<u32> {
let mut buf = [0; 4];
self.read_exact(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}
#[inline]
fn read_i32_le(&mut self) -> GameResult<i32> {
let mut buf = [0; 4];
self.read_exact(&mut buf)?;
Ok(i32::from_le_bytes(buf))
}
#[inline]
fn read_i32_be(&mut self) -> GameResult<i32> {
let mut buf = [0; 4];
self.read_exact(&mut buf)?;
Ok(i32::from_be_bytes(buf))
}
#[inline]
fn read_u64_le(&mut self) -> GameResult<u64> {
let mut buf = [0; 8];
self.read_exact(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
#[inline]
fn read_u64_be(&mut self) -> GameResult<u64> {
let mut buf = [0; 8];
self.read_exact(&mut buf)?;
Ok(u64::from_be_bytes(buf))
}
#[inline]
fn read_i64_le(&mut self) -> GameResult<i64> {
let mut buf = [0; 8];
self.read_exact(&mut buf)?;
Ok(i64::from_le_bytes(buf))
}
#[inline]
fn read_i64_be(&mut self) -> GameResult<i64> {
let mut buf = [0; 8];
self.read_exact(&mut buf)?;
Ok(i64::from_be_bytes(buf))
}
#[inline]
fn read_u128_le(&mut self) -> GameResult<u128> {
let mut buf = [0; 16];
self.read_exact(&mut buf)?;
Ok(u128::from_le_bytes(buf))
}
#[inline]
fn read_u128_be(&mut self) -> GameResult<u128> {
let mut buf = [0; 16];
self.read_exact(&mut buf)?;
Ok(u128::from_be_bytes(buf))
}
#[inline]
fn read_i128_le(&mut self) -> GameResult<i128> {
let mut buf = [0; 16];
self.read_exact(&mut buf)?;
Ok(i128::from_le_bytes(buf))
}
#[inline]
fn read_i128_be(&mut self) -> GameResult<i128> {
let mut buf = [0; 16];
self.read_exact(&mut buf)?;
Ok(i128::from_be_bytes(buf))
}
#[inline]
fn read_f32_le(&mut self) -> GameResult<f32> {
let mut buf = [0; 4];
self.read_exact(&mut buf)?;
Ok(f32::from_le_bytes(buf))
}
#[inline]
fn read_f32_be(&mut self) -> GameResult<f32> {
let mut buf = [0; 4];
self.read_exact(&mut buf)?;
Ok(f32::from_be_bytes(buf))
}
#[inline]
fn read_f64_le(&mut self) -> GameResult<f64> {
let mut buf = [0; 8];
self.read_exact(&mut buf)?;
Ok(f64::from_le_bytes(buf))
}
#[inline]
fn read_f64_be(&mut self) -> GameResult<f64> {
let mut buf = [0; 8];
self.read_exact(&mut buf)?;
Ok(f64::from_be_bytes(buf))
}
}
pub trait Write {
fn write(&mut self, buf: &[u8]) -> GameResult<usize>;
fn flush(&mut self) -> GameResult<()>;
fn write_all(&mut self, mut buf: &[u8]) -> GameResult<()> {
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => {
return Err(GameError::IOError(IOErrorKind::WriteZero));
}
Ok(n) => buf = &buf[n..],
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
fn write_u8(&mut self, n: u8) -> GameResult<()> {
self.write_all(&[n])
}
#[inline]
fn write_i8(&mut self, n: i8) -> GameResult<()> {
self.write_all(&[n as u8])
}
#[inline]
fn write_u16_le(&mut self, n: u16) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_u16_be(&mut self, n: u16) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_i16_le(&mut self, n: i16) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_i16_be(&mut self, n: i16) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_u32_le(&mut self, n: u32) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_u32_be(&mut self, n: u32) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_i32_le(&mut self, n: i32) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_i32_be(&mut self, n: i32) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_u64_le(&mut self, n: u64) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_u64_be(&mut self, n: u64) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_i64_le(&mut self, n: i64) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_i64_be(&mut self, n: i64) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_u128_le(&mut self, n: u128) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_u128_be(&mut self, n: u128) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_i128_le(&mut self, n: i128) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_i128_be(&mut self, n: i128) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_f32_le(&mut self, n: f32) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_f32_be(&mut self, n: f32) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
#[inline]
fn write_f64_le(&mut self, n: f64) -> GameResult<()> {
self.write_all(&n.to_le_bytes())
}
#[inline]
fn write_f64_be(&mut self, n: f64) -> GameResult<()> {
self.write_all(&n.to_be_bytes())
}
}
pub trait Seek {
fn seek(&mut self, pos: SeekFrom) -> GameResult<u64>;
fn rewind(&mut self) -> GameResult<()> {
self.seek(SeekFrom::Start(0))?;
Ok(())
}
fn stream_position(&mut self) -> GameResult<u64> {
self.seek(SeekFrom::Current(0))
}
}
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum SeekFrom {
Start(u64),
End(i64),
Current(i64),
}
impl Read for &[u8] {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize> {
let amt = cmp::min(buf.len(), self.len());
let (a, b) = self.split_at(amt);
// First check if the amount of bytes we want to read is small:
// `copy_from_slice` will generally expand to a call to `memcpy`, and
// for a single byte the overhead is significant.
if amt == 1 {
buf[0] = a[0];
} else {
buf[..amt].copy_from_slice(a);
}
*self = b;
Ok(amt)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> GameResult<()> {
if buf.len() > self.len() {
return Err(GameError::IOError(IOErrorKind::UnexpectedEof));
}
let (a, b) = self.split_at(buf.len());
// First check if the amount of bytes we want to read is small:
// `copy_from_slice` will generally expand to a call to `memcpy`, and
// for a single byte the overhead is significant.
if buf.len() == 1 {
buf[0] = a[0];
} else {
buf.copy_from_slice(a);
}
*self = b;
Ok(())
}
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> GameResult<usize> {
let len = self.len();
buf.try_reserve(len)?;
buf.extend_from_slice(*self);
*self = &self[len..];
Ok(len)
}
}
impl<R: Read + ?Sized> Read for &mut R {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize> {
(**self).read(buf)
}
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> GameResult<usize> {
(**self).read_to_end(buf)
}
#[inline]
fn read_to_string(&mut self, buf: &mut String) -> GameResult<usize> {
(**self).read_to_string(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> GameResult<()> {
(**self).read_exact(buf)
}
}
+7
View File
@@ -0,0 +1,7 @@
#![no_std]
extern crate alloc;
pub mod error;
pub mod filesystem;
pub mod io;
+7 -7
View File
@@ -1,16 +1,16 @@
use byteorder::{LE, ReadBytesExt, WriteBytesExt};
use drs_framework::io::{Read, Write};
use crate::common::Rect;
use crate::components::draw_common::{Alignment, draw_number, draw_number_zeros};
use crate::components::draw_common::{draw_number, draw_number_zeros, Alignment};
use crate::entity::GameEntity;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::filesystem;
use crate::framework::vfs::OpenOptions;
use crate::game::frame::Frame;
use crate::game::shared_game_state::{SharedGameState, TimingMode};
use crate::game::player::Player;
use crate::game::scripting::tsc::text_script::TextScriptExecutionState;
use crate::game::shared_game_state::{SharedGameState, TimingMode};
use crate::util::rng::RNG;
#[derive(Clone, Copy)]
@@ -29,10 +29,10 @@ impl NikumaruCounter {
let mut ticks: [u32; 4] = [0; 4];
for iter in 0..=3 {
ticks[iter] = data.read_u32::<LE>()?;
ticks[iter] = data.read_u32_le()?;
}
let random = data.read_u32::<LE>()?;
let random = data.read_u32_le()?;
let random_list: [u8; 4] = random.to_le_bytes();
for iter in 0..=3 {
@@ -72,10 +72,10 @@ impl NikumaruCounter {
ticks[iter].to_le_bytes()[3].wrapping_add(random_list[iter] / 2),
]);
data.write_u32::<LE>(ticks[iter])?;
data.write_u32_le(ticks[iter])?;
}
data.write_u32::<LE>(u32::from_le_bytes(random_list))?;
data.write_u32_le(u32::from_le_bytes(random_list))?;
} else {
log::warn!("Failed to write 290 record.");
}
+11 -17
View File
@@ -1,6 +1,4 @@
use std::io::{Cursor, Read};
use byteorder::{LE, ReadBytesExt, WriteBytesExt};
use drs_framework::io::{Cursor, Read, Write};
use crate::entity::GameEntity;
use crate::framework::context::Context;
@@ -9,10 +7,10 @@ use crate::framework::filesystem;
use crate::framework::keyboard::ScanCode;
use crate::framework::vfs::OpenOptions;
use crate::game::frame::Frame;
use crate::game::shared_game_state::{ReplayKind, ReplayState, SharedGameState};
use crate::input::replay_player_controller::{KeyState, ReplayController};
use crate::game::player::Player;
use crate::game::shared_game_state::{ReplayKind, ReplayState, SharedGameState};
use crate::graphics::font::Font;
use crate::input::replay_player_controller::{KeyState, ReplayController};
#[derive(Clone)]
pub struct Replay {
@@ -85,10 +83,10 @@ impl Replay {
[state.get_rec_filename(), replay_kind.get_suffix()].join(""),
OpenOptions::new().write(true).create(true),
) {
file.write_u16::<LE>(0)?; // Space for versioning replay files
file.write_u64::<LE>(self.rng_seed)?;
file.write_u16_le(0)?; // Space for versioning replay files
file.write_u64_le(self.rng_seed)?;
for input in &self.keylist {
file.write_u16::<LE>(*input)?;
file.write_u16_le(*input)?;
}
}
Ok(())
@@ -97,8 +95,8 @@ impl Replay {
fn read_replay(&mut self, state: &mut SharedGameState, ctx: &mut Context, replay_kind: ReplayKind) -> GameResult {
if let Ok(mut file) = filesystem::user_open(ctx, [state.get_rec_filename(), replay_kind.get_suffix()].join(""))
{
self.replay_version = file.read_u16::<LE>()?;
self.rng_seed = file.read_u64::<LE>()?;
self.replay_version = file.read_u16_le()?;
self.rng_seed = file.read_u64_le()?;
let mut data = Vec::new();
file.read_to_end(&mut data)?;
@@ -108,7 +106,7 @@ impl Replay {
let mut f = Cursor::new(data);
for _ in 0..count {
inputs.push(f.read_u16::<LE>()?);
inputs.push(f.read_u16_le()?);
}
self.keylist = inputs;
@@ -172,14 +170,10 @@ impl GameEntity<(&mut Context, &mut Player)> for Replay {
match state.replay_state {
ReplayState::None => {}
ReplayState::Playback(_) => {
state.font.builder()
.position(x, y)
.draw("PLAY", ctx, &state.constants, &mut state.texture_set)?;
state.font.builder().position(x, y).draw("PLAY", ctx, &state.constants, &mut state.texture_set)?;
}
ReplayState::Recording => {
state.font.builder()
.position(x, y)
.draw("REC", ctx, &state.constants, &mut state.texture_set)?;
state.font.builder().position(x, y).draw("REC", ctx, &state.constants, &mut state.texture_set)?;
}
}
+13 -12
View File
@@ -1,13 +1,14 @@
use std::{fmt, io};
use std::fmt;
use std::fmt::Debug;
use std::io::Cursor;
use std::io::ErrorKind;
use std::io::SeekFrom;
use drs_framework::io::Cursor;
use std::path::{Component, Path, PathBuf};
use drs_framework::error::{GameError, IOErrorKind};
use drs_framework::io::{self, SeekFrom};
use crate::framework::error::GameError::FilesystemError;
use crate::framework::error::GameResult;
use crate::framework::vfs::{OpenOptions, VFile, VFS, VMetadata};
use crate::framework::vfs::{OpenOptions, VFile, VMetadata, VFS};
#[derive(Debug)]
pub struct BuiltinFile(Cursor<&'static [u8]>);
@@ -19,24 +20,24 @@ impl BuiltinFile {
}
impl io::Read for BuiltinFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize> {
self.0.read(buf)
}
}
impl io::Seek for BuiltinFile {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
fn seek(&mut self, pos: SeekFrom) -> GameResult<u64> {
self.0.seek(pos)
}
}
impl io::Write for BuiltinFile {
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(io::Error::new(ErrorKind::PermissionDenied, "Built-in file system is read-only."))
fn write(&mut self, _buf: &[u8]) -> GameResult<usize> {
Err(GameError::IOError(IOErrorKind::PermissionDenied))
}
fn flush(&mut self) -> io::Result<()> {
Err(io::Error::new(ErrorKind::PermissionDenied, "Built-in file system is read-only."))
fn flush(&mut self) -> GameResult<()> {
Err(GameError::IOError(IOErrorKind::PermissionDenied))
}
}
@@ -278,7 +279,7 @@ impl VFS for BuiltinFS {
self.get_node(path).map(|v| v.to_metadata())
}
fn read_dir(&self, path: &Path) -> GameResult<Box<dyn Iterator<Item=GameResult<PathBuf>>>> {
fn read_dir(&self, path: &Path) -> GameResult<Box<dyn Iterator<Item = GameResult<PathBuf>>>> {
match self.get_node(path) {
Ok(FSNode::Directory(_, contents)) => {
let mut vec = Vec::new();
+5 -5
View File
@@ -174,11 +174,11 @@ impl VanillaExtractor {
let mut file = file.unwrap();
file.write_u8(0x42)?; // B
file.write_u8(0x4D)?; // M
file.write_u32::<LE>(bitmap.bytes.len() as u32 + 0xE)?; // Size of BMP file
file.write_u32::<LE>(0)?; // unused null bytes
file.write_u32::<LE>(0x76)?; // Bitmap data offset (hardcoded for now, might wanna get the actual offset)
file.write_u8(0x42).unwrap(); // B
file.write_u8(0x4D).unwrap(); // M
file.write_u32::<LE>(bitmap.bytes.len() as u32 + 0xE).unwrap(); // Size of BMP file
file.write_u32::<LE>(0).unwrap(); // unused null bytes
file.write_u32::<LE>(0x76).unwrap(); // Bitmap data offset (hardcoded for now, might wanna get the actual offset)
let result = file.write_all(&bitmap.bytes);
if result.is_err() {
+45 -41
View File
@@ -1,9 +1,8 @@
use std::collections::HashMap;
use std::io::{BufRead, BufReader, Cursor, Read};
use byteorder::{ReadBytesExt, LE};
use case_insensitive_hashmap::CaseInsensitiveHashMap;
use xmltree::Element;
use drs_framework::error::GameError;
use drs_framework::io::{Cursor, Read};
use crate::case_insensitive_hashmap;
use crate::common::{BulletFlag, Color, Rect};
@@ -1756,28 +1755,30 @@ impl EngineConstants {
pub fn load_nx_stringtable(&mut self, ctx: &mut Context) -> GameResult {
if let Ok(file) = filesystem::open(ctx, "/base/stringtable.sta") {
let mut reader = BufReader::new(file);
// let mut reader = BufReader::new(file);
// Only some versions start with the BOM marker, thankfully the file isn't that large to read twice
let mut bom = [0xef, 0xbb, 0xbf];
let buf = reader.fill_buf()?;
if buf.len() > 3 && buf[0..3] == bom {
reader.read_exact(&mut bom)?;
}
// // Only some versions start with the BOM marker, thankfully the file isn't that large to read twice
// let mut bom = [0xef, 0xbb, 0xbf];
// let buf = reader.fill_buf()?;
// if buf.len() > 3 && buf[0..3] == bom {
// reader.read_exact(&mut bom)?;
// }
if let Ok(xml) = Element::parse(reader) {
for node in &xml.get_child("category").unwrap().children {
let element = node.as_element().unwrap();
let key = element.attributes.get_key_value("name").unwrap().1.to_string();
let english = element
.get_child("string")
.unwrap()
.get_text()
.unwrap_or(std::borrow::Cow::Borrowed(""))
.to_string();
self.string_table.insert(key, english);
}
}
// if let Ok(xml) = Element::parse(reader) {
// for node in &xml.get_child("category").unwrap().children {
// let element = node.as_element().unwrap();
// let key = element.attributes.get_key_value("name").unwrap().1.to_string();
// let english = element
// .get_child("string")
// .unwrap()
// .get_text()
// .unwrap_or(std::borrow::Cow::Borrowed(""))
// .to_string();
// self.string_table.insert(key, english);
// }
// }
return Err(GameError::ResourceLoadError("TODO: unimplemented".to_owned()));
}
Ok(())
}
@@ -1814,8 +1815,10 @@ impl EngineConstants {
pub fn apply_constant_json_files(&mut self) {}
pub fn load_texture_size_hints(&mut self, ctx: &mut Context) -> GameResult {
if let Ok(file) = filesystem::open_find(ctx, &self.base_paths, "texture_sizes.json") {
match serde_json::from_reader::<_, TextureSizeTable>(file) {
if let Ok(mut file) = filesystem::open_find(ctx, &self.base_paths, "texture_sizes.json") {
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
match serde_json::from_slice::<TextureSizeTable>(&buf) {
Ok(tex_overrides) => {
for (key, (x, y)) in tex_overrides.sizes {
self.tex_sizes.insert(key, (x, y));
@@ -1842,17 +1845,17 @@ impl EngineConstants {
let bullet = BulletData {
damage: f.read_u8()?,
life: f.read_u8()?,
lifetime: f.read_u32::<LE>()? as u16,
flags: BulletFlag(f.read_u32::<LE>()? as u8),
enemy_hit_width: f.read_u32::<LE>()? as u16,
enemy_hit_height: f.read_u32::<LE>()? as u16,
block_hit_width: f.read_u32::<LE>()? as u16,
block_hit_height: f.read_u32::<LE>()? as u16,
lifetime: f.read_u32_le()? as u16,
flags: BulletFlag(f.read_u32_le()? as u8),
enemy_hit_width: f.read_u32_le()? as u16,
enemy_hit_height: f.read_u32_le()? as u16,
block_hit_width: f.read_u32_le()? as u16,
block_hit_height: f.read_u32_le()? as u16,
display_bounds: Rect {
left: f.read_u32::<LE>()? as u8,
top: f.read_u32::<LE>()? as u8,
right: f.read_u32::<LE>()? as u8,
bottom: f.read_u32::<LE>()? as u8,
left: f.read_u32_le()? as u8,
top: f.read_u32_le()? as u8,
right: f.read_u32_le()? as u8,
bottom: f.read_u32_le()? as u8,
},
};
new_bullet_table.push(bullet);
@@ -1869,9 +1872,9 @@ impl EngineConstants {
let mut new_level_table = EngineConstants::defaults().weapon.level_table;
for iter in 0..14 {
let level1 = f.read_u32::<LE>()? as u16;
let level2 = f.read_u32::<LE>()? as u16;
let level3 = f.read_u32::<LE>()? as u16;
let level1 = f.read_u32_le()? as u16;
let level2 = f.read_u32_le()? as u16;
let level3 = f.read_u32_le()? as u16;
new_level_table[iter] = [level1, level2, level3];
}
@@ -1890,13 +1893,14 @@ impl EngineConstants {
// Bugfix for Malco cutscene - this face should be used but the original tsc has the wrong ID
self.animated_face_table.push(AnimatedFace { face_id: 5, anim_id: 4, anim_frames: vec![(4, 0)] });
if let Ok(file) = filesystem::open_find(ctx, &self.base_paths, "faceanm.dat") {
let buf = BufReader::new(file);
if let Ok(mut file) = filesystem::open_find(ctx, &self.base_paths, "faceanm.dat") {
let mut buf = String::new();
file.read_to_string(&mut buf)?;
let mut face_id = 1;
let mut anim_id = 0;
for line in buf.lines() {
let line_str = line?.to_owned().replace(",", " ");
let line_str = line.replace(",", " ");
let mut anim_frames = Vec::new();
if line_str.find("\\") == None {
+8 -19
View File
@@ -1,8 +1,8 @@
use core::mem;
use drs_framework::io::Read;
use std::any::Any;
use std::cell::{RefCell, UnsafeCell};
use std::ffi::c_void;
use std::io::Read;
use std::ops::Deref;
use std::ptr::{null, null_mut};
use std::rc::Rc;
@@ -163,7 +163,7 @@ impl SDL2EventLoop {
let event_pump = sdl.event_pump().map_err(GameError::WindowError)?;
let video = sdl.video().map_err(GameError::WindowError)?;
let game_controller = sdl.game_controller().map_err(GameError::GamepadError)?;
let game_controller = sdl.game_controller().map_err(GameError::WindowError)?;
let mut controller_mappings = filesystem::open(ctx, "/builtin/gamecontrollerdb.txt")?;
game_controller.load_mappings_from_read(&mut controller_mappings).unwrap();
@@ -185,14 +185,13 @@ impl SDL2EventLoop {
let mut file = filesystem::open(&ctx, "/builtin/icon.bmp").unwrap();
let mut buf: Vec<u8> = Vec::new();
file.read_to_end(&mut buf)?;
let mut rwops = RWops::from_bytes(buf.as_slice()).unwrap();
let icon = Surface::load_bmp_rw(&mut rwops).unwrap();
window.set_icon(icon);
}
let opengl_available = if let Ok(v) = std::env::var("CAVESTORY_NO_OPENGL") { v != "1" } else { true };
let event_loop = SDL2EventLoop {
@@ -216,9 +215,7 @@ impl BackendEventLoop for SDL2EventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let state = unsafe { &mut *game.state.get() };
let imgui = unsafe {
(&*(ctx.renderer.as_ref().unwrap() as *const Box<dyn BackendRenderer>)).imgui().unwrap()
};
let imgui = unsafe { (&*(ctx.renderer.as_ref().unwrap() as *const Box<dyn BackendRenderer>)).imgui().unwrap() };
let mut imgui_sdl2 = ImguiSdl2::new(imgui, self.refs.deref().borrow().window.window());
{
@@ -399,11 +396,7 @@ impl BackendEventLoop for SDL2EventLoop {
let show_cursor = state.settings.window_mode.should_display_mouse_cursor();
window.set_fullscreen(fullscreen_type);
window
.subsystem()
.sdl()
.mouse()
.show_cursor(show_cursor);
window.subsystem().sdl().mouse().show_cursor(show_cursor);
refs.fullscreen_type = fullscreen_type;
}
@@ -591,11 +584,7 @@ impl SDL2Renderer {
};
imgui.fonts().tex_id = TextureId::new(imgui_font_tex.texture.as_ref().unwrap().raw() as usize);
Ok(Box::new(SDL2Renderer {
refs,
imgui: Rc::new(RefCell::new(imgui)),
imgui_font_tex,
}))
Ok(Box::new(SDL2Renderer { refs, imgui: Rc::new(RefCell::new(imgui)), imgui_font_tex }))
}
}
+2 -77
View File
@@ -1,78 +1,10 @@
//! Error types and conversion functions.
use std::error::Error;
use std::fmt;
use std::string::FromUtf8Error;
use core::fmt;
use std::sync::mpsc::SendError;
use std::sync::{Arc, PoisonError};
/// An enum containing all kinds of game framework errors.
#[derive(Debug, Clone)]
pub enum GameError {
/// An error in the filesystem layout
FilesystemError(String),
/// An error in the config file
ConfigError(String),
/// Happens when an `winit::EventsLoopProxy` attempts to
/// wake up an `winit::EventsLoop` that no longer exists.
EventLoopError(String),
/// An error trying to load a resource, such as getting an invalid image file.
ResourceLoadError(String),
/// Unable to find a resource; the `Vec` is the paths it searched for and associated errors
ResourceNotFound(String, Vec<(std::path::PathBuf, GameError)>),
/// Something went wrong in the renderer
RenderError(String),
/// Something went wrong in the audio playback
AudioError(String),
/// Something went wrong trying to set or get window properties.
WindowError(String),
/// Something went wrong trying to read from a file
IOError(Arc<std::io::Error>),
/// Something went wrong trying to load/render a font
FontError(String),
/// Something went wrong applying video settings.
VideoError(String),
/// Something went wrong compiling shaders
ShaderProgramError(String),
/// Something went wrong with the `gilrs` gamepad-input library.
GamepadError(String),
/// Something went wrong with the `lyon` shape-tesselation library.
LyonError(String),
/// Something went wrong while parsing something.
ParseError(String),
/// Something went wrong while converting a value.
InvalidValue(String),
/// Something went wrong while executing a debug command line command.
CommandLineError(String),
/// Something went wrong while initializing logger
LoggerError(String),
}
impl fmt::Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
GameError::ConfigError(ref s) => write!(f, "Config error: {}", s),
GameError::ResourceLoadError(ref s) => write!(f, "Error loading resource: {}", s),
GameError::ResourceNotFound(ref s, ref paths) => {
write!(f, "Resource not found: {}, searched in paths {:?}", s, paths)
}
GameError::WindowError(ref e) => write!(f, "Window creation error: {}", e),
_ => write!(f, "GameError {:?}", self),
}
}
}
impl Error for GameError {
fn cause(&self) -> Option<&dyn Error> {
match self {
GameError::IOError(e) => Some(e as &dyn Error),
_ => None,
}
}
}
/// A convenient result type consisting of a return type and a `GameError`
pub type GameResult<T = ()> = Result<T, GameError>;
pub use drs_framework::error::*;
impl From<std::io::Error> for GameError {
fn from(e: std::io::Error) -> GameError {
@@ -87,13 +19,6 @@ impl From<image::ImageError> for GameError {
}
}
impl From<std::string::FromUtf8Error> for GameError {
fn from(e: FromUtf8Error) -> Self {
let errstr = format!("UTF-8 decoding error: {:?}", e);
GameError::ConfigError(errstr)
}
}
impl From<serde_json::Error> for GameError {
fn from(e: serde_json::Error) -> Self {
let errstr = format!("JSON error: {:?}", e);
+31 -8
View File
@@ -1,9 +1,10 @@
use std::fmt;
use std::io;
use std::io::SeekFrom;
use core::fmt;
use std::path;
use std::path::PathBuf;
use drs_framework::io;
use drs_framework::io::SeekFrom;
use crate::framework::context::Context;
use crate::framework::error::{GameError, GameResult};
use crate::framework::vfs;
@@ -37,7 +38,7 @@ impl fmt::Debug for File {
}
impl io::Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize> {
match *self {
File::VfsFile(ref mut f) => f.read(buf),
}
@@ -45,13 +46,13 @@ impl io::Read for File {
}
impl io::Write for File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
fn write(&mut self, buf: &[u8]) -> GameResult<usize> {
match *self {
File::VfsFile(ref mut f) => f.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
fn flush(&mut self) -> GameResult<()> {
match *self {
File::VfsFile(ref mut f) => f.flush(),
}
@@ -59,13 +60,35 @@ impl io::Write for File {
}
impl io::Seek for File {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
fn seek(&mut self, pos: SeekFrom) -> GameResult<u64> {
match *self {
File::VfsFile(ref mut f) => f.seek(pos),
}
}
}
impl std::io::Read for File {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match *self {
File::VfsFile(ref mut f) => f.read(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, "")),
}
}
}
impl std::io::Seek for File {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let pos = match pos {
std::io::SeekFrom::Start(n) => SeekFrom::Start(n),
std::io::SeekFrom::End(n) => SeekFrom::End(n),
std::io::SeekFrom::Current(n) => SeekFrom::Current(n),
};
match *self {
File::VfsFile(ref mut f) => f.seek(pos).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, "")),
}
}
}
#[allow(unused)]
impl Filesystem {
pub fn new() -> Filesystem {
@@ -251,7 +274,7 @@ pub fn open_find<P: AsRef<path::Path>>(ctx: &Context, roots: &Vec<String>, path:
errors.push((PathBuf::from(full_path), result.err().unwrap()));
}
Err(GameError::ResourceNotFound("File not found".to_owned(), errors))
Err(GameError::ResourceNotFound("File not found".to_owned()))
}
/// Opens the given path in the user directory and returns the resulting `File`
+3 -1
View File
@@ -9,7 +9,7 @@ pub mod backend_null;
#[cfg(feature = "backend-sdl")]
pub mod backend_sdl2;
pub mod context;
pub mod error;
// pub mod error;
pub mod filesystem;
pub mod gamepad;
#[cfg(feature = "render-opengl")]
@@ -21,3 +21,5 @@ pub mod render_opengl;
pub mod ui;
pub mod util;
pub mod vfs;
pub use drs_framework::error;
+60 -36
View File
@@ -9,13 +9,15 @@
//! as a trait object, and its path abstraction is not the most
//! convenient.
use std::collections::VecDeque;
extern crate alloc;
use alloc::collections::VecDeque;
use core::fmt::{self, Debug};
use std::ffi::OsStr;
use std::fmt::{self, Debug};
use std::fs;
use std::io::{Read, Seek, Write};
use std::path::{self, Component, Path, PathBuf};
use drs_framework::io::{Read, Seek, SeekFrom, Write};
use crate::framework::error::{GameError, GameResult};
fn convenient_path_to_str(path: &path::Path) -> GameResult<&str> {
@@ -294,7 +296,7 @@ impl PhysicalFS {
/// malus.
fn create_root(&self) -> GameResult {
if !self.root.exists() {
fs::create_dir_all(&self.root).map_err(GameError::from)
fs::create_dir_all(&self.root).map_err(|e| GameError::FilesystemError(e.to_string()))
} else {
Ok(())
}
@@ -317,7 +319,11 @@ impl VFS for PhysicalFS {
}
self.create_root()?;
let p = self.to_absolute(path)?;
open_options.to_fs_openoptions().open(p).map(|x| Box::new(x) as Box<dyn VFile>).map_err(GameError::from)
open_options
.to_fs_openoptions()
.open(p)
.map(|x| Box::new(FSFileWrapper(x)) as Box<dyn VFile>)
.map_err(|e| GameError::FilesystemError(e.to_string()))
}
/// Create a directory at the location by this path
@@ -332,7 +338,7 @@ impl VFS for PhysicalFS {
self.create_root()?;
let p = self.to_absolute(path)?;
//println!("Creating {:?}", p);
fs::DirBuilder::new().recursive(true).create(p).map_err(GameError::from)
fs::DirBuilder::new().recursive(true).create(p).map_err(|e| GameError::FilesystemError(e.to_string()))
}
/// Remove a file
@@ -344,9 +350,9 @@ impl VFS for PhysicalFS {
self.create_root()?;
let p = self.to_absolute(path)?;
if p.is_dir() {
fs::remove_dir(p).map_err(GameError::from)
fs::remove_dir(p).map_err(|e| GameError::FilesystemError(e.to_string()))
} else {
fs::remove_file(p).map_err(GameError::from)
fs::remove_file(p).map_err(|e| GameError::FilesystemError(e.to_string()))
}
}
@@ -363,9 +369,9 @@ impl VFS for PhysicalFS {
self.create_root()?;
let p = self.to_absolute(path)?;
if p.is_dir() {
fs::remove_dir_all(p).map_err(GameError::from)
fs::remove_dir_all(p).map_err(|e| GameError::FilesystemError(e.to_string()))
} else {
fs::remove_file(p).map_err(GameError::from)
fs::remove_file(p).map_err(|e| GameError::FilesystemError(e.to_string()))
}
}
@@ -381,7 +387,9 @@ impl VFS for PhysicalFS {
fn metadata(&self, path: &Path) -> GameResult<Box<dyn VMetadata>> {
self.create_root()?;
let p = self.to_absolute(path)?;
p.metadata().map(|m| Box::new(PhysicalMetadata(m)) as Box<dyn VMetadata>).map_err(GameError::from)
p.metadata()
.map(|m| Box::new(PhysicalMetadata(m)) as Box<dyn VMetadata>)
.map_err(|e| GameError::FilesystemError(e.to_string()))
}
/// Retrieve the path entries in this path
@@ -402,7 +410,11 @@ impl VFS for PhysicalFS {
pathbuf.push(fname);
Ok(pathbuf)
};
let itr = fs::read_dir(p)?.map(|entry| direntry_to_path(&entry?)).collect::<Vec<_>>().into_iter();
let itr = fs::read_dir(p)
.map_err(|e| GameError::FilesystemError(e.to_string()))?
.map(|entry| direntry_to_path(&entry.map_err(|e| GameError::FilesystemError(e.to_string()))?))
.collect::<Vec<_>>()
.into_iter();
Ok(Box::new(itr))
}
@@ -451,22 +463,14 @@ impl OverlayFS {
impl VFS for OverlayFS {
/// Open the file at this path with the given options
fn open_options(&self, path: &Path, open_options: OpenOptions) -> GameResult<Box<dyn VFile>> {
let mut tried: Vec<(PathBuf, GameError)> = vec![];
for vfs in &self.roots {
match vfs.open_options(path, open_options) {
Err(e) => {
if let Some(vfs_path) = vfs.to_path_buf() {
tried.push((vfs_path, e));
} else {
tried.push((PathBuf::from("<invalid path>"), e));
}
}
Err(e) => continue,
f => return f,
}
}
let errmessage = String::from(convenient_path_to_str(path)?);
Err(GameError::ResourceNotFound(errmessage, tried))
Err(GameError::ResourceNotFound(errmessage))
}
/// Create a directory at the location by this path
@@ -584,20 +588,6 @@ mod tests {
assert!(sanitize_path(p).is_none());
}
#[test]
fn headless_test_read() {
let cargo_path = Path::new(env!("CARGO_MANIFEST_DIR"));
let fs = PhysicalFS::new(cargo_path, true);
let f = fs.open(Path::new("/Cargo.toml")).unwrap();
let mut bf = io::BufReader::new(f);
let mut s = String::new();
let _ = bf.read_line(&mut s).unwrap();
// Trim whitespace from string 'cause it will
// potentially be different on Windows and Unix.
let trimmed_string = s.trim();
assert_eq!(trimmed_string, "[package]");
}
#[test]
fn headless_test_read_overlay() {
let cargo_path = Path::new(env!("CARGO_MANIFEST_DIR"));
@@ -688,3 +678,37 @@ mod tests {
// BUGGO: TODO: Make sure all functions are tested for OverlayFS and ZipFS!!
}
#[derive(Debug)]
pub struct FSFileWrapper(std::fs::File);
impl Read for FSFileWrapper {
fn read(&mut self, buf: &mut [u8]) -> GameResult<usize> {
use std::io::Read;
self.0.read(buf).map_err(|e| GameError::FilesystemError(e.to_string()))
}
}
impl Write for FSFileWrapper {
fn write(&mut self, buf: &[u8]) -> GameResult<usize> {
use std::io::Write;
self.0.write(buf).map_err(|e| GameError::FilesystemError(e.to_string()))
}
fn flush(&mut self) -> GameResult {
use std::io::Write;
self.0.flush().map_err(|e| GameError::FilesystemError(e.to_string()))
}
}
impl Seek for FSFileWrapper {
fn seek(&mut self, pos: SeekFrom) -> GameResult<u64> {
use std::io::Seek;
let pos = match pos {
SeekFrom::Start(p) => std::io::SeekFrom::Start(p),
SeekFrom::End(p) => std::io::SeekFrom::End(p),
SeekFrom::Current(p) => std::io::SeekFrom::Current(p),
};
self.0.seek(pos).map_err(|e| GameError::FilesystemError(e.to_string()))
}
}
+12 -7
View File
@@ -1,5 +1,7 @@
use std::path::PathBuf;
use drs_framework::error::GameError;
use crate::{
data::builtin_fs::BuiltinFS,
framework::{
@@ -27,7 +29,7 @@ impl FilesystemContainer {
let resource_dir = if let Ok(data_dir) = std::env::var("CAVESTORY_DATA_DIR") {
PathBuf::from(data_dir)
} else {
let mut resource_dir = std::env::current_exe()?;
let mut resource_dir = std::env::current_exe().unwrap();
if resource_dir.file_name().is_some() {
let _ = resource_dir.pop();
}
@@ -170,13 +172,13 @@ impl FilesystemContainer {
let _ = std::fs::create_dir_all(user_dir.clone());
// copy user data from current user dir
for entry in std::fs::read_dir(&self.user_path)? {
let entry = entry?;
for entry in std::fs::read_dir(&self.user_path).map_err(|e| GameError::FilesystemError(e.to_string()))? {
let entry = entry.map_err(|e| GameError::FilesystemError(e.to_string()))?;
let path = entry.path();
let file_name = path.file_name().unwrap().to_str().unwrap();
let mut new_path = user_dir.clone();
new_path.push(file_name);
std::fs::copy(path, new_path)?;
std::fs::copy(path, new_path).map_err(|e| GameError::FilesystemError(e.to_string()))?;
}
// unmount old user dir
@@ -205,9 +207,12 @@ impl FilesystemContainer {
let vm_env = vm.attach_current_thread()?;
let class = vm_env.new_global_ref(JObject::from_raw(ndk_glue::native_activity().activity()))?;
let method = vm_env.call_method(class.as_obj(), "openDir", "(Ljava/lang/String;)V", &[
JValue::from(vm_env.new_string(path.to_str().unwrap()).unwrap())
])?;
let method = vm_env.call_method(
class.as_obj(),
"openDir",
"(Ljava/lang/String;)V",
&[JValue::from(vm_env.new_string(path.to_str().unwrap()).unwrap())],
)?;
return Ok(());
}
+61 -67
View File
@@ -1,9 +1,5 @@
use std::collections::HashMap;
use std::io;
use std::io::{BufRead, BufReader, Read};
use std::sync::Arc;
use byteorder::{ReadBytesExt, LE};
use drs_framework::io::{self, Read};
use crate::common::{Color, Rect};
use crate::framework::context::Context;
@@ -51,8 +47,8 @@ impl Map {
return Err(ResourceLoadError(format!("Unsupported PXM version: {:#x}", version)));
}
let width = map_data.read_u16::<LE>()?;
let height = map_data.read_u16::<LE>()?;
let width = map_data.read_u16_le()?;
let height = map_data.read_u16_le()?;
let mut tiles = vec![0u8; (width * height) as usize];
let mut attrib = [0u8; 0x100];
@@ -106,8 +102,8 @@ impl Map {
skip_string(&mut map_data)?;
skip_string(&mut map_data)?; // spritesheet
map_data.read_u16::<LE>()?;
map_data.read_u16::<LE>()?;
map_data.read_u16_le()?;
map_data.read_u16_le()?;
map_data.read_u8()?;
let bg_color = Color::from_rgb(map_data.read_u8()?, map_data.read_u8()?, map_data.read_u8()?);
@@ -144,8 +140,8 @@ impl Map {
return Err(ResourceLoadError("Invalid magic".to_owned()));
}
let width_fg = map_data.read_u16::<LE>()?;
let height_fg = map_data.read_u16::<LE>()?;
let width_fg = map_data.read_u16_le()?;
let height_fg = map_data.read_u16_le()?;
map_data.read_u8()?;
log::info!("Foreground map size: {}x{}", width_fg, height_fg);
@@ -161,8 +157,8 @@ impl Map {
return Err(ResourceLoadError("Invalid magic".to_owned()));
}
let width_mg = map_data.read_u16::<LE>()?;
let height_mg = map_data.read_u16::<LE>()?;
let width_mg = map_data.read_u16_le()?;
let height_mg = map_data.read_u16_le()?;
log::info!("Middleground map size: {}x{}", width_mg, height_mg);
@@ -179,8 +175,8 @@ impl Map {
return Err(ResourceLoadError("Invalid magic".to_owned()));
}
let width_bg = map_data.read_u16::<LE>()?;
let height_bg = map_data.read_u16::<LE>()?;
let width_bg = map_data.read_u16_le()?;
let height_bg = map_data.read_u16_le()?;
log::info!("Background map size: {}x{}", width_bg, height_bg);
@@ -207,8 +203,8 @@ impl Map {
return Err(ResourceLoadError("Invalid magic".to_owned()));
}
attrib_data.read_u16::<LE>()?;
attrib_data.read_u16::<LE>()?;
attrib_data.read_u16_le()?;
attrib_data.read_u16_le()?;
attrib_data.read_u8()?;
if attrib_data.read_exact(&mut attrib).is_err() {
@@ -489,16 +485,16 @@ impl NPCData {
return Err(ResourceLoadError(format!("Unsupported PXE version: {:#x}", version)));
}
let count = data.read_u32::<LE>()? as usize;
let count = data.read_u32_le()? as usize;
let mut npcs = Vec::with_capacity(count);
for i in 0..count {
let x = data.read_i16::<LE>()?;
let y = data.read_i16::<LE>()?;
let flag_num = data.read_u16::<LE>()?;
let event_num = data.read_u16::<LE>()?;
let npc_type = data.read_u16::<LE>()?;
let flags = data.read_u16::<LE>()?;
let x = data.read_i16_le()?;
let y = data.read_i16_le()?;
let flag_num = data.read_u16_le()?;
let event_num = data.read_u16_le()?;
let npc_type = data.read_u16_le()?;
let flags = data.read_u16_le()?;
// booster's lab also specifies a layer field in version 0x10, prob for multi-layered maps
let layer = if version == 0x10 { data.read_u8()? } else { 0 };
@@ -536,7 +532,7 @@ impl WaterParams {
WaterParams { entries: HashMap::new() }
}
pub fn load_from<R: io::Read>(&mut self, data: R) -> GameResult {
pub fn load_from<R: io::Read>(&mut self, mut file: R) -> GameResult {
fn next_u8<'a>(s: &mut impl Iterator<Item = &'a str>, error_msg: &str) -> GameResult<u8> {
match s.next() {
None => Err(GameError::ParseError("Out of range.".to_string())),
@@ -544,54 +540,52 @@ impl WaterParams {
}
}
for line in BufReader::new(data).lines() {
match line {
Ok(line) => {
let mut splits = line.split(':');
let mut buf = String::new();
file.read_to_string(&mut buf)?;
if splits.clone().count() != 5 {
return Err(GameError::ParseError("Invalid count of delimiters.".to_string()));
}
for line in buf.lines() {
let mut splits = line.split(':');
let tile_min = next_u8(&mut splits, "Invalid minimum tile value.")?;
let tile_max = next_u8(&mut splits, "Invalid maximum tile value.")?;
if splits.clone().count() != 5 {
return Err(GameError::ParseError("Invalid count of delimiters.".to_string()));
}
if tile_min > tile_max {
return Err(GameError::ParseError("tile_min > tile_max".to_string()));
}
let tile_min = next_u8(&mut splits, "Invalid minimum tile value.")?;
let tile_max = next_u8(&mut splits, "Invalid maximum tile value.")?;
let mut read_color = || -> GameResult<Color> {
let cstr = splits.next().unwrap().trim();
if !cstr.starts_with('[') || !cstr.ends_with(']') {
return Err(GameError::ParseError("Invalid format of color value.".to_string()));
}
if tile_min > tile_max {
return Err(GameError::ParseError("tile_min > tile_max".to_string()));
}
let mut csplits = cstr[1..cstr.len() - 1].split(',');
if csplits.clone().count() != 4 {
return Err(GameError::ParseError("Invalid count of delimiters.".to_string()));
}
let r = next_u8(&mut csplits, "Invalid red value.")?;
let g = next_u8(&mut csplits, "Invalid green value.")?;
let b = next_u8(&mut csplits, "Invalid blue value.")?;
let a = next_u8(&mut csplits, "Invalid alpha value.")?;
Ok(Color::from_rgba(r, g, b, a))
};
let color_top = read_color()?;
let color_middle = read_color()?;
let color_bottom = read_color()?;
let entry = WaterParamEntry { color_top, color_middle, color_bottom };
for i in tile_min..=tile_max {
let e = self.entries.entry(i);
e.or_insert(entry);
}
let mut read_color = || -> GameResult<Color> {
let cstr = splits.next().unwrap().trim();
if !cstr.starts_with('[') || !cstr.ends_with(']') {
return Err(GameError::ParseError("Invalid format of color value.".to_string()));
}
Err(e) => return Err(GameError::IOError(Arc::new(e))),
let mut csplits = cstr[1..cstr.len() - 1].split(',');
if csplits.clone().count() != 4 {
return Err(GameError::ParseError("Invalid count of delimiters.".to_string()));
}
let r = next_u8(&mut csplits, "Invalid red value.")?;
let g = next_u8(&mut csplits, "Invalid green value.")?;
let b = next_u8(&mut csplits, "Invalid blue value.")?;
let a = next_u8(&mut csplits, "Invalid alpha value.")?;
Ok(Color::from_rgba(r, g, b, a))
};
let color_top = read_color()?;
let color_middle = read_color()?;
let color_bottom = read_color()?;
let entry = WaterParamEntry { color_top, color_middle, color_bottom };
for i in tile_min..=tile_max {
let e = self.entries.entry(i);
e.or_insert(entry);
}
}

Some files were not shown because too many files have changed in this diff Show More