454 lines
12 KiB
TypeScript
454 lines
12 KiB
TypeScript
import * as Long from 'long';
|
|
import { Quaternion } from './Quaternion';
|
|
import { GlobalPosition } from './public/interfaces/GlobalPosition';
|
|
import { HTTPAssets } from '../enums/HTTPAssets';
|
|
import { Vector3 } from './Vector3';
|
|
|
|
export class Utils
|
|
{
|
|
static TWO_PI = 6.283185307179586476925286766559;
|
|
static CUT_QUANTA = 0.00002;
|
|
static SCALE_QUANTA = 0.01;
|
|
static SHEAR_QUANTA = 0.01;
|
|
static TAPER_QUANTA = 0.01;
|
|
static REV_QUANTA = 0.015;
|
|
static HOLLOW_QUANTA = 0.00002;
|
|
|
|
static StringToBuffer(str: string): Buffer
|
|
{
|
|
return Buffer.from(str + '\0', 'utf8');
|
|
}
|
|
|
|
static BufferToStringSimple(buf: Buffer, startPos?: number): string
|
|
{
|
|
if (buf.length === 0)
|
|
{
|
|
return '';
|
|
}
|
|
if (buf[buf.length - 1] === 0)
|
|
{
|
|
return buf.slice(0, buf.length - 1).toString('utf8');
|
|
}
|
|
else
|
|
{
|
|
return buf.toString('utf8');
|
|
}
|
|
}
|
|
|
|
static Clamp(value: number, min: number, max: number)
|
|
{
|
|
value = (value > max) ? max : value;
|
|
value = (value < min) ? min : value;
|
|
return value;
|
|
}
|
|
|
|
static fillArray<T>(value: T, count: number)
|
|
{
|
|
const arr: T[] = new Array<T>(count);
|
|
while (count--)
|
|
{
|
|
arr[count] = value;
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
static JSONStringify(obj: object, space: number)
|
|
{
|
|
const cache: any[] = [];
|
|
return JSON.stringify(obj, function (key, value)
|
|
{
|
|
if (typeof value === 'object' && value !== null)
|
|
{
|
|
if (cache.indexOf(value) !== -1)
|
|
{
|
|
try
|
|
{
|
|
return JSON.parse(JSON.stringify(value));
|
|
}
|
|
catch (error)
|
|
{
|
|
return 'Circular Reference';
|
|
}
|
|
}
|
|
cache.push(value);
|
|
}
|
|
return value;
|
|
}, space);
|
|
}
|
|
|
|
static BufferToString(buf: Buffer, startPos?: number):
|
|
{
|
|
readLength: number,
|
|
result: string
|
|
}
|
|
{
|
|
if (buf.length === 0)
|
|
{
|
|
return {
|
|
readLength: 0,
|
|
result: ''
|
|
};
|
|
}
|
|
if (startPos === undefined)
|
|
{
|
|
startPos = 0;
|
|
}
|
|
|
|
let foundNull = -1;
|
|
for (let x = startPos; x <= buf.length; x++)
|
|
{
|
|
if (buf[x] === 0)
|
|
{
|
|
foundNull = x;
|
|
break;
|
|
}
|
|
}
|
|
if (foundNull === -1)
|
|
{
|
|
console.error('BufferToString: Null terminator not found after ' + (buf.length - startPos) + ' bytes. Buffer length: ' + buf.length + ', startPos: ' + startPos);
|
|
foundNull = buf.length - 1;
|
|
}
|
|
return {
|
|
readLength: (foundNull - startPos) + 1,
|
|
result: buf.slice(startPos, foundNull).toString('utf8')
|
|
}
|
|
}
|
|
|
|
static RegionCoordinatesToHandle(regionX: number, regionY: number): GlobalPosition
|
|
{
|
|
const realRegionX = Math.floor(regionX / 256) * 256;
|
|
const realRegionY = Math.floor(regionY / 256) * 256;
|
|
const localX = regionX - realRegionX;
|
|
const localY = regionY - realRegionY;
|
|
const handle = new Long(realRegionY, realRegionX);
|
|
return {
|
|
'regionHandle': handle,
|
|
'regionX': realRegionX / 256,
|
|
'regionY': realRegionY / 256,
|
|
'localX': localX,
|
|
'localY': localY
|
|
};
|
|
}
|
|
|
|
static HTTPAssetTypeToInventoryType(HTTPAssetType: string)
|
|
{
|
|
switch (HTTPAssetType)
|
|
{
|
|
case HTTPAssets.ASSET_TEXTURE:
|
|
return 'texture';
|
|
case HTTPAssets.ASSET_SOUND:
|
|
return 'sound';
|
|
case HTTPAssets.ASSET_ANIMATION:
|
|
return 'animation';
|
|
case HTTPAssets.ASSET_GESTURE:
|
|
return 'gesture';
|
|
case HTTPAssets.ASSET_LANDMARK:
|
|
return 'landmark';
|
|
case HTTPAssets.ASSET_CALLINGCARD:
|
|
return 'callcard';
|
|
case HTTPAssets.ASSET_SCRIPT:
|
|
return 'script';
|
|
case HTTPAssets.ASSET_CLOTHING:
|
|
return 'wearable';
|
|
case HTTPAssets.ASSET_OBJECT:
|
|
return 'object';
|
|
case HTTPAssets.ASSET_NOTECARD:
|
|
return 'notecard';
|
|
case HTTPAssets.ASSET_CATEGORY:
|
|
return 'category';
|
|
case HTTPAssets.ASSET_LSL_TEXT:
|
|
return 'script';
|
|
case HTTPAssets.ASSET_LSL_BYTECODE:
|
|
return 'script';
|
|
case HTTPAssets.ASSET_BODYPART:
|
|
return 'wearable';
|
|
case HTTPAssets.ASSET_MESH:
|
|
return 'mesh';
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
static FloatToByte(val: number, lower: number, upper: number)
|
|
{
|
|
val = Utils.Clamp(val, lower, upper);
|
|
val -= lower;
|
|
val /= (upper - lower);
|
|
return Math.round(val * 255);
|
|
}
|
|
|
|
static ByteToFloat(byte: number, lower: number, upper: number)
|
|
{
|
|
const ONE_OVER_BYTEMAX: number = 1.0 / 255;
|
|
|
|
let fval: number = byte * ONE_OVER_BYTEMAX;
|
|
const delta: number = (upper - lower);
|
|
fval *= delta;
|
|
fval += lower;
|
|
|
|
const error: number = delta * ONE_OVER_BYTEMAX;
|
|
if (Math.abs(fval) < error)
|
|
{
|
|
fval = 0.0;
|
|
}
|
|
return fval;
|
|
}
|
|
|
|
static UInt16ToFloat(val: number, lower: number, upper: number)
|
|
{
|
|
const ONE_OVER_U16_MAX = 1.0 / 65535;
|
|
let fval = val * ONE_OVER_U16_MAX;
|
|
const delta = upper - lower;
|
|
fval *= delta;
|
|
fval += lower;
|
|
|
|
const maxError = delta * ONE_OVER_U16_MAX;
|
|
if (Math.abs(fval) < maxError)
|
|
{
|
|
fval = 0.0;
|
|
}
|
|
return fval;
|
|
}
|
|
|
|
static Base64EncodeString(str: string): string
|
|
{
|
|
const buff = new Buffer(str, 'utf8');
|
|
return buff.toString('base64');
|
|
}
|
|
|
|
static Base64DecodeString(str: string): string
|
|
{
|
|
const buff = new Buffer(str, 'base64');
|
|
return buff.toString('utf8');
|
|
}
|
|
|
|
static HexToLong(hex: string)
|
|
{
|
|
while (hex.length < 16)
|
|
{
|
|
hex = '0' + hex;
|
|
}
|
|
return new Long(parseInt(hex.substr(8), 16), parseInt(hex.substr(0, 8), 16));
|
|
}
|
|
|
|
static ReadRotationFloat(buf: Buffer, pos: number): number
|
|
{
|
|
return ((buf[pos] | (buf[pos + 1] << 8)) / 32768.0) * Utils.TWO_PI;
|
|
}
|
|
|
|
static ReadGlowFloat(buf: Buffer, pos: number): number
|
|
{
|
|
return buf[pos] / 255;
|
|
}
|
|
|
|
static ReadOffsetFloat(buf: Buffer, pos: number): number
|
|
{
|
|
const offset = buf.readInt16LE(pos);
|
|
return offset / 32767.0;
|
|
}
|
|
|
|
static TEOffsetShort(num: number)
|
|
{
|
|
num = Utils.Clamp(num, -1.0, 1.0);
|
|
num *= 32767.0;
|
|
return Math.round(num);
|
|
}
|
|
|
|
static IEEERemainder(x: number, y: number)
|
|
{
|
|
if (isNaN(x))
|
|
{
|
|
return x; // IEEE 754-2008: NaN payload must be preserved
|
|
}
|
|
if (isNaN(y))
|
|
{
|
|
return y; // IEEE 754-2008: NaN payload must be preserved
|
|
}
|
|
const regularMod = x % y;
|
|
if (isNaN(regularMod))
|
|
{
|
|
return NaN;
|
|
}
|
|
if (regularMod === 0)
|
|
{
|
|
if (Math.sign(x) < 0)
|
|
{
|
|
return -0;
|
|
}
|
|
}
|
|
const alternativeResult = regularMod - (Math.abs(y) * Math.sign(x));
|
|
if (Math.abs(alternativeResult) === Math.abs(regularMod))
|
|
{
|
|
const divisionResult = x / y;
|
|
const roundedResult = Math.round(divisionResult);
|
|
if (Math.abs(roundedResult) > Math.abs(divisionResult))
|
|
{
|
|
return alternativeResult;
|
|
}
|
|
else
|
|
{
|
|
return regularMod;
|
|
}
|
|
}
|
|
if (Math.abs(alternativeResult) < Math.abs(regularMod))
|
|
{
|
|
return alternativeResult;
|
|
}
|
|
else
|
|
{
|
|
return regularMod;
|
|
}
|
|
}
|
|
|
|
static TERotationShort(rotation: number)
|
|
{
|
|
return Math.floor(((Utils.IEEERemainder(rotation, Utils.TWO_PI) / Utils.TWO_PI) * 32768.0) + 0.5);
|
|
}
|
|
|
|
static TEGlowByte(glow: number)
|
|
{
|
|
return (glow * 255.0);
|
|
}
|
|
|
|
static NumberToByteBuffer(num: number): Buffer
|
|
{
|
|
const buf = Buffer.allocUnsafe(1);
|
|
buf.writeUInt8(num, 0);
|
|
return buf;
|
|
}
|
|
|
|
static NumberToShortBuffer(num: number): Buffer
|
|
{
|
|
const buf = Buffer.allocUnsafe(2);
|
|
buf.writeInt16LE(num, 0);
|
|
return buf;
|
|
}
|
|
|
|
static NumberToFloatBuffer(num: number): Buffer
|
|
{
|
|
const buf = Buffer.allocUnsafe(4);
|
|
buf.writeFloatLE(num, 0);
|
|
return buf;
|
|
}
|
|
|
|
static numberOrZero(num: number | undefined): number
|
|
{
|
|
if (num === undefined)
|
|
{
|
|
return 0;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
static vector3OrZero(vec: Vector3 | undefined): Vector3
|
|
{
|
|
if (vec === undefined)
|
|
{
|
|
return Vector3.getZero();
|
|
}
|
|
return vec;
|
|
}
|
|
|
|
static quaternionOrZero(quat: Quaternion | undefined): Quaternion
|
|
{
|
|
if (quat === undefined)
|
|
{
|
|
return Quaternion.getIdentity();
|
|
}
|
|
return quat;
|
|
}
|
|
|
|
static packBeginCut(beginCut: number): number
|
|
{
|
|
return Math.round(beginCut / Utils.CUT_QUANTA);
|
|
}
|
|
|
|
static packEndCut(endCut: number): number
|
|
{
|
|
return (50000 - Math.round(endCut / Utils.CUT_QUANTA));
|
|
}
|
|
|
|
static packPathScale(pathScale: number): number
|
|
{
|
|
return (200 - Math.round(pathScale / Utils.SCALE_QUANTA));
|
|
}
|
|
|
|
static packPathShear(pathShear: number): number
|
|
{
|
|
return Math.round(pathShear / Utils.SHEAR_QUANTA);
|
|
}
|
|
|
|
static packPathTwist(pathTwist: number): number
|
|
{
|
|
return Math.round(pathTwist / Utils.SCALE_QUANTA);
|
|
}
|
|
|
|
static packPathTaper(pathTaper: number): number
|
|
{
|
|
return Math.round(pathTaper / Utils.TAPER_QUANTA);
|
|
}
|
|
|
|
static packPathRevolutions(pathRevolutions: number): number
|
|
{
|
|
return Math.round((pathRevolutions - 1) / Utils.REV_QUANTA);
|
|
}
|
|
|
|
static packProfileHollow(profileHollow: number): number
|
|
{
|
|
return Math.round(profileHollow / Utils.HOLLOW_QUANTA);
|
|
}
|
|
|
|
static unpackBeginCut(beginCut: number): number
|
|
{
|
|
return beginCut * Utils.CUT_QUANTA;
|
|
}
|
|
|
|
static unpackEndCut(endCut: number): number
|
|
{
|
|
return (50000 - endCut) * Utils.CUT_QUANTA;
|
|
}
|
|
|
|
static unpackPathScale(pathScale: number): number
|
|
{
|
|
return (200 - pathScale) * Utils.SCALE_QUANTA;
|
|
}
|
|
|
|
static unpackPathShear(pathShear: number): number
|
|
{
|
|
return pathShear * Utils.SHEAR_QUANTA;
|
|
}
|
|
|
|
static unpackPathTwist(pathTwist: number): number
|
|
{
|
|
return pathTwist * Utils.SCALE_QUANTA;
|
|
}
|
|
|
|
static unpackPathTaper(pathTaper: number): number
|
|
{
|
|
return pathTaper * Utils.TAPER_QUANTA;
|
|
}
|
|
|
|
static unpackPathRevolutions(pathRevolutions: number): number
|
|
{
|
|
return pathRevolutions * Utils.REV_QUANTA + 1;
|
|
}
|
|
|
|
static unpackProfileHollow(profileHollow: number): number
|
|
{
|
|
return profileHollow * Utils.HOLLOW_QUANTA;
|
|
}
|
|
|
|
static nullTerminatedString(str: string)
|
|
{
|
|
const index = str.indexOf('\0');
|
|
if (index === -1)
|
|
{
|
|
return str;
|
|
}
|
|
else
|
|
{
|
|
return str.substr(0, index - 1);
|
|
}
|
|
}
|
|
}
|