mirror of
https://github.com/DrBeef/Raze.git
synced 2025-02-20 18:52:43 +00:00
- Backend update from GZDoom
* IQM enhancements * config storage in Users * moving of savegame filename generation to the backend
This commit is contained in:
parent
b4a49ea228
commit
41fc5660e5
25 changed files with 454 additions and 315 deletions
|
@ -138,7 +138,7 @@ IMPLEMENT_CLASS(DShape2D, false, false)
|
|||
|
||||
static void Shape2D_SetTransform(DShape2D* self, DShape2DTransform *transform)
|
||||
{
|
||||
self->transform = transform->transform;
|
||||
self->transform = PARAM_NULLCHECK(transform, transform)->transform;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, SetTransform, Shape2D_SetTransform)
|
||||
|
|
|
@ -177,7 +177,7 @@ static void setmsgcolor (int index, int color);
|
|||
FILE *Logfile = NULL;
|
||||
|
||||
|
||||
CVARD_NAMED(Int, msglevel, "msg", 0, CVAR_ARCHIVE, "Filters HUD message by importance");
|
||||
CVARD_NAMED(Int, msglevel, msg, 0, CVAR_ARCHIVE, "Filters HUD message by importance");
|
||||
|
||||
CUSTOM_CVAR (Int, msg0color, CR_UNTRANSLATED, CVAR_ARCHIVE)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ FString M_GetDemoPath();
|
|||
|
||||
FString M_GetNormalizedPath(const char* path);
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
FString M_GetMacAppSupportPath(const bool create = true);
|
||||
void M_GetMacSearchDirectories(FString& user_docs, FString& user_app_support, FString& local_app_support);
|
||||
|
|
|
@ -75,8 +75,8 @@ enum
|
|||
};
|
||||
|
||||
|
||||
void I_Error(const char *fmt, ...) ATTRIBUTE((format(printf,1,2)));
|
||||
void I_FatalError(const char* fmt, ...) ATTRIBUTE((format(printf, 1, 2)));
|
||||
[[noreturn]] void I_Error(const char *fmt, ...) ATTRIBUTE((format(printf,1,2)));
|
||||
[[noreturn]] void I_FatalError(const char* fmt, ...) ATTRIBUTE((format(printf, 1, 2)));
|
||||
|
||||
// This really could need some cleanup - the main problem is that it'd create
|
||||
// lots of potential for merge conflicts.
|
||||
|
|
|
@ -46,8 +46,11 @@
|
|||
#include "findfile.h"
|
||||
#include "v_draw.h"
|
||||
#include "savegamemanager.h"
|
||||
#include "m_argv.h"
|
||||
#include "i_specialpaths.h"
|
||||
|
||||
|
||||
CVAR(String, save_dir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
FString SavegameFolder;
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
|
@ -538,3 +541,56 @@ DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, WindowSize);
|
|||
DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, quickSaveSlot);
|
||||
DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, SaveCommentString);
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// todo: cache this - it never changes once set up.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
FString G_GetSavegamesFolder()
|
||||
{
|
||||
FString name;
|
||||
bool usefilter;
|
||||
|
||||
if (const char* const dir = Args->CheckValue("-savedir"))
|
||||
{
|
||||
name = dir;
|
||||
usefilter = false; //-savedir specifies an absolute save directory path.
|
||||
}
|
||||
else
|
||||
{
|
||||
name = **save_dir ? save_dir : M_GetSavegamesPath();
|
||||
usefilter = true;
|
||||
}
|
||||
|
||||
const size_t len = name.Len();
|
||||
if (len > 0)
|
||||
{
|
||||
FixPathSeperator(name);
|
||||
if (name[len - 1] != '/')
|
||||
name << '/';
|
||||
}
|
||||
|
||||
if (usefilter && SavegameFolder.IsNotEmpty())
|
||||
name << SavegameFolder << '/';
|
||||
|
||||
name = NicePath(name);
|
||||
CreatePath(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
FString G_BuildSaveName(const char* prefix)
|
||||
{
|
||||
FString name = G_GetSavegamesFolder() + prefix;
|
||||
DefaultExtension(name, "." SAVEGAME_EXT); // only add an extension if the prefix doesn't have one already.
|
||||
name = NicePath(name);
|
||||
name.Substitute("\\", "/");
|
||||
return name;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,3 +59,6 @@ public:
|
|||
|
||||
};
|
||||
|
||||
extern FString SavegameFolder; // specifies a subdirectory for the current IWAD.
|
||||
FString G_GetSavegamesFolder();
|
||||
FString G_BuildSaveName(const char* prefix);
|
||||
|
|
16
source/common/models/bonecomponents.h
Normal file
16
source/common/models/bonecomponents.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include "dobject.h"
|
||||
#include "tarray.h"
|
||||
#include "TRS.h"
|
||||
#include "matrix.h"
|
||||
|
||||
|
||||
class DBoneComponents : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DBoneComponents, DObject);
|
||||
public:
|
||||
TArray<TArray<TRS>> trscomponents;
|
||||
TArray<TArray<VSMatrix>> trsmatrix;
|
||||
|
||||
DBoneComponents() = default;
|
||||
};
|
|
@ -4,7 +4,9 @@
|
|||
#include "textureid.h"
|
||||
#include "i_modelvertexbuffer.h"
|
||||
#include "matrix.h"
|
||||
#include "TRS.h"
|
||||
|
||||
class DBoneComponents;
|
||||
class FModelRenderer;
|
||||
class FGameTexture;
|
||||
class IModelVertexBuffer;
|
||||
|
@ -74,8 +76,8 @@ public:
|
|||
virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0;
|
||||
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) = 0;
|
||||
virtual float getAspectFactor(float vscale) { return 1.f; }
|
||||
virtual const TArray<VSMatrix>* AttachAnimationData() { return nullptr; };
|
||||
virtual const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData) { return {}; };
|
||||
virtual const TArray<TRS>* AttachAnimationData() { return nullptr; };
|
||||
virtual const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>& animationData, DBoneComponents* bones, int index) { return {}; };
|
||||
|
||||
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
||||
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include "vectors.h"
|
||||
#include "matrix.h"
|
||||
#include "common/rendering/i_modelvertexbuffer.h"
|
||||
#include "m_swap.h"
|
||||
|
||||
class DBoneComponents;
|
||||
|
||||
struct IQMMesh
|
||||
{
|
||||
|
@ -112,8 +115,8 @@ public:
|
|||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData, int boneStartPosition) override;
|
||||
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
||||
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData) override;
|
||||
const TArray<TRS>* AttachAnimationData() override;
|
||||
const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>& animationData, DBoneComponents* bones, int index) override;
|
||||
|
||||
private:
|
||||
void LoadGeometry();
|
||||
|
@ -132,7 +135,6 @@ private:
|
|||
TArray<IQMJoint> Joints;
|
||||
TArray<IQMPose> Poses;
|
||||
TArray<IQMAnim> Anims;
|
||||
TArray<VSMatrix> FrameTransforms;
|
||||
TArray<IQMBounds> Bounds;
|
||||
TArray<IQMVertexArray> VertexArrays;
|
||||
uint32_t NumVertices = 0;
|
||||
|
@ -141,6 +143,7 @@ private:
|
|||
|
||||
TArray<VSMatrix> baseframe;
|
||||
TArray<VSMatrix> inversebaseframe;
|
||||
TArray<TRS> TRSData;
|
||||
};
|
||||
|
||||
struct IQMReadErrorException { };
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
#include "engineerrors.h"
|
||||
#include "dobject.h"
|
||||
#include "bonecomponents.h"
|
||||
|
||||
IMPLEMENT_CLASS(DBoneComponents, false, false);
|
||||
|
||||
|
||||
IQMModel::IQMModel()
|
||||
{
|
||||
|
@ -58,8 +63,11 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
uint32_t num_extensions = reader.ReadUInt32();
|
||||
uint32_t ofs_extensions = reader.ReadUInt32();
|
||||
|
||||
if (num_meshes <= 0)
|
||||
I_FatalError("Invalid model: \"%s%s\", no mesh data is unsupported", path, fileSystem.GetLongName(mLumpNum).GetChars());
|
||||
/*if (num_joints <= 0)
|
||||
{
|
||||
Printf("Invalid model: \"%s%s\", no joint data is present\n", path, fileSystem.GetLongName(mLumpNum).GetChars());
|
||||
return false;
|
||||
}*/
|
||||
|
||||
if (num_text == 0)
|
||||
return false;
|
||||
|
@ -168,18 +176,10 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
{
|
||||
baseframe[i] = m;
|
||||
inversebaseframe[i] = invm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swap YZ axis as we did that with the vertices down in LoadGeometry.
|
||||
// This is an unfortunate side effect of the coordinate system in the gzdoom model rendering system
|
||||
float swapYZ[16] = { 0.0f };
|
||||
swapYZ[0 + 0 * 4] = 1.0f;
|
||||
swapYZ[1 + 2 * 4] = 1.0f;
|
||||
swapYZ[2 + 1 * 4] = 1.0f;
|
||||
swapYZ[3 + 3 * 4] = 1.0f;
|
||||
|
||||
FrameTransforms.Resize(num_frames * num_poses);
|
||||
TRSData.Resize(num_frames * num_poses);
|
||||
reader.SeekTo(ofs_frames);
|
||||
for (uint32_t i = 0; i < num_frames; i++)
|
||||
{
|
||||
|
@ -204,39 +204,9 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
scale.Y = p.ChannelOffset[8]; if (p.ChannelMask & 0x100) scale.Y += reader.ReadUInt16() * p.ChannelScale[8];
|
||||
scale.Z = p.ChannelOffset[9]; if (p.ChannelMask & 0x200) scale.Z += reader.ReadUInt16() * p.ChannelScale[9];
|
||||
|
||||
VSMatrix m;
|
||||
m.loadIdentity();
|
||||
m.translate(translate.X, translate.Y, translate.Z);
|
||||
m.multQuaternion(quaternion);
|
||||
m.scale(scale.X, scale.Y, scale.Z);
|
||||
|
||||
// Concatenate each pose with the inverse base pose to avoid doing this at animation time.
|
||||
// If the joint has a parent, then it needs to be pre-concatenated with its parent's base pose.
|
||||
// Thus it all negates at animation time like so:
|
||||
// (parentPose * parentInverseBasePose) * (parentBasePose * childPose * childInverseBasePose) =>
|
||||
// parentPose * (parentInverseBasePose * parentBasePose) * childPose * childInverseBasePose =>
|
||||
// parentPose * childPose * childInverseBasePose
|
||||
VSMatrix& result = FrameTransforms[i * num_poses + j];
|
||||
if (p.Parent >= 0)
|
||||
{
|
||||
result = baseframe[p.Parent];
|
||||
result.multMatrix(m);
|
||||
result.multMatrix(inversebaseframe[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = m;
|
||||
result.multMatrix(inversebaseframe[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < num_poses; j++)
|
||||
{
|
||||
VSMatrix m;
|
||||
m.loadMatrix(swapYZ);
|
||||
m.multMatrix(FrameTransforms[i * num_poses + j]);
|
||||
m.multMatrix(swapYZ);
|
||||
FrameTransforms[i * num_poses + j] = m;
|
||||
TRSData[i * num_poses + j].translation = translate;
|
||||
TRSData[i * num_poses + j].rotation = quaternion;
|
||||
TRSData[i * num_poses + j].scaling = scale;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,7 +214,7 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
if (num_frames <= 0)
|
||||
{
|
||||
num_frames = 1;
|
||||
FrameTransforms.Resize(num_joints);
|
||||
TRSData.Resize(num_joints);
|
||||
|
||||
for (uint32_t j = 0; j < num_joints; j++)
|
||||
{
|
||||
|
@ -265,33 +235,9 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
scale.Y = Joints[j].Scale.Y;
|
||||
scale.Z = Joints[j].Scale.Z;
|
||||
|
||||
VSMatrix m;
|
||||
m.loadIdentity();
|
||||
m.translate(translate.X, translate.Y, translate.Z);
|
||||
m.multQuaternion(quaternion);
|
||||
m.scale(scale.X, scale.Y, scale.Z);
|
||||
|
||||
VSMatrix& result = FrameTransforms[j];
|
||||
if (Joints[j].Parent >= 0)
|
||||
{
|
||||
result = baseframe[Joints[j].Parent];
|
||||
result.multMatrix(m);
|
||||
result.multMatrix(inversebaseframe[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = m;
|
||||
result.multMatrix(inversebaseframe[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < num_joints; j++)
|
||||
{
|
||||
VSMatrix m;
|
||||
m.loadMatrix(swapYZ);
|
||||
m.multMatrix(FrameTransforms[j]);
|
||||
m.multMatrix(swapYZ);
|
||||
FrameTransforms[j] = m;
|
||||
TRSData[j].translation = translate;
|
||||
TRSData[j].rotation = quaternion;
|
||||
TRSData[j].scaling = scale;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,24 +457,25 @@ void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int fra
|
|||
{
|
||||
meshSkin = TexMan.GetGameTexture(surfaceskinids[i], true);
|
||||
}
|
||||
else if (!Meshes[i].Skin.isValid())
|
||||
else if (Meshes[i].Skin.isValid())
|
||||
{
|
||||
meshSkin = TexMan.GetGameTexture(Meshes[i].Skin, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshSkin = TexMan.GetGameTexture(Meshes[i].Skin, true);
|
||||
}
|
||||
if (!meshSkin) continue;
|
||||
}
|
||||
|
||||
if (meshSkin != lastSkin)
|
||||
if (meshSkin->isValid())
|
||||
{
|
||||
renderer->SetMaterial(meshSkin, false, translation);
|
||||
lastSkin = meshSkin;
|
||||
if (meshSkin != lastSkin)
|
||||
{
|
||||
renderer->SetMaterial(meshSkin, false, translation);
|
||||
lastSkin = meshSkin;
|
||||
}
|
||||
renderer->DrawElements(Meshes[i].NumTriangles * 3, Meshes[i].FirstTriangle * 3 * sizeof(unsigned int));
|
||||
}
|
||||
|
||||
renderer->DrawElements(Meshes[i].NumTriangles * 3, Meshes[i].FirstTriangle * 3 * sizeof(unsigned int));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,49 +509,99 @@ void IQMModel::AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids)
|
|||
}
|
||||
}
|
||||
|
||||
const TArray<VSMatrix>* IQMModel::AttachAnimationData()
|
||||
const TArray<TRS>* IQMModel::AttachAnimationData()
|
||||
{
|
||||
return &FrameTransforms;
|
||||
return &TRSData;
|
||||
}
|
||||
|
||||
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData)
|
||||
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>& animationData, DBoneComponents* boneComponentData, int index)
|
||||
{
|
||||
const TArray<VSMatrix>& animationFrames = &animationData ? animationData : FrameTransforms;
|
||||
|
||||
int numbones = Joints.Size();
|
||||
|
||||
frame1 = clamp(frame1, 0, ((int)animationFrames.Size() - 1) / numbones);
|
||||
frame2 = clamp(frame2, 0, ((int)animationFrames.Size() - 1) / numbones);
|
||||
|
||||
int offset1 = frame1 * numbones;
|
||||
int offset2 = frame2 * numbones;
|
||||
float t = (float)inter;
|
||||
float invt = 1.0f - t;
|
||||
|
||||
TArray<VSMatrix> bones(numbones, true);
|
||||
for (int i = 0; i < numbones; i++)
|
||||
const TArray<TRS>& animationFrames = &animationData ? animationData : TRSData;
|
||||
if (Joints.Size() > 0)
|
||||
{
|
||||
const float* from = animationFrames[offset1 + i].get();
|
||||
const float* to = animationFrames[offset2 + i].get();
|
||||
int numbones = Joints.Size();
|
||||
|
||||
// Interpolate bone between the two frames
|
||||
float bone[16];
|
||||
for (int j = 0; j < 16; j++)
|
||||
if (boneComponentData->trscomponents[index].Size() != numbones)
|
||||
boneComponentData->trscomponents[index].Resize(numbones);
|
||||
if (boneComponentData->trsmatrix[index].Size() != numbones)
|
||||
boneComponentData->trsmatrix[index].Resize(numbones);
|
||||
|
||||
frame1 = clamp(frame1, 0, ((int)animationFrames.Size() - 1) / numbones);
|
||||
frame2 = clamp(frame2, 0, ((int)animationFrames.Size() - 1) / numbones);
|
||||
|
||||
int offset1 = frame1 * numbones;
|
||||
int offset2 = frame2 * numbones;
|
||||
float t = (float)inter;
|
||||
float invt = 1.0f - t;
|
||||
|
||||
float swapYZ[16] = { 0.0f };
|
||||
swapYZ[0 + 0 * 4] = 1.0f;
|
||||
swapYZ[1 + 2 * 4] = 1.0f;
|
||||
swapYZ[2 + 1 * 4] = 1.0f;
|
||||
swapYZ[3 + 3 * 4] = 1.0f;
|
||||
|
||||
TArray<VSMatrix> bones(numbones, true);
|
||||
TArray<bool> modifiedBone(numbones, true);
|
||||
for (int i = 0; i < numbones; i++)
|
||||
{
|
||||
bone[j] = from[j] * invt + to[j] * t;
|
||||
TRS bone;
|
||||
TRS from = animationFrames[offset1 + i];
|
||||
TRS to = animationFrames[offset2 + i];
|
||||
|
||||
bone.translation = from.translation * invt + to.translation * t;
|
||||
bone.rotation = from.rotation * invt;
|
||||
if ((bone.rotation | to.rotation * t) < 0)
|
||||
{
|
||||
bone.rotation.X *= -1; bone.rotation.Y *= -1; bone.rotation.Z *= -1; bone.rotation.W *= -1;
|
||||
}
|
||||
bone.rotation += to.rotation * t;
|
||||
bone.rotation.MakeUnit();
|
||||
bone.scaling = from.scaling * invt + to.scaling * t;
|
||||
|
||||
if (Joints[i].Parent >= 0 && modifiedBone[Joints[i].Parent])
|
||||
{
|
||||
boneComponentData->trscomponents[index][i] = bone;
|
||||
modifiedBone[i] = true;
|
||||
}
|
||||
else if (boneComponentData->trscomponents[index][i].Equals(bone))
|
||||
{
|
||||
bones[i] = boneComponentData->trsmatrix[index][i];
|
||||
modifiedBone[i] = false;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
boneComponentData->trscomponents[index][i] = bone;
|
||||
modifiedBone[i] = true;
|
||||
}
|
||||
|
||||
VSMatrix m;
|
||||
m.loadIdentity();
|
||||
m.translate(bone.translation.X, bone.translation.Y, bone.translation.Z);
|
||||
m.multQuaternion(bone.rotation);
|
||||
m.scale(bone.scaling.X, bone.scaling.Y, bone.scaling.Z);
|
||||
|
||||
VSMatrix& result = bones[i];
|
||||
if (Joints[i].Parent >= 0)
|
||||
{
|
||||
result = bones[Joints[i].Parent];
|
||||
result.multMatrix(swapYZ);
|
||||
result.multMatrix(baseframe[Joints[i].Parent]);
|
||||
result.multMatrix(m);
|
||||
result.multMatrix(inversebaseframe[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.loadMatrix(swapYZ);
|
||||
result.multMatrix(m);
|
||||
result.multMatrix(inversebaseframe[i]);
|
||||
}
|
||||
result.multMatrix(swapYZ);
|
||||
}
|
||||
|
||||
// Apply parent bone
|
||||
if (Joints[i].Parent >= 0)
|
||||
{
|
||||
bones[i] = bones[Joints[i].Parent];
|
||||
bones[i].multMatrix(bone);
|
||||
}
|
||||
else
|
||||
{
|
||||
bones[i].loadMatrix(bone);
|
||||
}
|
||||
boneComponentData->trsmatrix[index] = bones;
|
||||
|
||||
return bones;
|
||||
}
|
||||
|
||||
return bones;
|
||||
return {};
|
||||
}
|
|
@ -515,5 +515,4 @@ void *DObject::ScriptVar(FName field, PType *type)
|
|||
}
|
||||
// This is only for internal use so I_Error is fine.
|
||||
I_Error("Variable %s not found in %s\n", field.GetChars(), cls->TypeName.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -37,21 +37,19 @@
|
|||
#include <lmcons.h>
|
||||
#include <shlobj.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include "i_specialpaths.h"
|
||||
#include "printf.h"
|
||||
#include "cmdlib.h"
|
||||
#include "findfile.h"
|
||||
#include "version.h" // for GAMENAME
|
||||
#include "gstrings.h"
|
||||
#include "i_mainwindow.h"
|
||||
#include "engineerrors.h"
|
||||
|
||||
// Vanilla MinGW does not have folder ids
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
static const GUID FOLDERID_LocalAppData = { 0xf1b32785, 0x6fba, 0x4fcf, 0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91 };
|
||||
static const GUID FOLDERID_RoamingAppData = { 0x3eb685db, 0x65f9, 0x4cf6, 0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d };
|
||||
static const GUID FOLDERID_SavedGames = { 0x4c5c32ff, 0xbb9d, 0x43b0, 0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4 };
|
||||
static const GUID FOLDERID_Documents = { 0xfdd39ad0, 0x238f, 0x46af, 0xad, 0xb4, 0x6c, 0x85, 0x48, 0x03, 0x69, 0xc7 };
|
||||
static const GUID FOLDERID_Pictures = { 0x33e28130, 0x4e1e, 0x4676, 0x83, 0x5a, 0x98, 0x39, 0x5c, 0x3b, 0xc3, 0xbb };
|
||||
#endif
|
||||
|
||||
static int isportable = -1;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
@ -62,18 +60,18 @@ static const GUID FOLDERID_Pictures = { 0x33e28130, 0x4e1e, 0x4676, 0x83, 0x5a,
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
bool UseKnownFolders()
|
||||
bool IsPortable()
|
||||
{
|
||||
// Cache this value so the semantics don't change during a single run
|
||||
// of the program. (e.g. Somebody could add write access while the
|
||||
// program is running.)
|
||||
static int iswritable = -1;
|
||||
HANDLE file;
|
||||
|
||||
if (iswritable >= 0)
|
||||
if (isportable >= 0)
|
||||
{
|
||||
return !iswritable;
|
||||
return !!isportable;
|
||||
}
|
||||
|
||||
// Consider 'Program Files' read only without actually checking.
|
||||
bool found = false;
|
||||
for (auto p : { L"ProgramFiles", L"ProgramFiles(x86)" })
|
||||
|
@ -85,50 +83,51 @@ bool UseKnownFolders()
|
|||
FixPathSeperator(envpath);
|
||||
if (progdir.MakeLower().IndexOf(envpath.MakeLower()) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
isportable = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
// A portable INI means that this storage location should also be portable if the file can be written to.
|
||||
FStringf path("%s" GAMENAME "_portable.ini", progdir.GetChars());
|
||||
if (FileExists(path))
|
||||
{
|
||||
std::wstring testpath = progdir.WideString() + L"writest";
|
||||
file = CreateFile(testpath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
||||
file = CreateFile(path.WideString().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file);
|
||||
if (!batchrun) Printf("Using program directory for storage\n");
|
||||
iswritable = true;
|
||||
return false;
|
||||
if (!batchrun) Printf("Using portable configuration\n");
|
||||
isportable = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!batchrun) Printf("Using known folders for storage\n");
|
||||
iswritable = false;
|
||||
return true;
|
||||
|
||||
isportable = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// GetKnownFolder
|
||||
//
|
||||
// Returns the known_folder if SHGetKnownFolderPath is available, otherwise
|
||||
// returns the shell_folder using SHGetFolderPath.
|
||||
// Returns the known_folder from SHGetKnownFolderPath
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create, FString &path)
|
||||
FString GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create)
|
||||
{
|
||||
PWSTR wpath;
|
||||
if (FAILED(SHGetKnownFolderPath(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath)))
|
||||
{
|
||||
return false;
|
||||
// This should never be triggered unless the OS was compromised
|
||||
I_FatalError("Unable to retrieve known folder.");
|
||||
}
|
||||
path = wpath;
|
||||
FString path = FString(wpath);
|
||||
FixPathSeperator(path);
|
||||
CoTaskMemFree(wpath);
|
||||
return true;
|
||||
return path;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
@ -141,14 +140,9 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create
|
|||
|
||||
FString M_GetAppDataPath(bool create)
|
||||
{
|
||||
FString path;
|
||||
FString path = GetKnownFolder(CSIDL_LOCAL_APPDATA, FOLDERID_LocalAppData, create);
|
||||
|
||||
if (!GetKnownFolder(CSIDL_LOCAL_APPDATA, FOLDERID_LocalAppData, create, path))
|
||||
{ // Failed (e.g. On Win9x): use program directory
|
||||
path = progdir;
|
||||
}
|
||||
path += "/" GAMENAMELOWERCASE;
|
||||
path.Substitute("//", "/"); // needed because progdir ends with a slash.
|
||||
if (create)
|
||||
{
|
||||
CreatePath(path);
|
||||
|
@ -166,16 +160,11 @@ FString M_GetAppDataPath(bool create)
|
|||
|
||||
FString M_GetCachePath(bool create)
|
||||
{
|
||||
FString path;
|
||||
FString path = GetKnownFolder(CSIDL_LOCAL_APPDATA, FOLDERID_LocalAppData, create);
|
||||
|
||||
if (!GetKnownFolder(CSIDL_LOCAL_APPDATA, FOLDERID_LocalAppData, create, path))
|
||||
{ // Failed (e.g. On Win9x): use program directory
|
||||
path = progdir;
|
||||
}
|
||||
// Don't use GAME_DIR and such so that ZDoom and its child ports can
|
||||
// share the node cache.
|
||||
path += "/zdoom/cache";
|
||||
path.Substitute("//", "/"); // needed because progdir ends with a slash.
|
||||
if (create)
|
||||
{
|
||||
CreatePath(path);
|
||||
|
@ -196,6 +185,84 @@ FString M_GetAutoexecPath()
|
|||
return "$PROGDIR/autoexec.cfg";
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// M_GetOldConfigPath
|
||||
//
|
||||
// Check if we have a config in a place that's no longer used.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FString M_GetOldConfigPath(int& type)
|
||||
{
|
||||
FString path;
|
||||
HRESULT hr;
|
||||
|
||||
// construct "$PROGDIR/-$USER.ini"
|
||||
WCHAR uname[UNLEN + 1];
|
||||
DWORD unamelen = UNLEN;
|
||||
|
||||
path = progdir;
|
||||
hr = GetUserNameW(uname, &unamelen);
|
||||
if (SUCCEEDED(hr) && uname[0] != 0)
|
||||
{
|
||||
// Is it valid for a user name to have slashes?
|
||||
// Check for them and substitute just in case.
|
||||
auto probe = uname;
|
||||
while (*probe != 0)
|
||||
{
|
||||
if (*probe == '\\' || *probe == '/')
|
||||
*probe = '_';
|
||||
++probe;
|
||||
}
|
||||
path << GAMENAMELOWERCASE "-" << FString(uname) << ".ini";
|
||||
type = 0;
|
||||
if (FileExists(path))
|
||||
return path;
|
||||
}
|
||||
|
||||
// Check in app data where this was previously stored.
|
||||
// We actually prefer to store the config in a more visible place so this is no longer used.
|
||||
path = GetKnownFolder(CSIDL_APPDATA, FOLDERID_RoamingAppData, true);
|
||||
path += "/" GAME_DIR "/" GAMENAMELOWERCASE ".ini";
|
||||
type = 1;
|
||||
if (FileExists(path))
|
||||
return path;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// M_MigrateOldConfig
|
||||
//
|
||||
// Ask the user what to do with their old config.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int M_MigrateOldConfig()
|
||||
{
|
||||
int selection = IDCANCEL;
|
||||
auto globalstr = L"Move to Users/ folder";
|
||||
auto portablestr = L"Convert to portable installation";
|
||||
auto cancelstr = L"Cancel";
|
||||
auto titlestr = L"Migrate existing configuration";
|
||||
auto infostr = L"" GAMENAME " found a user specific config in the game folder";
|
||||
const TASKDIALOG_BUTTON buttons[] = { {IDYES, globalstr}, {IDNO, portablestr}, {IDCANCEL, cancelstr} };
|
||||
TASKDIALOGCONFIG taskDialogConfig = {};
|
||||
taskDialogConfig.cbSize = sizeof(TASKDIALOGCONFIG);
|
||||
taskDialogConfig.pszMainIcon = TD_WARNING_ICON;
|
||||
taskDialogConfig.pButtons = buttons;
|
||||
taskDialogConfig.cButtons = countof(buttons);
|
||||
taskDialogConfig.pszWindowTitle = titlestr;
|
||||
taskDialogConfig.pszContent = infostr;
|
||||
taskDialogConfig.hwndParent = mainwindow.GetHandle();
|
||||
taskDialogConfig.dwFlags = TDF_USE_COMMAND_LINKS;
|
||||
TaskDialogIndirect(&taskDialogConfig, &selection, NULL, NULL);
|
||||
if (selection == IDYES || selection == IDNO) return selection;
|
||||
throw CExitEvent(3);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// M_GetConfigPath Windows
|
||||
|
@ -208,51 +275,43 @@ FString M_GetAutoexecPath()
|
|||
|
||||
FString M_GetConfigPath(bool for_reading)
|
||||
{
|
||||
FString path;
|
||||
HRESULT hr;
|
||||
|
||||
path.Format("%s" GAMENAMELOWERCASE "_portable.ini", progdir.GetChars());
|
||||
if (FileExists(path))
|
||||
if (IsPortable())
|
||||
{
|
||||
return path;
|
||||
return FStringf("%s" GAMENAMELOWERCASE "_portable.ini", progdir.GetChars());
|
||||
}
|
||||
path = "";
|
||||
|
||||
// Construct a user-specific config name
|
||||
if (UseKnownFolders() && GetKnownFolder(CSIDL_APPDATA, FOLDERID_RoamingAppData, true, path))
|
||||
FString path = GetKnownFolder(CSIDL_APPDATA, FOLDERID_Documents, true);
|
||||
path += "/My Games/" GAME_DIR;
|
||||
CreatePath(path);
|
||||
path += "/" GAMENAMELOWERCASE ".ini";
|
||||
if (!for_reading || FileExists(path))
|
||||
return path;
|
||||
|
||||
// No config was found in the accepted locations.
|
||||
// Look in previously valid places to see if we have something we can migrate
|
||||
|
||||
int type = 0;
|
||||
FString oldpath = M_GetOldConfigPath(type);
|
||||
if (!oldpath.IsEmpty())
|
||||
{
|
||||
path += "/" GAME_DIR;
|
||||
CreatePath(path);
|
||||
path += "/" GAMENAMELOWERCASE ".ini";
|
||||
}
|
||||
else
|
||||
{ // construct "$PROGDIR/-$USER.ini"
|
||||
WCHAR uname[UNLEN+1];
|
||||
DWORD unamelen = UNLEN;
|
||||
|
||||
path = progdir;
|
||||
hr = GetUserNameW(uname, &unamelen);
|
||||
if (SUCCEEDED(hr) && uname[0] != 0)
|
||||
if (type == 0)
|
||||
{
|
||||
// Is it valid for a user name to have slashes?
|
||||
// Check for them and substitute just in case.
|
||||
auto probe = uname;
|
||||
while (*probe != 0)
|
||||
// If we find a local per-user config, ask the user what to do with it.
|
||||
int action = M_MigrateOldConfig();
|
||||
if (action == IDNO)
|
||||
{
|
||||
if (*probe == '\\' || *probe == '/')
|
||||
*probe = '_';
|
||||
++probe;
|
||||
path.Format("%s" GAMENAMELOWERCASE "_portable.ini", progdir.GetChars());
|
||||
isportable = true;
|
||||
}
|
||||
path << GAMENAMELOWERCASE "-" << FString(uname) << ".ini";
|
||||
}
|
||||
else
|
||||
{ // Couldn't get user name, so just use base version.
|
||||
path += GAMENAMELOWERCASE ".ini";
|
||||
}
|
||||
bool res = MoveFileExW(WideString(oldpath).c_str(), WideString(path).c_str(), MOVEFILE_COPY_ALLOWED);
|
||||
if (res) return path;
|
||||
else return oldpath; // if we cannot move, just use the config where it was. It won't be written back, though and never be used again if a new one gets saved.
|
||||
}
|
||||
|
||||
// If we are reading the config file, check if it exists. If not, fallback
|
||||
// to base version.
|
||||
// Fall back to the global template if nothing was found.
|
||||
// If we are reading the config file, check if it exists. If not, fallback to base version.
|
||||
if (for_reading)
|
||||
{
|
||||
if (!FileExists(path))
|
||||
|
@ -273,30 +332,25 @@ FString M_GetConfigPath(bool for_reading)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
// I'm not sure when FOLDERID_Screenshots was added, but it was probably
|
||||
// for Windows 8, since it's not in the v7.0 Windows SDK.
|
||||
static const GUID MyFOLDERID_Screenshots = { 0xb7bede81, 0xdf94, 0x4682, 0xa7, 0xd8, 0x57, 0xa5, 0x26, 0x20, 0xb8, 0x6f };
|
||||
|
||||
FString M_GetScreenshotsPath()
|
||||
{
|
||||
FString path;
|
||||
|
||||
if (!UseKnownFolders())
|
||||
if (IsPortable())
|
||||
{
|
||||
path << progdir << "Screenshots/";
|
||||
}
|
||||
else if (GetKnownFolder(-1, MyFOLDERID_Screenshots, true, path))
|
||||
else if (IsWindows8OrGreater())
|
||||
{
|
||||
path = GetKnownFolder(-1, FOLDERID_Screenshots, true);
|
||||
|
||||
path << "/" GAMENAME "/";
|
||||
}
|
||||
else if (GetKnownFolder(CSIDL_MYPICTURES, FOLDERID_Pictures, true, path))
|
||||
else
|
||||
{
|
||||
path = GetKnownFolder(CSIDL_MYPICTURES, FOLDERID_Pictures, true);
|
||||
path << "/Screenshots/" GAMENAME "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
path << progdir << "/Screenshots/";
|
||||
}
|
||||
CreatePath(path);
|
||||
return path;
|
||||
}
|
||||
|
@ -313,27 +367,16 @@ FString M_GetSavegamesPath()
|
|||
{
|
||||
FString path;
|
||||
|
||||
if (!UseKnownFolders())
|
||||
if (IsPortable())
|
||||
{
|
||||
path << progdir << "Save/";
|
||||
}
|
||||
// Try standard Saved Games folder
|
||||
else if (GetKnownFolder(-1, FOLDERID_SavedGames, true, path))
|
||||
{
|
||||
path << "/" GAMENAME "/";
|
||||
}
|
||||
// Try defacto My Documents/My Games folder
|
||||
else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
|
||||
{
|
||||
// I assume since this isn't a standard folder, it doesn't have
|
||||
// a localized name either.
|
||||
path << "/My Games/" GAMENAME "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
path << progdir << "Save/";
|
||||
path = GetKnownFolder(-1, FOLDERID_SavedGames, true);
|
||||
path << "/" GAMENAME "/";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -349,29 +392,18 @@ FString M_GetDocumentsPath()
|
|||
{
|
||||
FString path;
|
||||
|
||||
// A portable INI means that this storage location should also be portable.
|
||||
path.Format("%s" GAMENAME "_portable.ini", progdir.GetChars());
|
||||
if (FileExists(path))
|
||||
{
|
||||
return progdir;
|
||||
}
|
||||
|
||||
if (!UseKnownFolders())
|
||||
if (IsPortable())
|
||||
{
|
||||
return progdir;
|
||||
}
|
||||
// Try defacto My Documents/My Games folder
|
||||
else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
|
||||
else
|
||||
{
|
||||
// I assume since this isn't a standard folder, it doesn't have
|
||||
// a localized name either.
|
||||
// I assume since this isn't a standard folder, it doesn't have a localized name either.
|
||||
path = GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true);
|
||||
path << "/My Games/" GAMENAME "/";
|
||||
CreatePath(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = progdir;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
@ -388,23 +420,17 @@ FString M_GetDemoPath()
|
|||
FString path;
|
||||
|
||||
// A portable INI means that this storage location should also be portable.
|
||||
FStringf inipath("%s" GAMENAME "_portable.ini", progdir.GetChars());
|
||||
if (FileExists(inipath) || !UseKnownFolders())
|
||||
if (IsPortable())
|
||||
{
|
||||
path << progdir << "Demos/";
|
||||
}
|
||||
else
|
||||
// Try defacto My Documents/My Games folder
|
||||
if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
|
||||
{
|
||||
// I assume since this isn't a standard folder, it doesn't have
|
||||
// a localized name either.
|
||||
// I assume since this isn't a standard folder, it doesn't have a localized name either.
|
||||
path = GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true);
|
||||
path << "/My Games/" GAMENAME "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
path << progdir << "Demos/";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
|
|
@ -170,6 +170,8 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
|||
mSceneViewport = *bounds;
|
||||
mScreenViewport = *bounds;
|
||||
mOutputLetterbox = *bounds;
|
||||
mGameScreenWidth = mScreenViewport.width;
|
||||
mGameScreenHeight = mScreenViewport.height;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -216,6 +218,9 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
|||
mSceneViewport.width = (int)round(mSceneViewport.width * scaleX);
|
||||
mSceneViewport.height = (int)round(mSceneViewport.height * scaleY);
|
||||
}
|
||||
|
||||
mGameScreenWidth = GetWidth();
|
||||
mGameScreenHeight = GetHeight();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
@ -226,12 +231,12 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
|||
|
||||
int DFrameBuffer::ScreenToWindowX(int x)
|
||||
{
|
||||
return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)GetWidth());
|
||||
return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)mGameScreenWidth);
|
||||
}
|
||||
|
||||
int DFrameBuffer::ScreenToWindowY(int y)
|
||||
{
|
||||
return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)GetHeight());
|
||||
return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)mGameScreenHeight);
|
||||
}
|
||||
|
||||
void DFrameBuffer::ScaleCoordsFromWindow(int16_t &x, int16_t &y)
|
||||
|
|
|
@ -147,6 +147,8 @@ public:
|
|||
BoneBuffer* mBones = nullptr; // Model bones
|
||||
IShadowMap mShadowMap;
|
||||
|
||||
int mGameScreenWidth = 0;
|
||||
int mGameScreenHeight = 0;
|
||||
IntRect mScreenViewport;
|
||||
IntRect mSceneViewport;
|
||||
IntRect mOutputLetterbox;
|
||||
|
|
|
@ -206,7 +206,7 @@ void VulkanFrameBuffer::RenderTextureView(FCanvasTexture* tex, std::function<voi
|
|||
mRenderState->EndRenderPass();
|
||||
|
||||
VkImageTransition()
|
||||
.AddImage(image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true)
|
||||
.AddImage(image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, false)
|
||||
.Execute(mCommands->GetDrawCommands());
|
||||
|
||||
mRenderState->SetRenderTarget(image, depthStencil->View.get(), image->Image->width, image->Image->height, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT);
|
||||
|
|
|
@ -110,8 +110,8 @@ public:
|
|||
};
|
||||
|
||||
// This must be a separate function because the VC compiler would otherwise allocate memory on the stack for every separate instance of the exception object that may get thrown.
|
||||
void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...);
|
||||
void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException reason, const char *moreinfo, ...);
|
||||
[[noreturn]] void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...);
|
||||
[[noreturn]] void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException reason, const char *moreinfo, ...);
|
||||
|
||||
void ClearGlobalVMStack();
|
||||
|
||||
|
@ -496,6 +496,7 @@ inline int VMCallAction(VMFunction *func, VMValue *params, int numparams, VMRetu
|
|||
|
||||
// Use these to collect the parameters in a native function.
|
||||
// variable name <x> at position <p>
|
||||
[[noreturn]]
|
||||
void NullParam(const char *varname);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -688,14 +688,14 @@ void CVMAbortException::MaybePrintMessage()
|
|||
}
|
||||
|
||||
|
||||
void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...)
|
||||
[[noreturn]] void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, moreinfo);
|
||||
throw CVMAbortException(reason, moreinfo, ap);
|
||||
}
|
||||
|
||||
void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException reason, const char *moreinfo, ...)
|
||||
[[noreturn]] void ThrowAbortException(VMScriptFunction *sfunc, VMOP *line, EVMAbortException reason, const char *moreinfo, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, moreinfo);
|
||||
|
@ -711,10 +711,9 @@ DEFINE_ACTION_FUNCTION(DObject, ThrowAbortException)
|
|||
PARAM_PROLOGUE;
|
||||
FString s = FStringFormat(VM_ARGS_NAMES);
|
||||
ThrowAbortException(X_OTHER, s.GetChars());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NullParam(const char *varname)
|
||||
[[noreturn]] void NullParam(const char *varname)
|
||||
{
|
||||
ThrowAbortException(X_READ_NIL, "In function parameter %s", varname);
|
||||
}
|
||||
|
|
56
source/common/utility/TRS.h
Normal file
56
source/common/utility/TRS.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
** TRS
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2022 Andrew Clarke
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "vectors.h"
|
||||
|
||||
class TRS
|
||||
{
|
||||
public:
|
||||
FVector3 translation;
|
||||
FVector4 rotation;
|
||||
FVector3 scaling;
|
||||
|
||||
TRS()
|
||||
{
|
||||
translation = FVector3(0,0,0);
|
||||
rotation = FVector4(0,0,0,1);
|
||||
scaling = FVector3(0,0,0);
|
||||
}
|
||||
|
||||
bool Equals(TRS& compare)
|
||||
{
|
||||
return compare.translation == this->translation && compare.rotation == this->rotation && compare.scaling == this->scaling;
|
||||
}
|
||||
};
|
||||
|
|
@ -63,7 +63,7 @@ void I_DebugPrint(const char *cp)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void I_Error(const char *error, ...)
|
||||
[[noreturn]] void I_Error(const char *error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char errortext[MAX_ERRORTEXT];
|
||||
|
@ -85,7 +85,7 @@ void I_Error(const char *error, ...)
|
|||
//==========================================================================
|
||||
extern FILE *Logfile;
|
||||
|
||||
void I_FatalError(const char *error, ...)
|
||||
[[noreturn]] void I_FatalError(const char *error, ...)
|
||||
{
|
||||
static bool alreadyThrown = false;
|
||||
gameisdead = true;
|
||||
|
|
|
@ -110,7 +110,7 @@ public:
|
|||
};
|
||||
|
||||
void I_ShowFatalError(const char *message);
|
||||
void I_Error (const char *error, ...) GCCPRINTF(1,2);
|
||||
void I_FatalError (const char *error, ...) GCCPRINTF(1,2);
|
||||
[[noreturn]] void I_Error (const char *error, ...) GCCPRINTF(1,2);
|
||||
[[noreturn]] void I_FatalError (const char *error, ...) GCCPRINTF(1,2);
|
||||
|
||||
#endif //__ERRORS_H__
|
||||
|
|
|
@ -846,6 +846,22 @@ struct TVector4
|
|||
return TVector4(v.X * scalar, v.Y * scalar, v.Z * scalar, v.W * scalar);
|
||||
}
|
||||
|
||||
// Multiply as Quaternion
|
||||
TVector4& operator*= (const TVector4& v)
|
||||
{
|
||||
*this = *this * v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend TVector4 operator* (const TVector4& v1, const TVector4& v2)
|
||||
{
|
||||
return TVector4(v2.W * v1.X + v2.X * v1.W + v2.Y * v1.Z - v1.Z * v1.Y,
|
||||
v2.W * v1.Y + v2.Y * v1.W + v2.Z * v1.X - v2.X * v1.Z,
|
||||
v2.W * v1.Z + v2.Z * v1.W + v2.X * v1.Y - v2.Y * v1.X,
|
||||
v2.W * v1.W - v2.X * v1.X - v2.Y * v1.Y - v2.Z * v1.Z
|
||||
);
|
||||
}
|
||||
|
||||
// Scalar division
|
||||
TVector4 &operator/= (vec_t scalar)
|
||||
{
|
||||
|
|
|
@ -842,7 +842,7 @@ static TArray<GrpEntry> SetupGame()
|
|||
LumpFilter = usedgroups.Last().FileInfo.name;
|
||||
LumpFilter.StripChars(".:/\\<>?\"*| \t\r\n");
|
||||
}
|
||||
|
||||
SavegameFolder = LumpFilter;
|
||||
currentGame = LumpFilter;
|
||||
currentGame.Truncate(currentGame.IndexOf("."));
|
||||
PClass::StaticInit();
|
||||
|
|
|
@ -393,47 +393,6 @@ int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
FString G_BuildSaveName (const char *prefix)
|
||||
{
|
||||
FString name;
|
||||
bool usefilter;
|
||||
|
||||
if (const char *const dir = Args->CheckValue("-savedir"))
|
||||
{
|
||||
name = dir;
|
||||
usefilter = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = **cl_savedir ? cl_savedir : M_GetSavegamesPath();
|
||||
usefilter = true;
|
||||
}
|
||||
|
||||
const size_t len = name.Len();
|
||||
if (len > 0)
|
||||
{
|
||||
name.Substitute("\\", "/");
|
||||
if (name[len - 1] != '/')
|
||||
name << '/';
|
||||
}
|
||||
|
||||
if (usefilter)
|
||||
name << LumpFilter << '/';
|
||||
|
||||
CreatePath(name);
|
||||
|
||||
name << prefix;
|
||||
if (!strchr(prefix, '.')) name << SAVEGAME_EXT; // only add an extension if the prefix doesn't have one already.
|
||||
name = NicePath(name);
|
||||
name.Substitute("\\", "/");
|
||||
return name;
|
||||
}
|
||||
|
||||
#include "build.h"
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
class FileReader;
|
||||
extern int SaveVersion;
|
||||
|
||||
FString G_BuildSaveName (const char *prefix);
|
||||
int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu);
|
||||
|
||||
void G_LoadGame(const char* filename, bool hidecon = false);
|
||||
|
@ -19,9 +18,6 @@ void G_DoLoadGame();
|
|||
|
||||
void M_Autosave();
|
||||
|
||||
#define SAVEGAME_EXT ".dsave"
|
||||
|
||||
|
||||
template<> inline FSerializer& Serialize(FSerializer& arc, const char* keyname, sectortype*& w, sectortype** def)
|
||||
{
|
||||
assert(arc.isReading() || w == nullptr || (w >= §or[0] && w <= §or.Last()));
|
||||
|
|
|
@ -69,6 +69,8 @@ const char *GetVersionString();
|
|||
#define SAVESIG_SW GAMENAME ".ShadowWarrior"
|
||||
#define SAVESIG_PS GAMENAME ".Exhumed"
|
||||
|
||||
#define SAVEGAME_EXT "dsave"
|
||||
|
||||
#define MINSAVEVER_DN3D 16
|
||||
#define MINSAVEVER_BLD 16
|
||||
#define MINSAVEVER_SW 17
|
||||
|
|
Loading…
Reference in a new issue