@r2o3/rgskin-browser
v0.0.1
Published
A library for converting rhythm game skins.
Downloads
94
Maintainers
Readme
rgskin
A library for loading and creating skins for various rhythm games. It supports cross-platform usage including Web and Node.js environments via WebAssembly (WASM).
Table of Contents
Rust Usage
Installation
Add this to your Cargo.toml:
[dependencies]
rgskin = "0.0.1"Or run:
cargo add rgskinAPI Reference
Importing/Loading Skins
Recommended way of Loading a skin
use rgskin::prelude::*;
// importing a skin from a directory
let osu_skin = import::osu::skin_from_dir("path/to/skin").expect("Failed to import skin!");
let fluxis_skin = import::fluxis::skin_from_dir("path/to/skin").expect("Failed to import skin!");Manually loading a skin
use rgskin::prelude::*;
// create a new texture store, this is where the actual textures will be stored
let mut textures = TextureStore::new();
// you can import textures from a directory like this:
textures = import::all_textures_from_dir("path/to/skin")?;
// or alternatively if you parse the skin config first, you can import only the textures you need like this:
let raw_str = import::osu::ini_str_from_dir("path/to/skin");
let skin_config = OsuSkinIni::from_str(&raw_str)?;
// since get_required_texture_paths returns a HashSet, we need to convert it to a Vec<&str> for the import function
let required_texture_paths_set = skin_config.get_required_texture_paths();
let required_texture_paths = required_texture_paths_set.iter().map(|s| s.as_str()).collect::<Vec<_>>();
textures = import::textures_from_dir("path/to/skin", &required_texture_paths)?;
// now you can create a skin from the config and textures
let osu_skin = OsuSkin::new(skin_config, Some(textures), None); // last parameter is the sound samples store, which you can import similarly to texturesCreating Skins
All skins are loaded in their original formats; Any textures go in TextureStore or samples go in SampleStore, etc. The config is also preserved so, this next part will talk about dealing with generic skins as creating skins for a specific game differs from one to another.
Additionally all skins can be converting into a generic version of it.
Examples:
OsuSkin::from_generic_mania(&generic);
OsuSkin.to_generic_mania();FluXisSkin::from_generic_mania(&generic);
FluXisSkin.to_generic_mania(fluxis_layout); // if you don't have a layout you can just pass None or ().Unlike skins from games not all textures are stored in TextureStore. Skin Elements can have their own textures that are shared pointers (Option<Arc<RwLock<Texture>>>). Meaning the texture can be shared anywhere either in a TextureStore or inside a Skin Element.
Exporting Skins
Recommened way of exporting a skin
export::osu::skin_to_dir(&skin, "path/to/export/to")?;
export::fluxis::skin_to_dir(&skin, "path/to/export/to")?;Manually exporting a skin
export::osu::ini_to_dir(skin.skin_ini, "path/to/export/to"); // export::{game}
export::textures_to_dir(skin.textures, "path/to/export/to");
export::samples_to_dir(skin.samples, "path/to/export/to"); // if you have samplesJavaScript/TypeScript Usage
Installation
For Node.js:
npm install @r2o3/rgskin-nodejsFor web projects:
<script src="https://unpkg.com/@r2o3/rgskin-browser@latest/rgskin.js"></script>or
npm install @r2o3/rgskin-browserthen use as an ES module
API Reference
Initialization
// For ES modules
import * as rgskin from '@r2o3/rgskin'; // or if not in node modules use the path to rgskin.js
// or alternatively
const rgskin = await import('path/to/rgskin.js')
// For CommonJS
const rgskin = require('rgskin');you may need to do await rgskin.default() after importing if you've imported it in a script tag (with type="module") or you get an error like Uncaught TypeError: Cannot read properties of undefined (reading '__wbindgen_malloc')
As of now you can't parse/write using the original structures in JS/TS, will be supported in the near future.
Importing/Loading Skins
For Node:
Recommended way of Loading a skin
const OsuSkin = rgskin.osuSkinFromDir("path/to/skin");
const FluXisSkin = rgskin.fluXisSkinFromDir("path/to/skin");Manually loading a skin
// create a new texture store, this is where the actual textures will be stored
let textures = new rgskin.TextureStore();
// you can import textures from a directory like this:
textures = rgskin.allTexturesFromDir("path/to/skin");
// you can parse configs like this:
let raw_str = rgskin.iniStrFromDir("path/to/skin")
let skin_config = rgskin.OsuSkinIni.fromStr(raw_str);
let osuSKin = new rgskin.OsuSkin(skin_config, textures, null) // last parameter is the sound samples store, which you can import similarly to texturesFor Browsers:
Unfortunately you can't automatically import everything using a single function. So you'll have to do a bit of work. Check FilesMap preparation
Recommended way of Loading a skin
const OsuSkin = rgskin.osuSkinFromFiles(filesMap);
const FluXisSkin = rgskin.fluXisSkinFromFiles(filesMap);Manually loading a skin
// create a new texture store, this is where the actual textures will be stored
let textures = new rgskin.TextureStore();
// you can import textures from files like this:
// filesMap is a Map object with relative path -> Uint8Array pairs
textures = rgskin.allTexturesFromFiles(filesMap);
// you can parse configs like this:
// assuming you have the skin.ini file in your filesMap
let raw_str = filesMap.get("skin.ini"); // get the Uint8Array
let decoder = new TextDecoder();
let ini_string = decoder.decode(raw_str);
let skin_config = rgskin.OsuSkinIni.fromStr(ini_string);
let osuSkin = new rgskin.OsuSkin(skin_config, textures, null); // last parameter is the sound samples store, which you can import similarly to texturesPreparing the filesMap
The filesMap parameter is a JavaScript Map object where:
- Keys are filenames (strings)
- Values are file contents as
Uint8Array
Example: Reading files from an HTML file input
const filesMap = new Map();
// assuming you have an <input type="file" multiple webkitdirectory> element
fileInput.addEventListener('change', async (event) => {
const files = event.target.files;
for (const file of files) {
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const relativePath = file.webkitRelativePath || file.name;
filesMap.set(relativePath, uint8Array);
}
// now you can import the skin
const skin = rgskin.osuSkinFromFiles(filesMap);
});Creating Skins
Check Rust's Creating Skins for more details
OsuSkin.fromGenericMania(&generic);
OsuSkin.toGenericMania();FluXisSkin.fromGenericMania(&generic);
FluXisSkin.toGenericMania(fluxis_layout); // if you don't have a layout you can just not pass anything or null.Exporting Skins
For Node:
Recommended way of exporting a skin
rgskin.osuSkinToDir(skin, "path/to/export/to");
rgskin.fluXisSkinToDir(skin, "path/to/export/to");Manually exporting a skin
rgskin.iniToDir(skin.skin_ini, "path/to/export/to");
rgskin.texturesToDir(skin.textures, "path/to/export/to");
rgskin.samplesToDir(skin.samples, "path/to/export/to"); // if you have samplesFor Browsers:
Recommended way of exporting a skin
// Returns a JavaScript Map object with relative path -> Uint8Array pairs
const filesMap = rgskin.osuSkinToFiles(skin);
const filesMap = rgskin.fluXisSkinToFiles(skin);Manually exporting a skin
const iniString = rgskin.iniToString(skin.skin_ini);
const texturesMap = rgskin.texturesToFiles(skin.textures);
const samplesMap = rgskin.samplesToFiles(skin.samples);
// combine them into a single Map if needed
const filesMap = new Map([
['skin.ini', new TextEncoder().encode(iniString)],
...texturesMap,
...samplesMap
]);Actually Exporting/Downloading the files will depend on your implementation.
Example using JSZip:
const filesMap = rgskin.osuSkinToFiles(skin);
const zip = new JSZip();
filesMap.forEach((data, path) => {
zip.file(path, data);
});
const zipBlob = await zip.generateAsync({ type: 'blob' });
const link = document.createElement('a');
link.href = URL.createObjectURL(zipBlob);
link.download = 'skin.zip';
link.click();Example Downloading each file indiviually:
const filesMap = rgskin.osuSkinToFiles(skin);
filesMap.forEach((data, path) => {
const blob = new Blob([data]);
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = path;
link.click();
});Building
Rust Library
cargo buildWASM Bindings
- Install wasm-pack:
cargo install wasm-pack[!IMPORTANT]
It's really recommended to have wasm-opt installed and added to path for the wasm build.
- Build the package:
npm run build # debug build
npm run build-release # release build- This will build it for both node and browser and the output will be in
dist-webanddist-nodedirectory.
License
r2o3 uses the MIT License for all its sibiling projects. See LICENSE for more information
