feat: finish sonnet tas file rw functions
This commit is contained in:
2
BallanceTasSonnet/Cargo.lock
generated
2
BallanceTasSonnet/Cargo.lock
generated
@@ -4,7 +4,7 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "BallanceTasSonnet"
|
name = "BallanceTasSonnet"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"libz-sys",
|
"libz-sys",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "BallanceTasSonnet"
|
name = "BallanceTasSonnet"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[lib]
|
[lib]
|
||||||
name = "BallanceTasSonnet"
|
name = "blctas"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
pub(crate) mod utils;
|
pub(crate) mod wrapped;
|
||||||
pub(crate) mod tasfile;
|
|
||||||
|
|
||||||
/// A Python module implemented in Rust.
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
mod BallanceTasSonnet {
|
mod tasfile {
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
/// Formats the sum of two numbers as string.
|
/// Formats the sum of two numbers as string.
|
||||||
|
|||||||
@@ -1,249 +1,7 @@
|
|||||||
use crate::utils::fps_converter;
|
use pyo3::prelude::*;
|
||||||
use thiserror::Error as TeError;
|
|
||||||
|
|
||||||
#[derive(Debug, TeError)]
|
/// Formats the sum of two numbers as string.
|
||||||
pub enum Error {
|
#[pyfunction]
|
||||||
#[error("{0}")]
|
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
|
||||||
BadFpsConv(#[from] fps_converter::Error),
|
Ok((a + b).to_string())
|
||||||
|
|
||||||
#[error("given index is out of range")]
|
|
||||||
IndexOutOfRange,
|
|
||||||
#[error("arithmetic overflow")]
|
|
||||||
NumOverflow,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub enum TasKey {
|
|
||||||
KeyUp,
|
|
||||||
KeyDown,
|
|
||||||
KeyLeft,
|
|
||||||
KeyRight,
|
|
||||||
KeyShift,
|
|
||||||
KeySpace,
|
|
||||||
KeyQ,
|
|
||||||
KeyEsc,
|
|
||||||
KeyEnter,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TasFrame {
|
|
||||||
delta_time: f32,
|
|
||||||
key_flags: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TasFrame {
|
|
||||||
pub fn new(delta_time: f32, key_flags: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
delta_time,
|
|
||||||
key_flags,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_fps(fps: f32) -> Result<Self> {
|
|
||||||
Ok(Self::new(fps_converter::to_delta(fps)?, 0u32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TasFrame {
|
|
||||||
pub fn get_delta_time(&self) -> f32 {
|
|
||||||
self.delta_time
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_delta_time(&mut self, delta_time: f32) {
|
|
||||||
self.delta_time = delta_time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TasFrame {
|
|
||||||
fn get_key_flag(key: TasKey) -> u32 {
|
|
||||||
let bit = match key {
|
|
||||||
TasKey::KeyUp => 0,
|
|
||||||
TasKey::KeyDown => 1,
|
|
||||||
TasKey::KeyLeft => 2,
|
|
||||||
TasKey::KeyRight => 3,
|
|
||||||
TasKey::KeyShift => 4,
|
|
||||||
TasKey::KeySpace => 5,
|
|
||||||
TasKey::KeyQ => 6,
|
|
||||||
TasKey::KeyEsc => 7,
|
|
||||||
TasKey::KeyEnter => 8,
|
|
||||||
};
|
|
||||||
1u32 << bit
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_key_pressed(&self, key: TasKey) -> bool {
|
|
||||||
(self.key_flags & Self::get_key_flag(key)) != 0u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_key_pressed(&mut self, key: TasKey, pressed: bool) {
|
|
||||||
if pressed {
|
|
||||||
self.key_flags |= Self::get_key_flag(key)
|
|
||||||
} else {
|
|
||||||
self.key_flags &= !(Self::get_key_flag(key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn flip_key_pressed(&mut self, key: TasKey) {
|
|
||||||
self.key_flags ^= Self::get_key_flag(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_key_up_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyUp)
|
|
||||||
}
|
|
||||||
pub fn set_key_up_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyUp, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_down_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyDown)
|
|
||||||
}
|
|
||||||
pub fn set_key_down_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyDown, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_left_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyLeft)
|
|
||||||
}
|
|
||||||
pub fn set_key_left_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyLeft, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_right_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyRight)
|
|
||||||
}
|
|
||||||
pub fn set_key_right_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyRight, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_shift_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyShift)
|
|
||||||
}
|
|
||||||
pub fn set_key_shift_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyShift, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_space_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeySpace)
|
|
||||||
}
|
|
||||||
pub fn set_key_space_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeySpace, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_q_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyQ)
|
|
||||||
}
|
|
||||||
pub fn set_key_q_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyQ, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_esc_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyEsc)
|
|
||||||
}
|
|
||||||
pub fn set_key_esc_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyEsc, pressed)
|
|
||||||
}
|
|
||||||
pub fn get_key_enter_pressed(&self) -> bool {
|
|
||||||
self.is_key_pressed(TasKey::KeyEnter)
|
|
||||||
}
|
|
||||||
pub fn set_key_enter_pressed(&mut self, pressed: bool) {
|
|
||||||
self.set_key_pressed(TasKey::KeyEnter, pressed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TasFile {
|
|
||||||
frames: Vec<TasFrame>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TasFile {
|
|
||||||
pub fn new(frames: Vec<TasFrame>) -> Self {
|
|
||||||
Self { frames }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_brandnew(count: usize, frame: TasFrame) -> Self {
|
|
||||||
Self::new(vec![frame; count])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_brandnew_with_fps(count: usize, fps: f32) -> Result<Self> {
|
|
||||||
Ok(Self::from_brandnew(count, TasFrame::with_fps(fps)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_file(reader: dyn)
|
|
||||||
|
|
||||||
pub fn from_file(file: &str) -> Result<Self> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save(&self, file: &str) -> Result<Self> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TasFile {
|
|
||||||
/// 清空存储结构。
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.frames.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取当前存储的TAS帧的个数。
|
|
||||||
pub fn get_count(&self) -> usize {
|
|
||||||
self.frames.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取当前存储结构是不是空的。
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.frames.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TasFile {
|
|
||||||
/// 访问给定索引的帧。
|
|
||||||
pub fn visit<'a>(&'a self, index: usize) -> Result<&'a TasFrame> {
|
|
||||||
self.frames.get(index).ok_or(Error::IndexOutOfRange)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 以可变形式访问给定索引的值。
|
|
||||||
pub fn visit_mut<'a>(&'a mut self, index: usize) -> Result<&'a mut TasFrame> {
|
|
||||||
self.frames.get_mut(index).ok_or(Error::IndexOutOfRange)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在给定的索引**之前**插入给定的项目。
|
|
||||||
///
|
|
||||||
/// 按照此函数约定,如果要在头部插入数据,则可以通过指定0来实现。
|
|
||||||
/// 然而对于在尾部插入数据,或在空的存储中插入数据,可以指定存储结构的长度来实现。
|
|
||||||
/// 即指定最大Index + 1的值来实现。
|
|
||||||
///
|
|
||||||
/// `index`为要在前方插入数据的元素的索引。`frames`为要插入的元素的切片。
|
|
||||||
pub fn insert(&mut self, index: usize, frames: &[TasFrame]) -> Result<()> {
|
|
||||||
if index > self.frames.len() {
|
|
||||||
Err(Error::IndexOutOfRange)
|
|
||||||
} else if index == self.frames.len() {
|
|
||||||
// Insert at tail
|
|
||||||
self.frames.extend_from_slice(frames);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
// Insert at middle or head
|
|
||||||
self.frames.splice(index..index, frames.iter().copied());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 从给定单元开始,移除给定个数的元素。
|
|
||||||
///
|
|
||||||
/// `index`为要开始移除的单元的索引。`count`为要移除的元素的个数。
|
|
||||||
pub fn remove(&mut self, index: usize, count: usize) -> Result<()> {
|
|
||||||
// Check index
|
|
||||||
let index_from = index;
|
|
||||||
if index_from >= self.frames.len() {
|
|
||||||
return Err(Error::IndexOutOfRange);
|
|
||||||
}
|
|
||||||
// Check count
|
|
||||||
let count = if count == 0 {
|
|
||||||
// Count == 0 may cause "..=" buggy, so we return first.
|
|
||||||
return Ok(());
|
|
||||||
} else {
|
|
||||||
count - 1
|
|
||||||
};
|
|
||||||
let index_to = index.checked_add(count).ok_or(Error::NumOverflow)?;
|
|
||||||
if index_to >= self.frames.len() {
|
|
||||||
return Err(Error::IndexOutOfRange);
|
|
||||||
}
|
|
||||||
// Perform remove
|
|
||||||
self.frames.drain(index_from..=index_to);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
pub(crate) mod fps_converter;
|
|
||||||
pub(crate) mod zlib;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
use thiserror::Error as TeError;
|
|
||||||
|
|
||||||
#[derive(Debug, TeError)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("delta time should not be zero or negative value")]
|
|
||||||
BadDeltaTime,
|
|
||||||
#[error("fps should should not be zero or negative value")]
|
|
||||||
BadFps,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub fn to_fps(delta: f32) -> Result<f32> {
|
|
||||||
if delta <= 0f32 {
|
|
||||||
Err(Error::BadDeltaTime)
|
|
||||||
} else {
|
|
||||||
Ok(1f32 / delta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_delta(fps: f32) -> Result<f32> {
|
|
||||||
if fps <= 0f32 {
|
|
||||||
Err(Error::BadFps)
|
|
||||||
} else {
|
|
||||||
Ok(1f32 / fps)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use std::{io::{Read, Write}, mem::MaybeUninit};
|
|
||||||
use libz_sys;
|
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt, NativeEndian};
|
|
||||||
use thiserror::Error as TeError;
|
|
||||||
use crate::tasfile::TasFrame;
|
|
||||||
|
|
||||||
#[derive(Debug, TeError)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("fail to read or write file")]
|
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
#[error("arithmetic overflow")]
|
|
||||||
NumOverflow,
|
|
||||||
#[error("fail to cast numeric value")]
|
|
||||||
BadNumCast,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub fn compress(writer: &mut dyn Write, frames: &[TasFrame]) -> Result<()> {
|
|
||||||
// Get decompressed size.
|
|
||||||
let usize_decomp_size = size_of::<TasFrame>().checked_mul(frames.len()).ok_or(Error::NumOverflow)?;
|
|
||||||
|
|
||||||
// Write decompressed size.
|
|
||||||
let u32_decomp_size = u32::try_from(usize_decomp_size).map_err(|e| Error::BadNumCast)?;
|
|
||||||
writer.write_u32::<NativeEndian>(u32_decomp_size)?;
|
|
||||||
|
|
||||||
let buffer: Box<[MaybeUninit<u8>]> = Box::new_uninit_slice(usize_decomp_size);
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decompress(reader: &mut dyn Read) -> Result<Vec<TasFrame>> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
1
BallanceTasSonnet/src/wrapped.rs
Normal file
1
BallanceTasSonnet/src/wrapped.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod tasfile;
|
||||||
374
BallanceTasSonnet/src/wrapped/tasfile.rs
Normal file
374
BallanceTasSonnet/src/wrapped/tasfile.rs
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use libz_sys;
|
||||||
|
use std::ffi::{c_int, c_ulong};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use thiserror::Error as TeError;
|
||||||
|
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("delta time should not be zero or negative value")]
|
||||||
|
BadDeltaTime,
|
||||||
|
#[error("fps should should not be zero or negative value")]
|
||||||
|
BadFps,
|
||||||
|
|
||||||
|
#[error("given index is out of range")]
|
||||||
|
IndexOutOfRange,
|
||||||
|
#[error("arithmetic overflow")]
|
||||||
|
NumOverflow,
|
||||||
|
#[error("fail to cast numeric value")]
|
||||||
|
BadNumCast,
|
||||||
|
|
||||||
|
#[error("fail to read or write file")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
#[error("fail to call zlib function")]
|
||||||
|
ZlibCall,
|
||||||
|
#[error("given TAS file is wrong")]
|
||||||
|
BadTasFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum TasKey {
|
||||||
|
KeyUp,
|
||||||
|
KeyDown,
|
||||||
|
KeyLeft,
|
||||||
|
KeyRight,
|
||||||
|
KeyShift,
|
||||||
|
KeySpace,
|
||||||
|
KeyQ,
|
||||||
|
KeyEsc,
|
||||||
|
KeyEnter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TasFrame {
|
||||||
|
delta_time: f32,
|
||||||
|
key_flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFrame {
|
||||||
|
pub fn new(delta_time: f32, key_flags: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
delta_time,
|
||||||
|
key_flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_fps(fps: f32) -> Result<Self> {
|
||||||
|
Ok(Self::new(Self::to_delta_time(fps)?, 0u32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFrame {
|
||||||
|
fn to_fps(delta_time: f32) -> Result<f32> {
|
||||||
|
if delta_time <= 0f32 {
|
||||||
|
Err(Error::BadDeltaTime)
|
||||||
|
} else {
|
||||||
|
Ok(1f32 / delta_time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_delta_time(fps: f32) -> Result<f32> {
|
||||||
|
if fps <= 0f32 {
|
||||||
|
Err(Error::BadFps)
|
||||||
|
} else {
|
||||||
|
Ok(1f32 / fps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_delta_time(&self) -> f32 {
|
||||||
|
self.delta_time
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fps(&self) -> Result<f32> {
|
||||||
|
Self::to_fps(self.get_delta_time())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_delta_time(&mut self, delta_time: f32) -> () {
|
||||||
|
self.delta_time = delta_time
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fps(&mut self, fps: f32) -> Result<()> {
|
||||||
|
Ok(self.set_delta_time(Self::to_delta_time(fps)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFrame {
|
||||||
|
fn get_key_flag(key: TasKey) -> u32 {
|
||||||
|
let bit = match key {
|
||||||
|
TasKey::KeyUp => 0,
|
||||||
|
TasKey::KeyDown => 1,
|
||||||
|
TasKey::KeyLeft => 2,
|
||||||
|
TasKey::KeyRight => 3,
|
||||||
|
TasKey::KeyShift => 4,
|
||||||
|
TasKey::KeySpace => 5,
|
||||||
|
TasKey::KeyQ => 6,
|
||||||
|
TasKey::KeyEsc => 7,
|
||||||
|
TasKey::KeyEnter => 8,
|
||||||
|
};
|
||||||
|
1u32 << bit
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_key_pressed(&self, key: TasKey) -> bool {
|
||||||
|
(self.key_flags & Self::get_key_flag(key)) != 0u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_key_pressed(&mut self, key: TasKey, pressed: bool) {
|
||||||
|
if pressed {
|
||||||
|
self.key_flags |= Self::get_key_flag(key)
|
||||||
|
} else {
|
||||||
|
self.key_flags &= !(Self::get_key_flag(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flip_key_pressed(&mut self, key: TasKey) {
|
||||||
|
self.key_flags ^= Self::get_key_flag(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_key_pressed(&mut self) {
|
||||||
|
self.key_flags = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TasFile {
|
||||||
|
frames: Vec<TasFrame>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFile {
|
||||||
|
pub fn new(frames: Vec<TasFrame>) -> Self {
|
||||||
|
Self { frames }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_brandnew(count: usize, frame: TasFrame) -> Self {
|
||||||
|
Self::new(vec![frame; count])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_brandnew_with_fps(count: usize, fps: f32) -> Result<Self> {
|
||||||
|
Ok(Self::from_brandnew(count, TasFrame::with_fps(fps)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(filename: &str) -> Result<Self> {
|
||||||
|
// Open file
|
||||||
|
let mut reader = File::open(filename)?;
|
||||||
|
|
||||||
|
// Read decompressed size.
|
||||||
|
let u32_decomp_size = reader.read_u32::<NativeEndian>()?;
|
||||||
|
let usize_decomp_size = usize::try_from(u32_decomp_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
let culong_decomp_size =
|
||||||
|
c_ulong::try_from(usize_decomp_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
|
||||||
|
// Check size and compute frame count.
|
||||||
|
let frame_size = size_of::<TasFrame>();
|
||||||
|
let frame_count = usize_decomp_size
|
||||||
|
.checked_div(frame_size)
|
||||||
|
.ok_or(Error::NumOverflow)?;
|
||||||
|
if !usize_decomp_size.is_multiple_of(frame_size) {
|
||||||
|
return Err(Error::BadTasFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all rest file into memory
|
||||||
|
let mut comp_buffer = Vec::new();
|
||||||
|
reader.read_to_end(&mut comp_buffer)?;
|
||||||
|
// Get compressed buffer size.
|
||||||
|
let usize_comp_size = comp_buffer.len();
|
||||||
|
let culong_comp_size = c_ulong::try_from(usize_comp_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
|
||||||
|
// Create decompressed buffer with uninitialized memory
|
||||||
|
let mut decomp_buffer: Box<[MaybeUninit<TasFrame>]> = Box::new_uninit_slice(frame_count);
|
||||||
|
|
||||||
|
// Decompress data
|
||||||
|
let source = comp_buffer.as_ptr() as *const libz_sys::Bytef;
|
||||||
|
let source_len = culong_comp_size;
|
||||||
|
let dest = decomp_buffer.as_mut_ptr() as *mut libz_sys::Bytef;
|
||||||
|
let mut dest_len = culong_decomp_size;
|
||||||
|
let rv = unsafe { libz_sys::uncompress(dest, &mut dest_len, source, source_len) };
|
||||||
|
if rv != libz_sys::Z_OK {
|
||||||
|
return Err(Error::ZlibCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert uninitialized buffer to initialized
|
||||||
|
// SAFETY: We've just initialized the buffer with uncompress
|
||||||
|
let frames = unsafe {
|
||||||
|
std::slice::from_raw_parts(decomp_buffer.as_ptr() as *const TasFrame, frame_count)
|
||||||
|
.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Okey
|
||||||
|
Ok(Self::new(frames))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self, filename: &str) -> Result<()> {
|
||||||
|
// Open file
|
||||||
|
let mut writer = File::create(filename)?;
|
||||||
|
|
||||||
|
// Get decompressed size.
|
||||||
|
let usize_decomp_size = size_of::<TasFrame>()
|
||||||
|
.checked_mul(self.frames.len())
|
||||||
|
.ok_or(Error::NumOverflow)?;
|
||||||
|
|
||||||
|
// Write decompressed size.
|
||||||
|
let u32_decomp_size = u32::try_from(usize_decomp_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
writer.write_u32::<NativeEndian>(u32_decomp_size)?;
|
||||||
|
|
||||||
|
// Get compressed buffer boundary
|
||||||
|
let culong_decomp_size =
|
||||||
|
c_ulong::try_from(usize_decomp_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
let culong_comp_bound_size: c_ulong =
|
||||||
|
unsafe { libz_sys::compressBound(culong_decomp_size) };
|
||||||
|
|
||||||
|
// Create buffer for it.
|
||||||
|
let usize_comp_bound_size =
|
||||||
|
usize::try_from(culong_comp_bound_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
let mut buffer: Box<[MaybeUninit<u8>]> = Box::new_uninit_slice(usize_comp_bound_size);
|
||||||
|
|
||||||
|
// Compress data into buffer
|
||||||
|
let source = self.frames.as_ptr() as *const libz_sys::Bytef;
|
||||||
|
let source_len: c_ulong = culong_decomp_size;
|
||||||
|
let dest = buffer.as_mut_ptr() as *mut libz_sys::Bytef;
|
||||||
|
let mut dest_len: c_ulong = culong_comp_bound_size;
|
||||||
|
let level: c_int = 9;
|
||||||
|
let rv = unsafe { libz_sys::compress2(dest, &mut dest_len, source, source_len, level) };
|
||||||
|
if rv != libz_sys::Z_OK {
|
||||||
|
return Err(Error::ZlibCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the final compressed length.
|
||||||
|
let culong_comp_size: c_ulong = dest_len;
|
||||||
|
let usize_comp_size: usize =
|
||||||
|
usize::try_from(culong_comp_size).map_err(|_| Error::BadNumCast)?;
|
||||||
|
|
||||||
|
// Write compressed data
|
||||||
|
let buffer = unsafe { buffer.assume_init() };
|
||||||
|
writer.write(&buffer[..usize_comp_size])?;
|
||||||
|
|
||||||
|
// Okey
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFile {
|
||||||
|
/// 清空存储结构。
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.frames.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取当前存储的TAS帧的个数。
|
||||||
|
pub fn get_count(&self) -> usize {
|
||||||
|
self.frames.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取当前存储结构是不是空的。
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.frames.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFile {
|
||||||
|
fn check_index(&self, index: usize) -> bool {
|
||||||
|
index < self.frames.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_index_range(&self, index_from: usize, index_to: usize) -> bool {
|
||||||
|
// Check index relation
|
||||||
|
if index_to < index_from {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Check index range
|
||||||
|
if index_to >= self.frames.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 访问给定索引的帧。
|
||||||
|
pub fn visit<'a>(&'a self, index: usize) -> Result<&'a TasFrame> {
|
||||||
|
if self.check_index(index) {
|
||||||
|
Ok(&self.frames[index])
|
||||||
|
} else {
|
||||||
|
Err(Error::IndexOutOfRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 以可变形式访问给定索引的值。
|
||||||
|
pub fn visit_mut<'a>(&'a mut self, index: usize) -> Result<&'a mut TasFrame> {
|
||||||
|
if self.check_index(index) {
|
||||||
|
Ok(&mut self.frames[index])
|
||||||
|
} else {
|
||||||
|
Err(Error::IndexOutOfRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 访问给定索引范围内的帧。
|
||||||
|
pub fn batchly_visit<'a>(&'a self, index_from: usize, index_to: usize) -> Result<&'a [TasFrame]> {
|
||||||
|
if self.check_index_range(index_from, index_to) {
|
||||||
|
Ok(&self.frames[index_from..=index_to])
|
||||||
|
} else {
|
||||||
|
Err(Error::IndexOutOfRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 以可变形式访问给定索引范围内的帧。
|
||||||
|
pub fn batchly_visit_mut<'a>(&'a mut self, index_from: usize, index_to: usize) -> Result<&'a mut[TasFrame]> {
|
||||||
|
if self.check_index_range(index_from, index_to) {
|
||||||
|
Ok(&mut self.frames[index_from..=index_to])
|
||||||
|
} else {
|
||||||
|
Err(Error::IndexOutOfRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TasFile {
|
||||||
|
/// 在结尾继续添加给的的帧序列。
|
||||||
|
///
|
||||||
|
/// `frames`为要插入的元素的切片。
|
||||||
|
pub fn append(&mut self, frames: &[TasFrame]) {
|
||||||
|
self.frames.extend_from_slice(frames)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 在给定的索引**之前**插入给定的帧序列。
|
||||||
|
///
|
||||||
|
/// 按照此函数约定,如果要在头部插入数据,则可以通过指定0来实现。
|
||||||
|
/// 然而对于在尾部插入数据,或在空的存储中插入数据,可以指定存储结构的长度来实现。
|
||||||
|
/// 即指定最大Index + 1的值来实现。
|
||||||
|
///
|
||||||
|
/// `index`为要在前方插入数据的元素的索引。`frames`为要插入的元素的切片。
|
||||||
|
pub fn insert(&mut self, index: usize, frames: &[TasFrame]) -> Result<()> {
|
||||||
|
if index > self.frames.len() {
|
||||||
|
Err(Error::IndexOutOfRange)
|
||||||
|
} else if index == self.frames.len() {
|
||||||
|
// Insert at tail
|
||||||
|
self.frames.extend_from_slice(frames);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// Insert at middle or head
|
||||||
|
self.frames.splice(index..index, frames.iter().copied());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将给定范围内的帧移除。
|
||||||
|
///
|
||||||
|
/// `index_from`为要开始移除的单元的索引。`index_to`为最后一个要移除的单元的索引。
|
||||||
|
/// `index_from`和`index_to`指向的帧均会被删除(端点inclusive模式)。
|
||||||
|
/// `index_to`不能小于`index_from`。
|
||||||
|
/// `index_from`和`index_to`均不能超过最大索引。
|
||||||
|
pub fn remove(&mut self, index_from: usize, index_to: usize) -> Result<()> {
|
||||||
|
// Check index relation
|
||||||
|
if index_to < index_from {
|
||||||
|
return Err(Error::IndexOutOfRange);
|
||||||
|
}
|
||||||
|
// Check index range
|
||||||
|
if index_to >= self.frames.len() {
|
||||||
|
return Err(Error::IndexOutOfRange);
|
||||||
|
}
|
||||||
|
// Perform remove
|
||||||
|
self.frames.drain(index_from..=index_to);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user