structures.h
loadVfx function body (pseudo .cpp)
NSeffElement is irrelevant, this is just so you can know how the NSeffData item structure is, because it's not in the code above.
little endian file reader .h that i used
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);
}
}