├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── src ├── BaseBuffer.ts ├── OsuReader.ts ├── OsuWriter.ts └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # JetBrains products 61 | .idea 62 | 63 | dist/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dillon Modine-Thuen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # osu-buffer [![npm version](https://badge.fury.io/js/osu-buffer.svg)](https://badge.fury.io/js/osu-buffer) [![CodeFactor](https://www.codefactor.io/repository/github/itsyuka/osu-buffer/badge)](https://www.codefactor.io/repository/github/itsyuka/osu-buffer) 2 | 3 | This package allows you to create a buffer that allows the readability of the packets or files of the game called osu! 4 | 5 | This does not automagically convert it, you do need to put some work to make it possible. 6 | 7 | ## How to use 8 | #### Require the package 9 | ```ecmascript 6 10 | const { OsuReader, OsuWriter } = require('osu-buffer') 11 | ``` 12 | 13 | #### Creating a new OsuReader from Node buffer 14 | ```ecmascript 6 15 | let buffer = Buffer.from([]); 16 | let reader = new OsuReader(buffer.buffer); 17 | ``` 18 | 19 | #### Creating a new OsuReader from String 20 | ```ecmascript 6 21 | let reader = OsuReader.fromString(""); 22 | ``` 23 | 24 | #### Creating a new OsuWriter 25 | ```ecmascript 6 26 | let write = new OsuWriter(); // Can be empty or ArrayBuffer 27 | ``` 28 | 29 | ## License 30 | MIT License 31 | 32 | Copyright (c) 2021 Dillon Modine-Thuen 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all 42 | copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 50 | SOFTWARE. 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osu-buffer", 3 | "version": "2.0.2", 4 | "description": "Allows you to read and write packets/files for the game osu!", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "prepare" : "npm run build" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/Itsyuka/osu-buffer.git" 15 | }, 16 | "author": "Dillon (Itsyuka)", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/Itsyuka/osu-buffer/issues" 20 | }, 21 | "homepage": "https://github.com/Itsyuka/osu-buffer#readme", 22 | "devDependencies": { 23 | "typescript": "^4.2.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/BaseBuffer.ts: -------------------------------------------------------------------------------- 1 | export class BaseBuffer { 2 | protected _buff: ArrayBuffer = new ArrayBuffer(8192); 3 | protected _position: number = 0; 4 | protected _length: number = 0; 5 | 6 | constructor(data?: ArrayBuffer) { 7 | if (data) { 8 | this._buff = data; 9 | this._length = data.byteLength; 10 | } 11 | } 12 | 13 | public get buff() { 14 | return this._buff; 15 | } 16 | 17 | public get position() { 18 | return this._position; 19 | } 20 | 21 | public get length() { 22 | return this._length; 23 | } 24 | 25 | public canRead(length: number): boolean { 26 | return length + this._position < this._buff.byteLength; 27 | } 28 | 29 | public atEnd(): boolean { 30 | return this._position >= this._buff.byteLength; 31 | } 32 | 33 | public slice(length: number, position: number = -1): ArrayBuffer { 34 | if (position > 0) { 35 | return this._buff.slice(position, position + length); 36 | } 37 | this._position += length; 38 | return this._buff.slice(this._position - length, this._position); 39 | } 40 | 41 | public toArrayBuffer() { 42 | return this._buff.slice(0, this._length); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/OsuReader.ts: -------------------------------------------------------------------------------- 1 | import { BaseBuffer } from "./BaseBuffer"; 2 | 3 | export class OsuReader extends BaseBuffer { 4 | public readUint8(): number { 5 | const dataView = new DataView(this._buff); 6 | const data = dataView.getUint8(this._position); 7 | this._position += 1; 8 | return data; 9 | } 10 | 11 | public readInt8(): number { 12 | const dataView = new DataView(this._buff); 13 | const data = dataView.getInt8(this._position); 14 | this._position += 1; 15 | return data; 16 | } 17 | 18 | public readUint16(): number { 19 | const dataView = new DataView(this._buff); 20 | const data = dataView.getUint16(this._position, true); 21 | this._position += 2; 22 | return data; 23 | } 24 | 25 | public readInt16(): number { 26 | const dataView = new DataView(this._buff); 27 | const data = dataView.getInt16(this._position, true); 28 | this._position += 2; 29 | return data; 30 | } 31 | 32 | public readUint32(): number { 33 | const dataView = new DataView(this._buff); 34 | const data = dataView.getUint32(this._position, true); 35 | this._position += 4; 36 | return data; 37 | } 38 | 39 | public readInt32(): number { 40 | const dataView = new DataView(this._buff); 41 | const data = dataView.getInt32(this._position, true); 42 | this._position += 4; 43 | return data; 44 | } 45 | 46 | public readUint64(): bigint { 47 | const dataView = new DataView(this._buff); 48 | const data = dataView.getBigUint64(this._position, true); 49 | this._position += 8; 50 | return data; 51 | } 52 | 53 | public readInt64(): bigint { 54 | const dataView = new DataView(this._buff); 55 | const data = dataView.getBigInt64(this._position, true); 56 | this._position += 8; 57 | return data; 58 | } 59 | 60 | public read7bitInt(): number { 61 | let total = 0; 62 | let shift = 0; 63 | let byte = this.readUint8(); 64 | if ((byte & 0x80) === 0) { 65 | total |= (byte & 0x7f) << shift; 66 | } else { 67 | let end = false; 68 | do { 69 | if (shift) { 70 | byte = this.readUint8(); 71 | } 72 | total |= (byte & 0x7f) << shift; 73 | if ((byte & 0x80) === 0) end = true; 74 | shift += 7; 75 | } while (!end); 76 | } 77 | 78 | return total; 79 | } 80 | 81 | public readString(): string | null { 82 | if (this.readUint8() === 0) { 83 | return null; 84 | } 85 | 86 | const length = this.read7bitInt(); 87 | const buffView = new Uint8Array(this._buff, this._position, length); 88 | let value = ""; 89 | for (const item of buffView) { 90 | value += String.fromCharCode(item); 91 | } 92 | 93 | this._position += length; 94 | this._length += length; 95 | 96 | return value; 97 | } 98 | 99 | public readFloat(): number { 100 | const dataView = new DataView(this._buff); 101 | const data = dataView.getFloat32(this._position, true); 102 | this._position += 4; 103 | return data; 104 | } 105 | 106 | public readDouble(): number { 107 | const dataView = new DataView(this._buff); 108 | const data = dataView.getFloat64(this._position, true); 109 | this._position += 8; 110 | return data; 111 | } 112 | 113 | public readBoolean(): boolean { 114 | return Boolean(this.readUint8()); 115 | } 116 | 117 | public readBytes(length: number) { 118 | let arr = []; 119 | for (let i = 0; i < length; i++) { 120 | arr[i] = this.readUint8(); 121 | } 122 | return arr; 123 | } 124 | 125 | public readInt32Array(): number[] { 126 | const length = this.readInt16(); 127 | let arr = []; 128 | for (let i = 0; i < length; i++) { 129 | arr[i] = this.readInt32(); 130 | } 131 | return arr; 132 | } 133 | 134 | public readInt32DoublePair(): Map { 135 | const length = this.readInt32(); 136 | const map = new Map(); 137 | for (let i = 0; i < length; i++) { 138 | this.readUint8(); 139 | const key = this.readInt32(); 140 | this.readUint8(); 141 | const value = this.readInt32(); 142 | map.set(key, value); 143 | } 144 | return map; 145 | } 146 | 147 | public readDateTime(): Date { 148 | const ticks = this.readUint64(); 149 | let date = new Date( 150 | Number((ticks - BigInt("621355968000000000")) / BigInt(10000)) 151 | ); 152 | return date; 153 | } 154 | 155 | public static fromString(value: string): OsuReader { 156 | const buff = new ArrayBuffer(value.length); 157 | const buffView = new Uint8Array(buff); 158 | for (let i = 0; i < value.length; i++) { 159 | buffView[i] = value.charCodeAt(i); 160 | } 161 | return new OsuReader(buff); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/OsuWriter.ts: -------------------------------------------------------------------------------- 1 | import { BaseBuffer } from "./BaseBuffer"; 2 | 3 | export class OsuWriter extends BaseBuffer { 4 | public writeUint8(value: number): OsuWriter { 5 | const dataView = new DataView(this._buff); 6 | dataView.setUint8(this._position, value); 7 | this._position += 1; 8 | this._length += 1; 9 | return this; 10 | } 11 | 12 | public writeInt8(value: number): OsuWriter { 13 | const dataView = new DataView(this._buff); 14 | dataView.setInt8(this._position, value); 15 | this._position += 1; 16 | this._length += 1; 17 | return this; 18 | } 19 | 20 | public writeUint16(value: number): OsuWriter { 21 | const dataView = new DataView(this._buff); 22 | dataView.setUint16(this._position, value, true); 23 | this._position += 2; 24 | this._length += 2; 25 | return this; 26 | } 27 | 28 | public writeInt16(value: number): OsuWriter { 29 | const dataView = new DataView(this._buff); 30 | dataView.setInt16(this._position, value, true); 31 | this._position += 2; 32 | this._length += 2; 33 | return this; 34 | } 35 | 36 | public writeUint32(value: number): OsuWriter { 37 | const dataView = new DataView(this._buff); 38 | dataView.setUint32(this._position, value, true); 39 | this._position += 4; 40 | this._length += 4; 41 | return this; 42 | } 43 | 44 | public writeInt32(value: number): OsuWriter { 45 | const dataView = new DataView(this._buff); 46 | dataView.setInt32(this._position, value, true); 47 | this._position += 4; 48 | this._length += 4; 49 | return this; 50 | } 51 | 52 | public writeUint64(value: bigint): OsuWriter { 53 | const dataView = new DataView(this._buff); 54 | dataView.setBigUint64(this._position, value, true); 55 | this._position += 8; 56 | this._length += 8; 57 | return this; 58 | } 59 | 60 | public writeInt64(value: bigint): OsuWriter { 61 | const dataView = new DataView(this._buff); 62 | dataView.setBigInt64(this._position, value, true); 63 | this._position += 8; 64 | this._length += 8; 65 | return this; 66 | } 67 | 68 | public write7bitInt(value: number): OsuWriter { 69 | let arr = []; 70 | let len = 0; 71 | do { 72 | arr[len] = value & 0x7f; 73 | if ((value >>= 7) !== 0) arr[len] |= 0x80; 74 | len++; 75 | } while (value > 0); 76 | 77 | const dataView = new Uint8Array(this._buff, this._position, arr.length); 78 | dataView.set(arr); 79 | 80 | this._position += arr.length; 81 | this._length += arr.length; 82 | 83 | return this; 84 | } 85 | 86 | public writeString(value: string): OsuWriter { 87 | if (!value || value.length === 0) { 88 | this.writeUint8(0); 89 | } else { 90 | this.writeUint8(11); 91 | this.write7bitInt(value.length); 92 | const dataView = new Uint8Array(this._buff, this._position, value.length); 93 | for (let i = 0; i < value.length; i++) { 94 | dataView[i] = value.charCodeAt(i); 95 | } 96 | 97 | this._position += value.length; 98 | this._length += value.length; 99 | } 100 | return this; 101 | } 102 | 103 | public writeFloat(value: number): OsuWriter { 104 | const dataView = new DataView(this._buff); 105 | dataView.setFloat32(this._position, value, true); 106 | this._position += 4; 107 | this._length += 4; 108 | return this; 109 | } 110 | 111 | public writeDouble(value: number): OsuWriter { 112 | const dataView = new DataView(this._buff); 113 | dataView.setFloat64(this._position, value, true); 114 | this._position += 8; 115 | this._length += 8; 116 | return this; 117 | } 118 | 119 | public writeBoolean(value: boolean): OsuWriter { 120 | return this.writeUint8(value ? 1 : 0); 121 | } 122 | 123 | public writeBytes(values: number[]): OsuWriter { 124 | for (const v of values) { 125 | this.writeUint8(v); 126 | } 127 | return this; 128 | } 129 | 130 | public writeInt32Array(values: number[]): OsuWriter { 131 | this.writeInt16(values.length); 132 | for (const v of values) { 133 | this.writeInt32(v); 134 | } 135 | return this; 136 | } 137 | 138 | public writeInt32DoublePair(values: Map): OsuWriter { 139 | this.writeInt32(values.size); 140 | for (const [k, v] of values.entries()) { 141 | this.writeUint8(8); 142 | this.writeInt32(k); 143 | this.writeUint8(13); 144 | this.writeDouble(v); 145 | } 146 | return this; 147 | } 148 | 149 | public writeDateTime(value: Date): OsuWriter { 150 | const data = BigInt(value.getTime() * 10000) + BigInt("621355968000000000"); 151 | 152 | this.writeUint64(data); 153 | return this; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./OsuReader"; 2 | export * from "./OsuWriter"; 3 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "pretty": false, 8 | "strict": true, 9 | }, 10 | "include": [ 11 | "src" 12 | ], 13 | "exclude": [ 14 | "node_modules", 15 | "dist" 16 | ] 17 | } 18 | --------------------------------------------------------------------------------