Code:
#pragma once
#include <vector>
//C++ doesnt have those data types, so I'll just store them in floats, but this is how you read them
using half = float; //2 bytes, short(value)/32767.0f
using single = float; //1 byte, byte(value)/127.0f
using half_mid = float; //2 bytes, (short(value) < 0) ? ((-(val + 32768))/32767.0f) : (val/32767.0f);
using single_mid = float; //1 byte, (byte(value) < 0) ? ((-(val + 128))/127.0f) : (val/127.0f);
struct Coordinates
{
float x;
float y;
float z;
};
struct CoordinatesHalfMid
{
half_mid x;
half_mid y;
half_mid z;
};
struct CoordinatesUByte
{
uint8_t x;
uint8_t z;
uint8_t y;
};
using Vertex = Coordinates;
using Normal = Coordinates;
struct UVCoord
{
float u;
float v;
};
struct Face
{
uint32_t f1;
uint32_t f2;
uint32_t f3;
};
struct Group
{
std::vector<Face> faces;
};
struct Quaternion
{
float x;
float y;
float z;
float w;
};
struct QuaternionHalfMid
{
half_mid x;
half_mid y;
half_mid z;
half_mid w;
};
using Rotation = Quaternion;
using Scale = Coordinates;
struct TranslateAnimationFrameInfo
{
int16_t timestamp;
Coordinates translate;
};
struct RotateAnimationFrameInfo
{
int16_t timestamp;
Rotation rotation;
};
struct ScaleAnimationFrameInfo
{
int16_t timestamp;
Scale scale;
};
struct BoundingBox
{
Coordinates min;
Coordinates mid;
Coordinates max;
float radius;
};
struct TextureInfo
{
int32_t texture_id;
bool is_both_sides; //backface culling
uint16_t group_id; //id of faces group
};
struct AnimationHeader
{
uint16_t unk; //???
uint16_t length; //these 3 names might be misleading, don't assume they are correct
uint16_t ticks;
uint16_t speed;
};
struct NodeAnimationFrameValues
{
Rotation rotation;
Scale scale;
Coordinates translation;
};
struct NodeInfo
{
Coordinates translate;
Rotation rotate;
Scale scale;
std::vector<TranslateAnimationFrameInfo> translate_animation_frames;
std::vector<RotateAnimationFrameInfo> rotate_animation_frames;
std::vector<ScaleAnimationFrameInfo> scale_animation_frames;
std::vector<TextureInfo> texture_infos;
};
struct TransformNode
{
std::vector<TransformNode> children;
NodeInfo node_info;
};
struct TextureData
{
int16_t width;
int16_t height;
bool smooth_scaling;
uint8_t mipmap_depth;
uint32_t internal_format;
uint32_t format;
uint32_t type;
uint32_t texId;
uint8_t bytesize;
std::vector<char> data;
};
struct Color
{
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
};
struct VfxConfigurationFromAtoB
{
float origin_height;
uint8_t follow_entity_height;
uint8_t padding_bytes[42];
};
struct VfxConfigurationNormal
{
float origin_height;
Coordinates origin_offset;
float target_height;
Coordinates target_offset;
float distance_offset; //offset in origin<->target axis
float travel_speed;
float height_parabola;
uint16_t unk1;
half_mid plane_parabola;
};
struct VfxConfigurationRandomness
{
CoordinatesHalfMid emitters_offset; //the whole cube of emitters moves
QuaternionHalfMid emitters_rotation; //the whole cube of emitters rotates
CoordinatesHalfMid emitters_distance; //distance between themselves in their grid
CoordinatesHalfMid emitters_vector;
CoordinatesUByte emitters_count; //amount of emitters and amount of axis it emits on given coord
CoordinatesUByte particles_rotation; //along given axis, intensity
CoordinatesUByte particles_scale; //along given axis, intensity
uint8_t gravity;
uint8_t first_tick_intensity;
uint8_t intensity;
uint8_t rate;
uint16_t tick_delay_min;
uint16_t tick_delay_max;
uint16_t particle_lifespan_min;
uint16_t particle_lifespan_max;
};
union VfxConfiguration
{
VfxConfigurationNormal normal;
VfxConfigurationFromAtoB from_a_to_b;
VfxConfigurationRandomness randomness;
};
struct VfxPatchCount
{
uint32_t count;
uint32_t timestamps_address; //only in-memory for faster reading (?)
uint32_t values_address; //only in-memory for faster reading (?)
};
enum VfxObjectType : uint8_t
{
NORMAL = 0,
TARGETED = 1,
RANDOMNESS = 2
};
enum VfxObjectInterpolationType : uint8_t
{
RUN_ONCE = 0,
RUN_ONCE_AND_TO_FIRST = 1,
REPEAT = 2,
ONLY_LAST = 3
};
struct VfxObjectHeader
{
uint32_t ft;
VfxObjectType type; //0 - normal, 1 - from A to B, 2 - randomness
uint8_t camera_lock; //maybe bit flags?
uint8_t only_on_hit;
uint8_t split_damage;
uint32_t source_blending;
uint32_t destination_blending;
int32_t model_1_id;
int32_t model_2_id; //apparently always -1 and not actually model2? wtf
uint8_t on_target;
VfxConfiguration configuration; //VfxConfigurationNormal, VfxConfigurationFromAtoB, or VfxConfigurationRandomness
AnimationHeader animation_ticks_info;
int32_t anim_offset; //appear after this time
uint32_t anim_duration; //absolute, so if anim_offset > anim_duration, nothing will show
uint8_t flag_0;
VfxObjectInterpolationType interpolation;
uint8_t follow_previous_object; //if true, object will be relative to the previous (?) one
uint8_t has_depth_test;
uint8_t has_transform; //scale, rotate and translate patches
uint8_t flag_2;
uint8_t flag_3; //unused???
uint8_t has_texture_transform; //texture_scale, texture_rotation and texture_translate patches
VfxPatchCount patch_rotation_count;
VfxPatchCount patch_scale_count;
VfxPatchCount patch_translation_count;
VfxPatchCount patch_color_count;
VfxPatchCount patch_texture_count;
VfxPatchCount patch_texture_rotation_count;
VfxPatchCount patch_texture_scale_count;
VfxPatchCount patch_texture_translate_count;
};
struct VfxHeader
{
int32_t vfx_id;
uint32_t unk1;
uint32_t unk2;
uint16_t object_count;
uint32_t unk3;
int32_t unk_id2;
uint32_t unk4;
};
struct VfxPatchRotation
{
std::vector<uint16_t> timestamps;
std::vector<Rotation> rotations;
};
struct VfxPatchScale
{
std::vector<uint16_t> timestamps;
std::vector<Scale> scales;
};
struct VfxPatchTranslation
{
std::vector<uint16_t> timestamps;
std::vector<Coordinates> translations;
};
struct VfxPatchColor
{
std::vector<uint16_t> timestamps;
std::vector<Color> colors;
};
struct VfxPatchTexture
{
std::vector<uint16_t> timestamps;
std::vector<int32_t> textures;
};
struct VfxPatchTextureRotation
{
std::vector<uint16_t> timestamps;
std::vector<Rotation> texture_rotations;
};
struct VfxPatchTextureScale
{
std::vector<uint16_t> timestamps;
std::vector<Scale> texture_scales;
};
struct VfxPatchTextureTranslation
{
std::vector<uint16_t> timestamps;
std::vector<Coordinates> texture_translations;
};
struct VfxObjectPatch
{
VfxPatchRotation rotation;
VfxPatchScale scale;
VfxPatchTranslation translation;
VfxPatchColor color;
VfxPatchTexture texture;
VfxPatchTextureRotation texture_rotation;
VfxPatchTextureScale texture_scale;
VfxPatchTextureTranslation texture_translation;
};
struct VfxObjectPatchValues
{
Rotation rotation;
Scale scale;
Coordinates translation;
Color color;
int32_t texture;
Rotation texture_rotation;
Scale texture_scale;
Coordinates texture_translation;
};
NSeffElement is irrelevant, this is just so you can know how the NSeffData item structure is, because it's not in the code above.
Code:
NSeffElement vfx;
VfxHeader vfx_header{};
vfx_header.vfx_id = LittleEndianFileReader::readInt(h);
vfx_header.unk1 = LittleEndianFileReader::readInt(h);
vfx_header.unk2 = LittleEndianFileReader::readInt(h);
vfx_header.object_count = LittleEndianFileReader::readShort(h);
vfx_header.unk3 = LittleEndianFileReader::readInt(h);
vfx_header.unk_id2 = LittleEndianFileReader::readShort(h);
vfx_header.unk4 = LittleEndianFileReader::readInt(h);
vfx.setHeader(vfx_header);
for (int i = 0; i < vfx_header.object_count; i++)
{
VfxObjectHeader obj_info{};
obj_info.ft = LittleEndianFileReader::readUInt(h);
obj_info.type = (VfxObjectType)LittleEndianFileReader::readUByte(h);
obj_info.camera_lock = LittleEndianFileReader::readUByte(h);
obj_info.only_on_hit = LittleEndianFileReader::readUByte(h);
obj_info.split_damage = LittleEndianFileReader::readUByte(h);
obj_info.source_blending = LittleEndianFileReader::readUInt(h);
obj_info.destination_blending = LittleEndianFileReader::readUInt(h);
obj_info.model_1_id = LittleEndianFileReader::readInt(h);
obj_info.model_2_id = LittleEndianFileReader::readInt(h);
obj_info.on_target = LittleEndianFileReader::readUByte(h);
if (obj_info.type == VfxObjectType::RANDOMNESS)
{
VfxConfigurationRandomness conf{};
conf.emitters_offset.x = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_offset.y= LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_offset.z = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_rotation.x = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_rotation.y = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_rotation.z = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_rotation.w = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_distance.x = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_distance.y = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_distance.z = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_vector.x = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_vector.y = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_vector.z = LittleEndianFileReader::readPackedMidFloat(h);
conf.emitters_count.x = LittleEndianFileReader::readUByte(h);
conf.emitters_count.y = LittleEndianFileReader::readUByte(h);
conf.emitters_count.z = LittleEndianFileReader::readUByte(h);
conf.particles_rotation.x = LittleEndianFileReader::readUByte(h);
conf.particles_rotation.y = LittleEndianFileReader::readUByte(h);
conf.particles_rotation.z = LittleEndianFileReader::readUByte(h);
conf.particles_scale.x = LittleEndianFileReader::readUByte(h);
conf.particles_scale.y = LittleEndianFileReader::readUByte(h);
conf.particles_scale.z = LittleEndianFileReader::readUByte(h);
conf.gravity = LittleEndianFileReader::readUByte(h);
conf.first_tick_intensity = LittleEndianFileReader::readUByte(h);
conf.intensity = LittleEndianFileReader::readUByte(h);
conf.rate = LittleEndianFileReader::readUByte(h);
conf.tick_delay_min = LittleEndianFileReader::readUShort(h);
conf.tick_delay_max = LittleEndianFileReader::readUShort(h);
conf.particle_lifespan_min = LittleEndianFileReader::readUShort(h);
conf.particle_lifespan_max = LittleEndianFileReader::readUShort(h);
obj_info.configuration.randomness = conf;
}
else if (obj_info.type == VfxObjectType::NORMAL)
{
VfxConfigurationNormal conf{};
conf.origin_height = LittleEndianFileReader::readFloat(h);
conf.origin_offset.x = LittleEndianFileReader::readFloat(h);
conf.origin_offset.y = LittleEndianFileReader::readFloat(h);
conf.origin_offset.z = LittleEndianFileReader::readFloat(h);
conf.target_height = LittleEndianFileReader::readFloat(h);
conf.target_offset.x = LittleEndianFileReader::readFloat(h);
conf.target_offset.y = LittleEndianFileReader::readFloat(h);
conf.target_offset.z = LittleEndianFileReader::readFloat(h);
conf.distance_offset = LittleEndianFileReader::readFloat(h);
conf.travel_speed = LittleEndianFileReader::readFloat(h);
conf.height_parabola = LittleEndianFileReader::readFloat(h);
conf.unk1 = LittleEndianFileReader::readUShort(h);
conf.plane_parabola = LittleEndianFileReader::readBytePackedMidFloat(h);
obj_info.configuration.normal = conf;
}
else
{
VfxConfigurationFromAtoB conf{};
conf.origin_height = LittleEndianFileReader::readFloat(h);
conf.follow_entity_height = LittleEndianFileReader::readUByte(h);
conf.padding_bytes;
h.read(reinterpret_cast<char*>(conf.padding_bytes), 42);
obj_info.configuration.from_a_to_b = conf;
}
obj_info.animation_ticks_info.unk = LittleEndianFileReader::readUShort(h);
obj_info.animation_ticks_info.length = LittleEndianFileReader::readUShort(h);
obj_info.animation_ticks_info.ticks = LittleEndianFileReader::readUShort(h);
obj_info.animation_ticks_info.speed = LittleEndianFileReader::readUShort(h);
obj_info.anim_offset = LittleEndianFileReader::readInt(h);
obj_info.anim_duration = LittleEndianFileReader::readUInt(h);
obj_info.flag_0 = LittleEndianFileReader::readUByte(h);
obj_info.interpolation = (VfxObjectInterpolationType)LittleEndianFileReader::readUByte(h);
obj_info.follow_previous_object = LittleEndianFileReader::readUByte(h);
obj_info.has_depth_test = LittleEndianFileReader::readUByte(h);
obj_info.has_transform = LittleEndianFileReader::readUByte(h);
obj_info.flag_2 = LittleEndianFileReader::readUByte(h);
obj_info.flag_3 = LittleEndianFileReader::readUByte(h);
obj_info.has_texture_transform = LittleEndianFileReader::readUByte(h);
obj_info.patch_rotation_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_rotation_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_rotation_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_scale_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_scale_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_scale_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_translation_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_translation_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_translation_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_color_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_color_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_color_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_rotation_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_rotation_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_rotation_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_scale_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_scale_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_scale_count.values_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_translate_count.count = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_translate_count.timestamps_address = LittleEndianFileReader::readUInt(h);
obj_info.patch_texture_translate_count.values_address = LittleEndianFileReader::readUInt(h);
vfx.addObjectInfo(obj_info);
}
for (int i = 0; i < vfx_header.object_count; i++)
{
VfxObjectPatch obj_patch;
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_rotation_count.count; j++)
{
obj_patch.rotation.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_rotation_count.count; j++)
{
Rotation rotation{};
rotation.x = LittleEndianFileReader::readFloat(h);
rotation.y = LittleEndianFileReader::readFloat(h);
rotation.z = LittleEndianFileReader::readFloat(h);
rotation.w = LittleEndianFileReader::readFloat(h);
obj_patch.rotation.rotations.push_back(rotation);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_scale_count.count; j++)
{
obj_patch.scale.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_scale_count.count; j++)
{
Scale scale{};
scale.x = LittleEndianFileReader::readFloat(h);
scale.y = LittleEndianFileReader::readFloat(h);
scale.z = LittleEndianFileReader::readFloat(h);
obj_patch.scale.scales.push_back(scale);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_translation_count.count; j++)
{
obj_patch.translation.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_translation_count.count; j++)
{
Coordinates translation{};
translation.x = LittleEndianFileReader::readFloat(h);
translation.y = LittleEndianFileReader::readFloat(h);
translation.z = LittleEndianFileReader::readFloat(h);
obj_patch.translation.translations.push_back(translation);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_color_count.count; j++)
{
obj_patch.color.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_color_count.count; j++)
{
Color color{};
color.blue = LittleEndianFileReader::readUByte(h);
color.green = LittleEndianFileReader::readUByte(h);
color.red = LittleEndianFileReader::readUByte(h);
color.alpha = LittleEndianFileReader::readUByte(h);
obj_patch.color.colors.push_back(color);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_count.count; j++)
{
obj_patch.texture.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_count.count; j++)
{
int32_t texture = LittleEndianFileReader::readInt(h);
obj_patch.texture.textures.push_back(texture);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_rotation_count.count; j++)
{
obj_patch.texture_rotation.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_rotation_count.count; j++)
{
Rotation texture_rotation{};
texture_rotation.x = LittleEndianFileReader::readFloat(h);
texture_rotation.y = LittleEndianFileReader::readFloat(h);
texture_rotation.z = LittleEndianFileReader::readFloat(h);
texture_rotation.w = LittleEndianFileReader::readFloat(h);
obj_patch.texture_rotation.texture_rotations.push_back(texture_rotation);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_scale_count.count; j++)
{
obj_patch.texture_scale.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_scale_count.count; j++)
{
Scale texture_scale{};
texture_scale.x = LittleEndianFileReader::readFloat(h);
texture_scale.y = LittleEndianFileReader::readFloat(h);
texture_scale.z = LittleEndianFileReader::readFloat(h);
obj_patch.texture_scale.texture_scales.push_back(texture_scale);
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_translate_count.count; j++)
{
obj_patch.texture_translation.timestamps.push_back(LittleEndianFileReader::readUShort(h));
}
for (uint32_t j = 0; j < vfx.getObjectInfos(i).patch_texture_translate_count.count; j++)
{
Coordinates texture_translation{};
texture_translation.x = LittleEndianFileReader::readFloat(h);
texture_translation.y = LittleEndianFileReader::readFloat(h);
texture_translation.z = LittleEndianFileReader::readFloat(h);
obj_patch.texture_translation.texture_translations.push_back(texture_translation);
}
vfx.addObjectPatch(obj_patch);
}
Code:
#pragma once
#include <cstdint>
#include <fstream>
namespace LittleEndianFileReader
{
inline int8_t readByte(std::fstream& f)
{
int8_t val = 0;
char bytes[1] = { 0 };
f.read(bytes, 1);
val = bytes[0];
return val;
}
inline uint8_t readUByte(std::fstream& f)
{
uint8_t val = 0;
char bytes[1] = { 0 };
f.read(bytes, 1);
val = (unsigned)bytes[0];
return val;
}
inline int32_t readInt(std::fstream& f)
{
int32_t val = 0;
char bytes[4] = { 0 };
f.read(bytes, 4);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8) | ((bytes[2] & 0xFF) << 16) | ((bytes[3] & 0xFF) << 24);
return val;
}
inline uint32_t readUInt(std::fstream& f)
{
uint32_t val = 0;
char bytes[4] = { 0 };
f.read(bytes, 4);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8) | ((bytes[2] & 0xFF) << 16) | ((bytes[3] & 0xFF) << 24);
return val;
}
inline int16_t readShort(std::fstream& f)
{
int16_t val = 0;
char bytes[2] = { 0 };
f.read(bytes, 2);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8);
return val;
}
inline uint16_t readUShort(std::fstream& f)
{
uint16_t val = 0;
char bytes[2] = { 0 };
f.read(bytes, 2);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8);
return val;
}
inline float readFloat(std::fstream& f)
{
uint32_t val = 0;
char bytes[4] = { 0 };
f.read(bytes, 4);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8) | ((bytes[2] & 0xFF) << 16) | ((bytes[3] & 0xFF) << 24);
return reinterpret_cast<float&>(val);
}
inline float readPackedFloat(std::fstream& f)
{
int16_t val = 0;
char bytes[2] = { 0 };
f.read(bytes, 2);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8);
return val / 32767.0f;
}
inline float readPackedMidFloat(std::fstream& f)
//0x0000~0x7FFF positive, 0x8000~0xFFFF negative, where 0x8000 is equal to -0x0001
{
int16_t val = 0;
char bytes[2] = { 0 };
f.read(bytes, 2);
val = (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8);
if (val < 0)
val = -(val + 32768);
return val / 32767.0f;
}
inline float readBytePackedFloat(std::fstream& f)
{
int8_t val = 0;
char bytes[1] = { 0 };
f.read(bytes, 1);
val = bytes[0] & 0xFF;
return val / 127.0f;
}
inline float readBytePackedMidFloat(std::fstream& f)
//0x00~0x7F positive, 0x80~0xFF negative, where 0x80 is equal to -0x01
{
int8_t val = 0;
char bytes[1] = { 0 };
f.read(bytes, 1);
val = bytes[0] & 0xFF;
if (val < 0)
val = -(val + 128);
return val / 127.0f;
}
inline void skip(std::fstream& f, uint32_t amount)
{
f.seekg(amount, std::ios::cur);
}
}







