From 49940b43d531612f56a0a35fbaea635e6fb78700 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 18 Jan 2026 14:56:37 +0800 Subject: [PATCH] write some sonnet code --- BallanceTasSonnet/.github/workflows/CI.yml | 186 +++++++++++++ BallanceTasSonnet/.gitignore | 72 +++++ .../BallanceTasSonnet/__init__.py | 0 BallanceTasSonnet/Cargo.lock | 247 +++++++++++++++++ BallanceTasSonnet/Cargo.toml | 15 ++ BallanceTasSonnet/pyproject.toml | 13 + BallanceTasSonnet/src/lib.rs | 16 ++ BallanceTasSonnet/src/tasfile.rs | 249 ++++++++++++++++++ BallanceTasSonnet/src/utils.rs | 2 + BallanceTasSonnet/src/utils/fps_converter.rs | 27 ++ BallanceTasSonnet/src/utils/zlib.rs | 36 +++ 11 files changed, 863 insertions(+) create mode 100644 BallanceTasSonnet/.github/workflows/CI.yml create mode 100644 BallanceTasSonnet/.gitignore create mode 100644 BallanceTasSonnet/BallanceTasSonnet/__init__.py create mode 100644 BallanceTasSonnet/Cargo.lock create mode 100644 BallanceTasSonnet/Cargo.toml create mode 100644 BallanceTasSonnet/pyproject.toml create mode 100644 BallanceTasSonnet/src/lib.rs create mode 100644 BallanceTasSonnet/src/tasfile.rs create mode 100644 BallanceTasSonnet/src/utils.rs create mode 100644 BallanceTasSonnet/src/utils/fps_converter.rs create mode 100644 BallanceTasSonnet/src/utils/zlib.rs diff --git a/BallanceTasSonnet/.github/workflows/CI.yml b/BallanceTasSonnet/.github/workflows/CI.yml new file mode 100644 index 0000000..c6d8bc5 --- /dev/null +++ b/BallanceTasSonnet/.github/workflows/CI.yml @@ -0,0 +1,186 @@ +# This file is autogenerated by maturin v1.11.5 +# To update, run +# +# maturin generate-ci github +# +name: CI + +on: + push: + branches: + - main + - master + tags: + - '*' + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + linux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + - runner: ubuntu-22.04 + target: s390x + - runner: ubuntu-22.04 + target: ppc64le + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v5 + with: + name: wheels-linux-${{ matrix.platform.target }} + path: dist + + musllinux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + manylinux: musllinux_1_2 + - name: Upload wheels + uses: actions/upload-artifact@v5 + with: + name: wheels-musllinux-${{ matrix.platform.target }} + path: dist + + windows: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: windows-latest + target: x64 + python_arch: x64 + - runner: windows-latest + target: x86 + python_arch: x86 + - runner: windows-11-arm + target: aarch64 + python_arch: arm64 + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.13 + architecture: ${{ matrix.platform.python_arch }} + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + - name: Upload wheels + uses: actions/upload-artifact@v5 + with: + name: wheels-windows-${{ matrix.platform.target }} + path: dist + + macos: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: macos-15-intel + target: x86_64 + - runner: macos-latest + target: aarch64 + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: ${{ !startsWith(github.ref, 'refs/tags/') }} + - name: Upload wheels + uses: actions/upload-artifact@v5 + with: + name: wheels-macos-${{ matrix.platform.target }} + path: dist + + sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v5 + with: + name: wheels-sdist + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} + needs: [linux, musllinux, windows, macos, sdist] + permissions: + # Use to sign the release artifacts + id-token: write + # Used to upload release artifacts + contents: write + # Used to generate artifact attestation + attestations: write + steps: + - uses: actions/download-artifact@v6 + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v3 + with: + subject-path: 'wheels-*/*' + - name: Install uv + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: astral-sh/setup-uv@v7 + - name: Publish to PyPI + if: ${{ startsWith(github.ref, 'refs/tags/') }} + run: uv publish 'wheels-*/*' + env: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} diff --git a/BallanceTasSonnet/.gitignore b/BallanceTasSonnet/.gitignore new file mode 100644 index 0000000..c8f0442 --- /dev/null +++ b/BallanceTasSonnet/.gitignore @@ -0,0 +1,72 @@ +/target + +# Byte-compiled / optimized / DLL files +__pycache__/ +.pytest_cache/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +.venv/ +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +include/ +man/ +venv/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +pip-selfcheck.json + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +.DS_Store + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Pyenv +.python-version diff --git a/BallanceTasSonnet/BallanceTasSonnet/__init__.py b/BallanceTasSonnet/BallanceTasSonnet/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/BallanceTasSonnet/Cargo.lock b/BallanceTasSonnet/Cargo.lock new file mode 100644 index 0000000..ac5dabe --- /dev/null +++ b/BallanceTasSonnet/Cargo.lock @@ -0,0 +1,247 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "BallanceTasSonnet" +version = "0.1.0" +dependencies = [ + "byteorder", + "libz-sys", + "pyo3", + "thiserror", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libz-sys" +version = "1.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "proc-macro2" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d" +dependencies = [ + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6" +dependencies = [ + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba" + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" diff --git a/BallanceTasSonnet/Cargo.toml b/BallanceTasSonnet/Cargo.toml new file mode 100644 index 0000000..e064afc --- /dev/null +++ b/BallanceTasSonnet/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "BallanceTasSonnet" +version = "0.1.0" +edition = "2024" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "BallanceTasSonnet" +crate-type = ["cdylib"] + +[dependencies] +pyo3 = "0.27.0" +thiserror = "2.0.12" +byteorder = "1.5.0" +libz-sys = "1.1.23" diff --git a/BallanceTasSonnet/pyproject.toml b/BallanceTasSonnet/pyproject.toml new file mode 100644 index 0000000..ead64e8 --- /dev/null +++ b/BallanceTasSonnet/pyproject.toml @@ -0,0 +1,13 @@ +[build-system] +requires = ["maturin>=1.11,<2.0"] +build-backend = "maturin" + +[project] +name = "BallanceTasSonnet" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dynamic = ["version"] diff --git a/BallanceTasSonnet/src/lib.rs b/BallanceTasSonnet/src/lib.rs new file mode 100644 index 0000000..ee42f9c --- /dev/null +++ b/BallanceTasSonnet/src/lib.rs @@ -0,0 +1,16 @@ +use pyo3::prelude::*; + +pub(crate) mod utils; +pub(crate) mod tasfile; + +/// A Python module implemented in Rust. +#[pymodule] +mod BallanceTasSonnet { + use pyo3::prelude::*; + + /// Formats the sum of two numbers as string. + #[pyfunction] + fn sum_as_string(a: usize, b: usize) -> PyResult { + Ok((a + b).to_string()) + } +} diff --git a/BallanceTasSonnet/src/tasfile.rs b/BallanceTasSonnet/src/tasfile.rs new file mode 100644 index 0000000..748c799 --- /dev/null +++ b/BallanceTasSonnet/src/tasfile.rs @@ -0,0 +1,249 @@ +use crate::utils::fps_converter; +use thiserror::Error as TeError; + +#[derive(Debug, TeError)] +pub enum Error { + #[error("{0}")] + BadFpsConv(#[from] fps_converter::Error), + + #[error("given index is out of range")] + IndexOutOfRange, + #[error("arithmetic overflow")] + NumOverflow, +} + +type Result = std::result::Result; + +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 { + 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, +} + +impl TasFile { + pub fn new(frames: Vec) -> 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 { + Ok(Self::from_brandnew(count, TasFrame::with_fps(fps)?)) + } + + pub fn load_file(reader: dyn) + + pub fn from_file(file: &str) -> Result { + todo!() + } + + pub fn save(&self, file: &str) -> Result { + 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(()) + } +} diff --git a/BallanceTasSonnet/src/utils.rs b/BallanceTasSonnet/src/utils.rs new file mode 100644 index 0000000..2093228 --- /dev/null +++ b/BallanceTasSonnet/src/utils.rs @@ -0,0 +1,2 @@ +pub(crate) mod fps_converter; +pub(crate) mod zlib; diff --git a/BallanceTasSonnet/src/utils/fps_converter.rs b/BallanceTasSonnet/src/utils/fps_converter.rs new file mode 100644 index 0000000..141cbbb --- /dev/null +++ b/BallanceTasSonnet/src/utils/fps_converter.rs @@ -0,0 +1,27 @@ +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 = std::result::Result; + +pub fn to_fps(delta: f32) -> Result { + if delta <= 0f32 { + Err(Error::BadDeltaTime) + } else { + Ok(1f32 / delta) + } +} + +pub fn to_delta(fps: f32) -> Result { + if fps <= 0f32 { + Err(Error::BadFps) + } else { + Ok(1f32 / fps) + } +} diff --git a/BallanceTasSonnet/src/utils/zlib.rs b/BallanceTasSonnet/src/utils/zlib.rs new file mode 100644 index 0000000..db90077 --- /dev/null +++ b/BallanceTasSonnet/src/utils/zlib.rs @@ -0,0 +1,36 @@ +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 = std::result::Result; + +pub fn compress(writer: &mut dyn Write, frames: &[TasFrame]) -> Result<()> { + // Get decompressed size. + let usize_decomp_size = size_of::().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::(u32_decomp_size)?; + + let buffer: Box<[MaybeUninit]> = Box::new_uninit_slice(usize_decomp_size); + + + Ok(()) +} + +pub fn decompress(reader: &mut dyn Read) -> Result> { + +} +