mirror of
https://github.com/doukutsu-rs/doukutsu-rs.git
synced 2026-08-24 13:02:49 -07:00
remove most uses of std::io
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
ColumnLimit: '120'
|
||||
FixNamespaceComments: 'true'
|
||||
IndentWidth: '4'
|
||||
Language: Cpp
|
||||
PointerAlignment: Left
|
||||
TabWidth: '4'
|
||||
UseTab: ForIndentation
|
||||
+2
-1
@@ -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"] }
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod error;
|
||||
pub mod filesystem;
|
||||
pub mod io;
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user