backend update from GZDoom.

This commit is contained in:
Christoph Oelckers 2025-01-20 14:47:21 +01:00
parent b0e8aae59f
commit 3590de2f2a
44 changed files with 645 additions and 168 deletions

View file

@ -43,6 +43,8 @@
#include "s_soundinternal.h"
#include "i_time.h"
EXTERN_CVAR(Bool, cl_capfps)
class FBurnTexture : public FTexture
{
TArray<uint32_t> WorkBuffer;
@ -163,6 +165,8 @@ protected:
public:
virtual ~Wiper();
virtual bool Run(int ticks) = 0;
virtual bool RunInterpolated(double ticks) { return true; };
virtual bool Interpolatable() { return false; }
virtual void SetTextures(FGameTexture* startscreen, FGameTexture* endscreen)
{
startScreen = startscreen;
@ -177,9 +181,11 @@ class Wiper_Crossfade : public Wiper
{
public:
bool Run(int ticks) override;
bool RunInterpolated(double ticks) override;
bool Interpolatable() override { return true; }
private:
int Clock = 0;
float Clock = 0;
};
class Wiper_Melt : public Wiper
@ -187,10 +193,12 @@ class Wiper_Melt : public Wiper
public:
Wiper_Melt();
bool Run(int ticks) override;
bool RunInterpolated(double ticks) override;
bool Interpolatable() override { return true; }
private:
enum { WIDTH = 320, HEIGHT = 200 };
int y[WIDTH];
double y[WIDTH];
};
class Wiper_Burn : public Wiper
@ -268,7 +276,23 @@ bool Wiper_Crossfade::Run(int ticks)
Clock += ticks;
DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
return Clock >= 32;
return Clock >= 32.;
}
//==========================================================================
//
// OpenGLFrameBuffer :: Wiper_Crossfade :: Run
//
// Fades the old screen into the new one over 32 ticks.
//
//==========================================================================
bool Wiper_Crossfade::RunInterpolated(double ticks)
{
Clock += ticks;
DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
return Clock >= 32.;
}
//==========================================================================
@ -282,7 +306,7 @@ Wiper_Melt::Wiper_Melt()
y[0] = -(M_Random() & 15);
for (int i = 1; i < WIDTH; ++i)
{
y[i] = clamp(y[i-1] + (M_Random() % 3) - 1, -15, 0);
y[i] = clamp(y[i-1] + (double)(M_Random() % 3) - 1., -15., 0.);
}
}
@ -307,25 +331,25 @@ bool Wiper_Melt::Run(int ticks)
{
if (y[i] < HEIGHT)
{
if (y[i] < 0)
y[i]++;
else if (y[i] < 16)
y[i] += y[i] + 1;
if (y[i] < 0.)
y[i] = y[i] + 1.;
else if (y[i] < 16.)
y[i] += y[i] + 1.;
else
y[i] = min<int>(y[i] + 8, HEIGHT);
y[i] = min<double>(y[i] + 8., HEIGHT);
done = false;
}
if (ticks == 0)
{
struct {
int32_t x;
int32_t y;
double y;
} dpt;
struct {
int32_t left;
int32_t top;
double top;
int32_t right;
int32_t bottom;
double bottom;
} rect;
// Only draw for the final tick.
@ -333,7 +357,7 @@ bool Wiper_Melt::Run(int ticks)
int w = startScreen->GetTexelWidth();
int h = startScreen->GetTexelHeight();
dpt.x = i * w / WIDTH;
dpt.y = max(0, y[i] * h / HEIGHT);
dpt.y = max(0., y[i] * (double)h / (double)HEIGHT);
rect.left = dpt.x;
rect.top = 0;
rect.right = (i + 1) * w / WIDTH;
@ -348,6 +372,77 @@ bool Wiper_Melt::Run(int ticks)
return done;
}
//==========================================================================
//
// Wiper_Melt :: RunInterpolated
//
// Melts the old screen into the new one over 32 ticks (interpolated).
//
//==========================================================================
bool Wiper_Melt::RunInterpolated(double ticks)
{
bool done = false;
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
// Copy the old screen in vertical strips on top of the new one.
while (ticks > 0.)
{
done = true;
for (int i = 0; i < WIDTH; i++)
{
if (y[i] < (double)HEIGHT)
{
if (ticks > 0. && ticks < 1.)
{
if (y[i] < 0)
y[i] += ticks;
else if (y[i] < 16)
y[i] += (y[i] + 1) * ticks;
else
y[i] = min<double>(y[i] + (8 * ticks), (double)HEIGHT);
}
else if (y[i] < 0.)
y[i] = y[i] + 1.;
else if (y[i] < 16.)
y[i] += y[i] + 1.;
else
y[i] = min<double>(y[i] + 8., HEIGHT);
done = false;
}
}
ticks -= 1.;
}
for (int i = 0; i < WIDTH; i++)
{
struct {
int32_t x;
double y;
} dpt;
struct {
int32_t left;
double top;
int32_t right;
double bottom;
} rect;
// Only draw for the final tick.
int w = startScreen->GetTexelWidth();
double h = startScreen->GetTexelHeight();
dpt.x = i * w / WIDTH;
dpt.y = max(0., y[i] * (double)h / (double)HEIGHT);
rect.left = dpt.x;
rect.top = 0;
rect.right = (i + 1) * w / WIDTH;
rect.bottom = h - dpt.y;
if (rect.bottom > rect.top)
{
DrawTexture(twod, startScreen, 0, dpt.y, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_ClipLeft, rect.left, DTA_ClipRight, rect.right, DTA_Masked, false, TAG_DONE);
}
}
return done;
}
//==========================================================================
//
// OpenGLFrameBuffer :: Wiper_Burn Constructor
@ -423,6 +518,7 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
{
// wipe update
uint64_t wipestart, nowtime, diff;
double diff_frac;
bool done;
GSnd->SetSfxPaused(true, 1);
@ -438,20 +534,34 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
do
{
do
if (wiper->Interpolatable() && !cl_capfps)
{
I_WaitVBL(2);
nowtime = I_msTime();
diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow.
} while (diff < 1);
wipestart = nowtime;
twod->Begin(screen->GetWidth(), screen->GetHeight());
done = wiper->Run(1);
if (overlaydrawer) overlaydrawer();
twod->End();
screen->Update();
twod->OnFrameDone();
diff_frac = (nowtime - wipestart) * 40. / 1000.; // Using 35 here feels too slow.
wipestart = nowtime;
twod->Begin(screen->GetWidth(), screen->GetHeight());
done = wiper->RunInterpolated(diff_frac);
if (overlaydrawer) overlaydrawer();
twod->End();
screen->Update();
twod->OnFrameDone();
}
else
{
do
{
I_WaitVBL(2);
nowtime = I_msTime();
diff = (nowtime - wipestart) * 40 / 1000; // Using 35 here feels too slow.
} while (diff < 1);
wipestart = nowtime;
twod->Begin(screen->GetWidth(), screen->GetHeight());
done = wiper->Run(1);
if (overlaydrawer) overlaydrawer();
twod->End();
screen->Update();
twod->OnFrameDone();
}
} while (!done);
delete wiper;
I_FreezeTime(false);

View file

@ -61,7 +61,7 @@ enum
{
DEFAULT_PITCH = 128,
};
static FRandom pr_soundpitch ("SoundPitch");
static FCRandom pr_soundpitch ("SoundPitch");
SoundEngine* soundEngine;
//==========================================================================

View file

@ -193,7 +193,7 @@ int FCommandLine::argc ()
return _argc;
}
char *FCommandLine::operator[] (int i)
const char *FCommandLine::operator[] (int i)
{
if (_argv == NULL)
{

View file

@ -44,7 +44,7 @@ public:
FCommandLine (const char *commandline, bool no_escapes = false);
~FCommandLine ();
int argc ();
char *operator[] (int i);
const char *operator[] (int i);
const char *args () { return cmd; }
void Shift();

View file

@ -240,6 +240,16 @@ void* FBaseCVar::GetExtraDataPointer()
return m_ExtraDataPointer;
}
void FBaseCVar::SetExtraDataPointer2(void *pointer)
{
m_ExtraDataPointer2 = pointer;
}
void* FBaseCVar::GetExtraDataPointer2()
{
return m_ExtraDataPointer2;
}
const char *FBaseCVar::GetHumanString(int precision) const
{
return GetGenericRep(CVAR_String).String;

View file

@ -222,8 +222,10 @@ public:
void ClearCallback();
void SetExtraDataPointer(void *pointer);
void SetExtraDataPointer2(void *pointer);
void* GetExtraDataPointer();
void* GetExtraDataPointer2();
int pnum = -1;
FName userinfoName;
@ -259,7 +261,8 @@ private:
static inline bool m_UseCallback = false;
static inline bool m_DoNoSet = false;
void *m_ExtraDataPointer;
void *m_ExtraDataPointer = nullptr;
void *m_ExtraDataPointer2 = nullptr;
// These need to go away!
friend FString C_GetMassCVarString (uint32_t filter, bool compact);
@ -275,6 +278,8 @@ private:
friend void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32_t filter);
friend void C_DeinitConsole();
friend void C_ListCVarsWithoutDescription();
friend class GLDefsParser;
};
// Returns a string with all cvars whose flags match filter. In compact mode,

View file

@ -132,7 +132,8 @@ enum
PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt
PRE_ALLFULL, // Sent from host to an unwanted guest
PRE_ALLHEREACK, // Sent from guest to host to acknowledge PRE_ALLHEREACK receipt
PRE_GO // Sent from host to guest to continue game startup
PRE_GO, // Sent from host to guest to continue game startup
PRE_IN_PROGRESS, // Sent from host to guest if the game has already started
};
// Set PreGamePacket.fake to this so that the game rejects any pregame packets
@ -269,6 +270,8 @@ void PacketSend (void)
// I_Error ("SendPacket error: %s",strerror(errno));
}
void PreSend(const void* buffer, int bufferlen, const sockaddr_in* to);
void SendConAck(int num_connected, int num_needed);
//
// PacketGet
@ -303,7 +306,7 @@ void PacketGet (void)
GetPlayerName(node).GetChars());
}
doomcom.data[0] = 0x80; // NCMD_EXIT
doomcom.data[0] = NCMD_EXIT;
c = 1;
}
else if (err != WSAEWOULDBLOCK)
@ -341,10 +344,11 @@ void PacketGet (void)
}
else if (c > 0)
{ //The packet is not from any in-game node, so we might as well discard it.
// Don't show the message for disconnect notifications.
if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT)
if (TransmitBuffer[0] == PRE_FAKE)
{
DPrintf(DMSG_WARNING, "Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port);
// If it's someone waiting in the lobby, let them know the game already started
uint8_t msg[] = { PRE_FAKE, PRE_IN_PROGRESS };
PreSend(msg, 2, &fromaddress);
}
doomcom.remotenode = -1;
return;
@ -369,7 +373,22 @@ sockaddr_in *PreGet (void *buffer, int bufferlen, bool noabort)
int err = WSAGetLastError();
if (err == WSAEWOULDBLOCK || (noabort && err == WSAECONNRESET))
return NULL; // no packet
I_Error ("PreGet: %s", neterror ());
if (doomcom.consoleplayer == 0)
{
int node = FindNode(&fromaddress);
I_NetMessage("Got unexpected disconnect.");
doomcom.numnodes--;
for (; node < doomcom.numnodes; ++node)
sendaddress[node] = sendaddress[node + 1];
// Let remaining guests know that somebody left.
SendConAck(doomcom.numnodes, doomcom.numplayers);
}
else
{
I_NetError("The host disbanded the game unexpectedly");
}
}
return &fromaddress;
}
@ -499,7 +518,7 @@ void SendAbort (void)
}
}
static void SendConAck (int num_connected, int num_needed)
void SendConAck (int num_connected, int num_needed)
{
PreGamePacket packet;
@ -708,7 +727,7 @@ bool HostGame (int i)
doomcom.numnodes = 1;
I_NetInit ("Waiting for players", numplayers);
I_NetInit ("Hosting game", numplayers);
// Wait for numplayers-1 different connections
if (!I_NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers))
@ -783,13 +802,15 @@ bool Guest_ContactHost (void *userdata)
}
else if (packet.Message == PRE_DISCONNECT)
{
doomcom.numnodes = 0;
I_FatalError ("The host cancelled the game.");
I_NetError("The host cancelled the game.");
}
else if (packet.Message == PRE_ALLFULL)
{
doomcom.numnodes = 0;
I_FatalError ("The game is full.");
I_NetError("The game is full.");
}
else if (packet.Message == PRE_IN_PROGRESS)
{
I_NetError("The game was already started.");
}
}
}
@ -850,7 +871,7 @@ bool Guest_WaitForOthers (void *userdata)
return true;
case PRE_DISCONNECT:
I_FatalError ("The host cancelled the game.");
I_NetError("The host cancelled the game.");
break;
}
}
@ -875,6 +896,7 @@ bool JoinGame (int i)
BuildAddress (&sendaddress[1], Args->GetArg(i+1));
sendplayer[1] = 0;
doomcom.numnodes = 2;
doomcom.consoleplayer = -1;
// Let host know we are here
@ -1046,6 +1068,13 @@ void I_NetMessage(const char* text, ...)
#endif
}
void I_NetError(const char* error)
{
doomcom.numnodes = 0;
StartWindow->NetClose();
I_FatalError("%s", error);
}
// todo: later these must be dispatched by the main menu, not the start screen.
void I_NetProgress(int val)
{

View file

@ -7,6 +7,7 @@
int I_InitNetwork (void);
void I_NetCmd (void);
void I_NetMessage(const char*, ...);
void I_NetError(const char* error);
void I_NetProgress(int val);
void I_NetInit(const char* msg, int num);
bool I_NetLoop(bool (*timer_callback)(void*), void* userdata);

View file

@ -83,7 +83,7 @@ FRandom pr_exrandom("EX_Random");
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FRandom M_Random;
FCRandom M_Random;
// Global seed. This is modified predictably to initialize every RNG.
uint32_t rngseed;
@ -126,8 +126,8 @@ CCMD(rngseed)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
FRandom *FRandom::RNGList;
static TDeletingArray<FRandom *> NewRNGs;
FRandom *FRandom::RNGList, *FRandom::CRNGList;
static TDeletingArray<FRandom *> NewRNGs, NewCRNGs;
// CODE --------------------------------------------------------------------
@ -139,14 +139,22 @@ static TDeletingArray<FRandom *> NewRNGs;
//
//==========================================================================
FRandom::FRandom ()
: NameCRC (0)
FRandom::FRandom (bool client)
: NameCRC (0), bClient(client)
{
#ifndef NDEBUG
Name = NULL;
#endif
Next = RNGList;
RNGList = this;
if (bClient)
{
Next = CRNGList;
CRNGList = this;
}
else
{
Next = RNGList;
RNGList = this;
}
Init(0);
}
@ -158,7 +166,7 @@ FRandom::FRandom ()
//
//==========================================================================
FRandom::FRandom (const char *name)
FRandom::FRandom (const char *name, bool client) : bClient(client)
{
NameCRC = CalcCRC32 ((const uint8_t *)name, (unsigned int)strlen (name));
#ifndef NDEBUG
@ -170,7 +178,7 @@ FRandom::FRandom (const char *name)
#endif
// Insert the RNG in the list, sorted by CRC
FRandom **prev = &RNGList, *probe = RNGList;
FRandom **prev = (bClient ? &CRNGList : &RNGList), * probe = (bClient ? CRNGList : RNGList);
while (probe != NULL && probe->NameCRC < NameCRC)
{
@ -205,8 +213,8 @@ FRandom::~FRandom ()
FRandom *last = NULL;
prev = &RNGList;
rng = RNGList;
prev = bClient ? &CRNGList : &RNGList;
rng = bClient ? CRNGList : RNGList;
while (rng != NULL && rng != this)
{
@ -237,6 +245,11 @@ void FRandom::StaticClearRandom ()
{
rng->Init(rngseed);
}
for (FRandom* rng = FRandom::CRNGList; rng != NULL; rng = rng->Next)
{
rng->Init(rngseed);
}
}
//==========================================================================
@ -345,15 +358,15 @@ void FRandom::StaticReadRNGState(FSerializer &arc)
//
//==========================================================================
FRandom *FRandom::StaticFindRNG (const char *name)
FRandom *FRandom::StaticFindRNG (const char *name, bool client)
{
uint32_t NameCRC = CalcCRC32 ((const uint8_t *)name, (unsigned int)strlen (name));
// Use the default RNG if this one happens to have a CRC of 0.
if (NameCRC == 0) return &pr_exrandom;
if (NameCRC == 0) return client ? &M_Random : &pr_exrandom;
// Find the RNG in the list, sorted by CRC
FRandom **prev = &RNGList, *probe = RNGList;
FRandom **prev = (client ? &CRNGList : &RNGList), *probe = (client ? CRNGList : RNGList);
while (probe != NULL && probe->NameCRC < NameCRC)
{
@ -364,14 +377,32 @@ FRandom *FRandom::StaticFindRNG (const char *name)
if (probe == NULL || probe->NameCRC != NameCRC)
{
// A matching RNG doesn't exist yet so create it.
probe = new FRandom(name);
probe = new FRandom(name, client);
// Store the new RNG for destruction when ZDoom quits.
NewRNGs.Push(probe);
if (client)
NewCRNGs.Push(probe);
else
NewRNGs.Push(probe);
}
return probe;
}
void FRandom::SaveRNGState(TArray<FRandom>& backups)
{
for (auto cur = RNGList; cur != nullptr; cur = cur->Next)
backups.Push(*cur);
}
void FRandom::RestoreRNGState(TArray<FRandom>& backups)
{
unsigned int i = 0u;
for (auto cur = RNGList; cur != nullptr; cur = cur->Next)
*cur = backups[i++];
backups.Clear();
}
//==========================================================================
//
// FRandom :: StaticPrintSeeds

View file

@ -44,9 +44,9 @@ class FSerializer;
class FRandom : public SFMTObj
{
public:
FRandom ();
FRandom (const char *name);
~FRandom ();
FRandom() : FRandom(false) {}
FRandom(const char* name) : FRandom(name, false) {}
~FRandom();
int Seed() const
{
@ -170,20 +170,34 @@ public:
static void StaticClearRandom ();
static void StaticReadRNGState (FSerializer &arc);
static void StaticWriteRNGState (FSerializer &file);
static FRandom *StaticFindRNG(const char *name);
static FRandom *StaticFindRNG(const char *name, bool client);
static void SaveRNGState(TArray<FRandom>& backups);
static void RestoreRNGState(TArray<FRandom>& backups);
#ifndef NDEBUG
static void StaticPrintSeeds ();
#endif
protected:
FRandom(bool client);
FRandom(const char* name, bool client);
private:
#ifndef NDEBUG
const char *Name;
#endif
FRandom *Next;
uint32_t NameCRC;
bool bClient;
static FRandom *RNGList;
static FRandom *RNGList, *CRNGList;
};
class FCRandom : public FRandom
{
public:
FCRandom() : FRandom(true) {}
FCRandom(const char* name) : FRandom(name, true) {}
};
extern uint32_t rngseed; // The starting seed (not part of state)
@ -193,6 +207,6 @@ extern bool use_staticrng;
// M_Random can be used for numbers that do not affect gameplay
extern FRandom M_Random;
extern FCRandom M_Random;
#endif

View file

@ -41,6 +41,12 @@ xx(Random2)
xx(RandomPick)
xx(FRandomPick)
xx(SetRandomSeed)
xx(CRandom)
xx(CFRandom)
xx(CRandom2)
xx(CRandomPick)
xx(CFRandomPick)
xx(CSetRandomSeed)
xx(BuiltinRandomSeed)
xx(BuiltinNew)
xx(GetClass)
@ -190,9 +196,15 @@ xx(TranslationID)
xx(Overlay)
xx(IsValid)
xx(IsNull)
xx(IsEmpty)
xx(IsFixed)
xx(IsKeep)
xx(Exists)
xx(SetInvalid)
xx(SetNull)
xx(SetEmpty)
xx(SetFixed)
xx(SetKeep)
xx(Key)
xx(Index)
xx(Find)

View file

@ -252,7 +252,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &
FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverride &mo, struct AnimModelOverride *def);
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnim &ao, ModelAnim *def);
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def);
FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
FSerializer &Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);

View file

@ -55,6 +55,7 @@ public:
virtual void NetInit(const char *message, int num_players) {}
virtual void NetProgress(int count) {}
virtual void NetDone() {}
virtual void NetClose() {}
virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata) { return false; }
virtual void AppendStatusLine(const char* status) {}
virtual void LoadingStatus(const char* message, int colors) {}
@ -74,6 +75,7 @@ public:
void NetProgress(int count);
void NetMessage(const char* format, ...); // cover for printf
void NetDone();
void NetClose();
bool NetLoop(bool (*timer_callback)(void*), void* userdata);
protected:
int NetMaxPos, NetCurPos;

View file

@ -42,8 +42,8 @@ public:
void SetMaxIwadNum(int x) { MaxIwadIndex = x; }
bool InitSingleFile(const char *filename, FileSystemMessageFunc Printf = nullptr);
bool InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false, FILE* hashfile = nullptr);
void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile);
bool InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false);
void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf);
int CheckIfResourceFileLoaded (const char *name) noexcept;
void AddAdditionalFile(const char* filename, FileReader* wadinfo = NULL) {}

View file

@ -238,7 +238,7 @@ bool FileSystem::InitSingleFile(const char* filename, FileSystemMessageFunc Prin
return InitMultipleFiles(filenames, nullptr, Printf);
}
bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates, FILE* hashfile)
bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates)
{
int numfiles;
@ -269,7 +269,7 @@ bool FileSystem::InitMultipleFiles (std::vector<std::string>& filenames, LumpFil
for(size_t i=0;i<filenames.size(); i++)
{
AddFile(filenames[i].c_str(), nullptr, filter, Printf, hashfile);
AddFile(filenames[i].c_str(), nullptr, filter, Printf);
if (i == (unsigned)MaxIwadIndex) MoveLumpsInFolder("after_iwad/");
std::string path = "filter/%s";
@ -327,7 +327,7 @@ int FileSystem::AddFromBuffer(const char* name, char* data, int size, int id, in
// [RH] Removed reload hack
//==========================================================================
void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile)
void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInfo* filter, FileSystemMessageFunc Printf)
{
int startlump;
bool isdir = false;
@ -396,49 +396,10 @@ void FileSystem::AddFile (const char *filename, FileReader *filer, LumpFilterInf
path += ':';
path += resfile->getName(i);
auto embedded = resfile->GetEntryReader(i, READER_CACHED);
AddFile(path.c_str(), &embedded, filter, Printf, hashfile);
AddFile(path.c_str(), &embedded, filter, Printf);
}
}
if (hashfile)
{
uint8_t cksum[16];
char cksumout[33];
memset(cksumout, 0, sizeof(cksumout));
if (filereader.isOpen())
{
filereader.Seek(0, FileReader::SeekSet);
md5Hash(filereader, cksum);
for (size_t j = 0; j < sizeof(cksum); ++j)
{
snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]);
}
fprintf(hashfile, "file: %s, hash: %s, size: %td\n", filename, cksumout, filereader.GetLength());
}
else
fprintf(hashfile, "file: %s, Directory structure\n", filename);
for (int i = 0; i < resfile->EntryCount(); i++)
{
int flags = resfile->GetEntryFlags(i);
if (!(flags & RESFF_EMBEDDED))
{
auto reader = resfile->GetEntryReader(i, READER_SHARED, 0);
md5Hash(filereader, cksum);
for (size_t j = 0; j < sizeof(cksum); ++j)
{
snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]);
}
fprintf(hashfile, "file: %s, lump: %s, hash: %s, size: %zu\n", filename, resfile->getName(i), cksumout, (uint64_t)resfile->Length(i));
}
}
}
return;
}
}

View file

@ -51,6 +51,7 @@
CVAR(String, save_dir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
FString SavegameFolder;
CVAR(Int, save_sort_order, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
//=============================================================================
//
@ -136,7 +137,19 @@ int FSavegameManagerBase::InsertSaveNode(FSaveGameNode *node)
//if (SaveGames[0] == &NewSaveNode) i++; // To not insert above the "new savegame" dummy entry.
for (; i < SaveGames.Size(); i++)
{
if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0)
bool sortcmp = false;
switch(save_sort_order)
{
case 1:
sortcmp = node->CreationTime.CompareNoCase(SaveGames[i]->CreationTime) > 0;
break;
default:
case 0:
sortcmp = node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0;
break;
}
if (SaveGames[i]->bOldVersion || sortcmp)
{
break;
}
@ -170,12 +183,18 @@ void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &tit
#endif
{
node->SaveTitle = title;
node->CreationTime = myasctime();
node->bOldVersion = false;
node->bMissingWads = false;
// refresh my game's position on the list (needed if time/name changed)
SaveGames.Delete(i);
int index = InsertSaveNode(node);
if (okForQuicksave)
{
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = i;
LastAccessed = LastSaved = index;
}
return;
}
@ -183,6 +202,7 @@ void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &tit
auto node = new FSaveGameNode;
node->SaveTitle = title;
node->CreationTime = myasctime();
node->Filename = file;
node->bOldVersion = false;
node->bMissingWads = false;

View file

@ -11,6 +11,7 @@ struct FSaveGameNode
{
FString SaveTitle;
FString Filename;
FString CreationTime;
bool bOldVersion = false;
bool bMissingWads = false;
bool bNoDelete = false;

View file

@ -66,6 +66,7 @@ public:
void NetInit(const char* message, int playerCount);
void NetProgress(int count);
void NetDone();
void NetClose();
private:
NSWindow* m_window;

View file

@ -531,3 +531,8 @@ void FConsoleWindow::NetDone()
m_netAbortButton = nil;
}
}
void FConsoleWindow::NetClose()
{
// TODO: Implement this
}

View file

@ -110,6 +110,11 @@ void FBasicStartupScreen::NetDone()
FConsoleWindow::GetInstance().NetDone();
}
void FBasicStartupScreen::NetClose()
{
FConsoleWindow::GetInstance().NetClose();
}
bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData)
{
while (true)

View file

@ -54,17 +54,6 @@ bool I_WriteIniFailed (const char* filename);
class FGameTexture;
bool I_SetCursor(FGameTexture *);
static inline char *strlwr(char *str)
{
char *ptr = str;
while(*ptr)
{
*ptr = tolower(*ptr);
++ptr;
}
return str;
}
inline int I_GetNumaNodeCount() { return 1; }
inline int I_GetNumaNodeThreadCount(int numaNode) { return std::max<int>(std::thread::hardware_concurrency(), 1); }
inline void I_SetThreadNumaNode(std::thread &thread, int numaNode) { }

View file

@ -308,7 +308,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad, int&
#ifdef __APPLE__
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
#else
return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, extraArgs);
return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, &extraArgs);
#endif
}

View file

@ -57,6 +57,7 @@ class FTTYStartupScreen : public FStartupScreen
void NetInit(const char *message, int num_players);
void NetProgress(int count);
void NetDone();
void NetClose();
bool NetLoop(bool (*timer_callback)(void *), void *userdata);
protected:
bool DidNetInit;
@ -237,6 +238,11 @@ void FTTYStartupScreen::NetProgress(int count)
}
}
void FTTYStartupScreen::NetClose()
{
// TODO: Implement this
}
//===========================================================================
//
// FTTYStartupScreen :: NetLoop

View file

@ -134,6 +134,39 @@ void I_SetIWADInfo()
{
}
//==========================================================================
//
// isConsoleApp()
//
// runtime detection to detect if this is a console subsystem app.
//
// the reason for doing this is because it is possible to edit a binary directly and change its subsystem
// type via hexedit so in order to gain flexibility it makes no sense to just compile out the unused code.
//
// we may plan to publish tools to allow users to do this manually on their own.
//
//==========================================================================
bool isConsoleApp()
{
static bool alreadychecked = false;
static bool returnvalue;
if (!alreadychecked)
{
DWORD pids[2];
DWORD num_pids = GetConsoleProcessList(pids, 2);
bool win32con_is_exclusive = (num_pids <= 1);
returnvalue = ((GetConsoleWindow() != NULL && !win32con_is_exclusive) || (GetStdHandle(STD_OUTPUT_HANDLE) != NULL));
alreadychecked = true;
}
//printf("isConsoleApp is %i\n", returnvalue);
return returnvalue;
}
//==========================================================================
//
// DoMain
@ -158,7 +191,22 @@ int DoMain (HINSTANCE hInstance)
Args->AppendArg(FString(wargv[i]));
}
if (Args->CheckParm("-stdout") || Args->CheckParm("-norun"))
if (isConsoleApp())
{
StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
DWORD mode;
if (GetConsoleMode(StdOut, &mode))
{
if (SetConsoleMode(StdOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
FancyStdOut = IsWindows10OrGreater(); // Windows 8.1 and lower do not understand ANSI formatting.
}
}
else if (Args->CheckParm("-stdout") || Args->CheckParm("-norun"))
{
// As a GUI application, we don't normally get a console when we start.
// If we were run from the shell and are on XP+, we can attach to its
@ -475,6 +523,11 @@ CUSTOM_CVAR(Bool, disablecrashlog, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
//
//==========================================================================
int wmain()
{
return wWinMain(GetModuleHandle(0), 0, GetCommandLineW(), SW_SHOW);
}
int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int nCmdShow)
{
g_hInst = hInstance;

View file

@ -128,6 +128,11 @@ void MainWindow::HideNetStartPane()
NetStartWindow::HideNetStartPane();
}
void MainWindow::CloseNetStartPane()
{
NetStartWindow::NetClose();
}
void MainWindow::SetNetStartProgress(int pos)
{
NetStartWindow::SetNetStartProgress(pos);

View file

@ -29,6 +29,7 @@ public:
void SetNetStartProgress(int pos);
bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
void HideNetStartPane();
void CloseNetStartPane();
void SetWindowTitle(const char* caption);

View file

@ -102,6 +102,7 @@
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void DestroyCustomCursor();
bool isConsoleApp();
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
@ -306,6 +307,7 @@ static void PrintToStdOut(const char *cpt, HANDLE StdOut)
else break;
}
}
DWORD bytes_written;
WriteFile(StdOut, printData.GetChars(), (DWORD)printData.Len(), &bytes_written, NULL);
if (terminal)
@ -370,7 +372,7 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad, int&
}
if (showwin || (vkey != 0 && GetAsyncKeyState(vkey)))
{
return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, extraArgs);
return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, &extraArgs);
}
return defaultiwad;
}

View file

@ -201,3 +201,8 @@ bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata
{
return mainwindow.RunMessageLoop(timer_callback, userdata);
}
void FBasicStartupScreen::NetClose()
{
mainwindow.CloseNetStartPane();
}

View file

@ -50,6 +50,7 @@
//===========================================================================
CVAR(Bool, gl_aalines, false, CVAR_ARCHIVE)
CVAR(Bool, hw_2dmip, true, CVAR_ARCHIVE)
void Draw2D(F2DDrawer* drawer, FRenderState& state)
{
@ -71,6 +72,8 @@ void Draw2D(F2DDrawer* drawer, FRenderState& state, int x, int y, int width, int
state.EnableMultisampling(false);
state.EnableLineSmooth(gl_aalines);
bool cache_hw_2dmip = hw_2dmip; // cache cvar lookup so it's not done in a loop
auto &vertices = drawer->mVertices;
auto &indices = drawer->mIndices;
auto &commands = drawer->mData;
@ -180,7 +183,7 @@ void Draw2D(F2DDrawer* drawer, FRenderState& state, int x, int y, int width, int
auto flags = cmd.mTexture->GetUseType() >= ETextureType::Special? UF_None : cmd.mTexture->GetUseType() == ETextureType::FontChar? UF_Font : UF_Texture;
auto scaleflags = cmd.mFlags & F2DDrawer::DTF_Indexed ? CTF_Indexed : 0;
state.SetMaterial(cmd.mTexture, flags, scaleflags, cmd.mFlags & F2DDrawer::DTF_Wrap ? CLAMP_NONE : CLAMP_XY_NOMIP, cmd.mTranslationId, -1);
state.SetMaterial(cmd.mTexture, flags, scaleflags, cmd.mFlags & F2DDrawer::DTF_Wrap ? CLAMP_NONE : (cache_hw_2dmip ? CLAMP_XY : CLAMP_XY_NOMIP), cmd.mTranslationId, -1);
state.EnableTexture(true);
// Canvas textures are stored upside down

View file

@ -947,6 +947,7 @@ PPCustomShaderInstance::PPCustomShaderInstance(PostProcessShader *desc) : Desc(d
case PostProcessUniformType::Int: AddUniformField(offset, name, UniformType::Int, sizeof(int)); break;
case PostProcessUniformType::Vec2: AddUniformField(offset, name, UniformType::Vec2, sizeof(float) * 2); break;
case PostProcessUniformType::Vec3: AddUniformField(offset, name, UniformType::Vec3, sizeof(float) * 3, sizeof(float) * 4); break;
case PostProcessUniformType::Vec4: AddUniformField(offset, name, UniformType::Vec4, sizeof(float) * 4); break;
default: break;
}
}
@ -1085,6 +1086,13 @@ void PPCustomShaderInstance::SetUniforms(PPRenderState *renderstate)
fValues[2] = (float)pair->Value.Values[2];
memcpy(dst, fValues, sizeof(float) * 3);
break;
case PostProcessUniformType::Vec4:
fValues[0] = (float)pair->Value.Values[0];
fValues[1] = (float)pair->Value.Values[1];
fValues[2] = (float)pair->Value.Values[2];
fValues[3] = (float)pair->Value.Values[3];
memcpy(dst, fValues, sizeof(float) * 4);
break;
default:
break;
}

View file

@ -116,6 +116,31 @@ DEFINE_ACTION_FUNCTION(_PPShader, SetUniform3f)
return 0;
}
DEFINE_ACTION_FUNCTION(_PPShader, SetUniform4f)
{
PARAM_PROLOGUE;
PARAM_STRING(shaderName);
PARAM_STRING(uniformName);
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
PARAM_FLOAT(w);
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
{
PostProcessShader &shader = PostProcessShaders[i];
if (shader.Name == shaderName)
{
double *vec4 = shader.Uniforms[uniformName].Values;
vec4[0] = x;
vec4[1] = y;
vec4[2] = z;
vec4[3] = w;
}
}
return 0;
}
DEFINE_ACTION_FUNCTION(_PPShader, SetUniform1i)
{
PARAM_PROLOGUE;

View file

@ -9,7 +9,8 @@ enum class PostProcessUniformType
Int,
Float,
Vec2,
Vec3
Vec3,
Vec4
};
struct PostProcessUniformValue

View file

@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------
**
** Copyright(C) 2017 Magnus Norddahl
** Copyright(C) 2017-2020 Rachael Alexanderson
** Copyright(C) 2017-2024 Rachael Alexanderson
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -98,6 +98,33 @@ namespace
{
return (uint32_t)((float)inheight * v_MinimumToFill(inwidth, inheight));
}
float v_MinimumToFill2(uint32_t inwidth, uint32_t inheight)
{
// sx = screen x dimension, sy = same for y
float sx = (float)inwidth * 1.2f, sy = (float)inheight;
static float lastsx = 0., lastsy = 0., result = 0.;
if (lastsx != sx || lastsy != sy)
{
if (sx <= 0. || sy <= 0.)
return 1.; // prevent x/0 error
// set absolute minimum scale to fill the entire screen but get as close to 640x400 as possible
float ssx = (float)(VID_MIN_UI_WIDTH) / 1.2f / sx, ssy = (float)(VID_MIN_UI_HEIGHT) / sy;
result = (ssx < ssy) ? ssy : ssx;
lastsx = sx;
lastsy = sy;
}
return result;
}
inline uint32_t v_mfillX2(uint32_t inwidth, uint32_t inheight)
{
return (uint32_t)((float)inwidth * v_MinimumToFill2(inwidth, inheight) * 1.2);
}
inline uint32_t v_mfillY2(uint32_t inwidth, uint32_t inheight)
{
return (uint32_t)((float)inheight * v_MinimumToFill2(inwidth, inheight));
}
inline void refresh_minimums()
{
// specialUI is tracking a state where high-res console fonts are actually required, and
@ -128,16 +155,17 @@ namespace
// the odd formatting of this struct definition is meant to resemble a table header. set your tab stops to 4 when editing this file.
struct v_ScaleTable
{ bool isValid; uint32_t(*GetScaledWidth)(uint32_t Width, uint32_t Height); uint32_t(*GetScaledHeight)(uint32_t Width, uint32_t Height); float pixelAspect; bool isCustom; };
{ bool isValid; uint32_t(*GetScaledWidth)(uint32_t Width, uint32_t Height); uint32_t(*GetScaledHeight)(uint32_t Width, uint32_t Height); float pixelAspect; bool isCustom; };
v_ScaleTable vScaleTable[] =
{
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return Width; }, [](uint32_t Width, uint32_t Height)->uint32_t { return Height; }, 1.0f, false }, // 0 - Native
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillX(Width, Height); }, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillY(Width, Height); }, 1.0f, false }, // 6 - Minimum Scale to Fill Entire Screen
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 640; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 400; }, 1.2f, false }, // 2 - 640x400 (formerly 320x200)
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 960; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 600; }, 1.2f, false }, // 3 - 960x600 (formerly 640x400)
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 1280; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 800; }, 1.2f, false }, // 4 - 1280x800
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customwidth; }, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customheight; }, 1.0f, true }, // 5 - Custom
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 320; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 200; }, 1.2f, false }, // 7 - 320x200
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return Width; }, [](uint32_t Width, uint32_t Height)->uint32_t { return Height; }, 1.0f, false }, // 0 - Native
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillX(Width, Height); }, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillY(Width, Height); }, 1.0f, false }, // 1 - Minimum Scale to Fill Entire Screen
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 640; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 400; }, 1.2f, false }, // 2 - 640x400 (formerly 320x200)
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 960; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 600; }, 1.2f, false }, // 3 - 960x600 (formerly 640x400)
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 1280; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 800; }, 1.2f, false }, // 4 - 1280x800
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customwidth; }, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customheight; }, 1.0f, true }, // 5 - Custom
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 320; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 200; }, 1.2f, false }, // 6 - 320x200
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillX2(Width, Height) * 1.2f; }, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillY2(Width, Height); }, 1.2f, false }, // 7 - Minimum Scale to Fill Entire Screen (1.2)
};
bool isOutOfBounds(int x)
{
@ -248,7 +276,7 @@ CCMD (vid_scaletoheight)
}
}
inline bool atob(char* I)
inline bool atob(const char* I)
{
if (stricmp (I, "true") == 0 || stricmp (I, "1") == 0)
return true;

View file

@ -1842,6 +1842,12 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
{
goto basereturn;
}
else if (ctx.Version >= MakeVersion(4, 15, 0) && basex->ValueType == TypeNullPtr && (ValueType == TypeSpriteID || ValueType == TypeTextureID || ValueType == TypeTranslationID))
{
delete basex;
basex = new FxConstant(0, ScriptPosition);
goto basereturn;
}
else if (IsFloat())
{
FxExpression *x = new FxFloatCast(basex);
@ -8268,8 +8274,12 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction
FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&args, const FScriptPosition &pos)
: FxExpression(EFX_FunctionCall, pos)
{
const bool isClient = methodname == NAME_CRandom || methodname == NAME_CFRandom
|| methodname == NAME_CRandomPick || methodname == NAME_CFRandomPick
|| methodname == NAME_CRandom2 || methodname == NAME_CSetRandomSeed;
MethodName = methodname;
RNG = &pr_exrandom;
RNG = isClient ? &M_Random : &pr_exrandom;
ArgList = std::move(args);
if (rngname != NAME_None)
{
@ -8281,7 +8291,16 @@ FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&
case NAME_FRandomPick:
case NAME_Random2:
case NAME_SetRandomSeed:
RNG = FRandom::StaticFindRNG(rngname.GetChars());
RNG = FRandom::StaticFindRNG(rngname.GetChars(), false);
break;
case NAME_CRandom:
case NAME_CFRandom:
case NAME_CRandomPick:
case NAME_CFRandomPick:
case NAME_CRandom2:
case NAME_CSetRandomSeed:
RNG = FRandom::StaticFindRNG(rngname.GetChars(), true);
break;
default:
@ -8498,6 +8517,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
case NAME_State:
case NAME_SpriteID:
case NAME_TextureID:
case NAME_TranslationID:
if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition))
{
PType *type =
@ -8547,13 +8567,22 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
}
break;
case NAME_CSetRandomSeed:
if (CheckArgSize(NAME_CRandom, ArgList, 1, 1, ScriptPosition))
{
func = new FxRandomSeed(RNG, ArgList[0], ScriptPosition, ctx.FromDecorate);
ArgList[0] = nullptr;
}
break;
case NAME_Random:
case NAME_CRandom:
// allow calling Random without arguments to default to (0, 255)
if (ArgList.Size() == 0)
{
func = new FxRandom(RNG, new FxConstant(0, ScriptPosition), new FxConstant(255, ScriptPosition), ScriptPosition, ctx.FromDecorate);
}
else if (CheckArgSize(NAME_Random, ArgList, 2, 2, ScriptPosition))
else if (CheckArgSize(MethodName, ArgList, 2, 2, ScriptPosition))
{
func = new FxRandom(RNG, ArgList[0], ArgList[1], ScriptPosition, ctx.FromDecorate);
ArgList[0] = ArgList[1] = nullptr;
@ -8561,7 +8590,8 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
break;
case NAME_FRandom:
if (CheckArgSize(NAME_FRandom, ArgList, 2, 2, ScriptPosition))
case NAME_CFRandom:
if (CheckArgSize(MethodName, ArgList, 2, 2, ScriptPosition))
{
func = new FxFRandom(RNG, ArgList[0], ArgList[1], ScriptPosition);
ArgList[0] = ArgList[1] = nullptr;
@ -8570,14 +8600,17 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
case NAME_RandomPick:
case NAME_FRandomPick:
case NAME_CRandomPick:
case NAME_CFRandomPick:
if (CheckArgSize(MethodName, ArgList, 1, -1, ScriptPosition))
{
func = new FxRandomPick(RNG, ArgList, MethodName == NAME_FRandomPick, ScriptPosition, ctx.FromDecorate);
func = new FxRandomPick(RNG, ArgList, MethodName == NAME_FRandomPick || MethodName == NAME_CFRandomPick, ScriptPosition, ctx.FromDecorate);
}
break;
case NAME_Random2:
if (CheckArgSize(NAME_Random2, ArgList, 0, 1, ScriptPosition))
case NAME_CRandom2:
if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition))
{
func = new FxRandom2(RNG, ArgList.Size() == 0? nullptr : ArgList[0], ScriptPosition, ctx.FromDecorate);
if (ArgList.Size() > 0) ArgList[0] = nullptr;
@ -8850,7 +8883,24 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
return Self;
}
}
else if (Self->ValueType == TypeTextureID)
else if (ctx.Version >= MakeVersion(4, 15, 0) && Self->ValueType == TypeSound && MethodName == NAME_IsValid)
{
if (ArgList.Size() > 0)
{
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
delete this;
return nullptr;
}
Self->ValueType = TypeSInt32; // treat as integer
FxExpression *x = new FxCompareRel('>', Self, new FxConstant(0, ScriptPosition));
Self = nullptr;
SAFE_RESOLVE(x, ctx);
delete this;
return x;
}
else if (Self->ValueType == TypeTextureID || (ctx.Version >= MakeVersion(4, 15, 0) && (Self->ValueType == TypeTranslationID)))
{
if (MethodName == NAME_IsValid || MethodName == NAME_IsNull || MethodName == NAME_Exists || MethodName == NAME_SetInvalid || MethodName == NAME_SetNull)
{
@ -8893,6 +8943,67 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
}
}
else if (ctx.Version >= MakeVersion(4, 15, 0) && Self->ValueType == TypeSpriteID)
{
if (MethodName == NAME_IsValid || MethodName == NAME_IsEmpty || MethodName == NAME_IsFixed || MethodName == NAME_IsKeep
|| MethodName == NAME_Exists
|| MethodName == NAME_SetInvalid || MethodName == NAME_SetEmpty || MethodName == NAME_SetFixed || MethodName == NAME_SetKeep)
{
if (ArgList.Size() > 0)
{
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
delete this;
return nullptr;
}
// No need to create a dedicated node here, all builtins map directly to trivial operations.
Self->ValueType = TypeSInt32; // all builtins treat the texture index as integer.
FxExpression *x = nullptr;
switch (MethodName.GetIndex())
{
case NAME_IsValid:
x = new FxCompareRel(TK_Geq, Self, new FxConstant(0, ScriptPosition));
break;
case NAME_IsEmpty: // TNT1
x = new FxCompareEq(TK_Eq, Self, new FxConstant(0, ScriptPosition));
break;
case NAME_IsFixed: // "----"
x = new FxCompareEq(TK_Eq, Self, new FxConstant(1, ScriptPosition));
break;
case NAME_IsKeep: // "####"
x = new FxCompareEq(TK_Eq, Self, new FxConstant(2, ScriptPosition));
break;
case NAME_Exists:
x = new FxCompareRel(TK_Geq, Self, new FxConstant(3, ScriptPosition));
break;
case NAME_SetInvalid:
x = new FxAssign(Self, new FxConstant(-1, ScriptPosition));
break;
case NAME_SetEmpty: // TNT1
x = new FxAssign(Self, new FxConstant(0, ScriptPosition));
break;
case NAME_SetFixed: // "----"
x = new FxAssign(Self, new FxConstant(1, ScriptPosition));
break;
case NAME_SetKeep: // "####"
x = new FxAssign(Self, new FxConstant(2, ScriptPosition));
break;
}
Self = nullptr;
SAFE_RESOLVE(x, ctx);
if (MethodName == NAME_SetInvalid || MethodName == NAME_SetEmpty || MethodName == NAME_SetFixed || MethodName == NAME_SetKeep) x->ValueType = TypeVoid; // override the default type of the assignment operator.
delete this;
return x;
}
}
else if (Self->IsVector())
{
// handle builtins: Vectors got 5.
@ -8967,7 +9078,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
}
else
{
if (PFunction **Override; ctx.Version >= MakeVersion(4, 11, 0) && (Override = static_cast<PDynArray*>(Self->ValueType)->FnOverrides.CheckKey(MethodName)))
if (PFunction **Override; (Override = static_cast<PDynArray*>(Self->ValueType)->FnOverrides.CheckKey(MethodName)))
{
afd_override = *Override;
}
@ -12640,6 +12751,15 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx)
if (ValueType->RegType == REGT_NIL && ValueType != TypeAuto)
{
auto sfunc = static_cast<VMScriptFunction *>(ctx.Function->Variants[0].Implementation);
const unsigned MAX_STACK_ALLOC = 512 * 1024; // Windows stack is 1 MB, but we cannot go up there without problems
if (uint64_t(ValueType->Size) + uint64_t(sfunc->ExtraSpace) > MAX_STACK_ALLOC)
{
ScriptPosition.Message(MSG_ERROR, "%s exceeds max. allowed size of 512kb for local variables at variable %s", sfunc->Name.GetChars(), Name.GetChars());
delete this;
return nullptr;
}
StackOffset = sfunc->AllocExtraStack(ValueType);
if (Init != nullptr)

View file

@ -40,6 +40,7 @@
#include "filesystem.h"
CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
CVAR(Bool, warningstoerrors, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
EXTERN_CVAR(Bool, vm_jit)
EXTERN_CVAR(Bool, vm_jit_aot)
@ -879,10 +880,18 @@ void FFunctionBuildList::Build()
{
if (!item.Code->CheckReturn())
{
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
newcmpd->Add(item.Code);
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
item.Code = newcmpd->Resolve(ctx);
if (ctx.ReturnProto == nullptr || !ctx.ReturnProto->ReturnTypes.Size())
{
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
newcmpd->Add(item.Code);
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
item.Code = newcmpd->Resolve(ctx);
}
else
{
item.Code->ScriptPosition.Message(MSG_ERROR, "Missing return statement in %s", item.PrintableName.GetChars());
continue;
}
}
item.Proto = ctx.ReturnProto;

View file

@ -2286,6 +2286,11 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize,
Error(arraysize, "Array size must be positive");
return TypeError;
}
if (uint64_t(size) * baseType->Size > 0x7fffffff)
{
Error(arraysize, "Array size overflow. Total size must be less than 2GB");
return TypeError;
}
baseType = NewArray(baseType, size);
}

View file

@ -1706,7 +1706,7 @@ DEFINE_ACTION_FUNCTION(DScriptScanner, ScriptError)
{
PARAM_SELF_PROLOGUE(DScriptScanner);
FString s = FStringFormat(VM_ARGS_NAMES);
FString s = FStringFormat(VM_ARGS_NAMES, 1);
self->wrapped.ScriptError("%s", s.GetChars());
return 0;
}
@ -1715,7 +1715,7 @@ DEFINE_ACTION_FUNCTION(DScriptScanner, ScriptMessage)
{
PARAM_SELF_PROLOGUE(DScriptScanner);
FString s = FStringFormat(VM_ARGS_NAMES);
FString s = FStringFormat(VM_ARGS_NAMES, 1);
self->wrapped.ScriptMessage("%s", s.GetChars());
return 0;
}

View file

@ -62,6 +62,7 @@ enum EGameTexFlags
GTexf_OffsetsNotForFont = 512, // The offsets must be ignored when using this texture in a font.
GTexf_NoTrim = 1024, // Don't perform trimming on this texture.
GTexf_Seen = 2048, // Set to true when the texture is being used for rendering. Must be cleared manually if the check is needed.
GTexf_NoMipmap = 4096, // Disable mipmapping for this texture
};
struct FMaterialLayers
@ -265,6 +266,9 @@ public:
void SetGlowing(PalEntry color) { flags = (flags & ~GTexf_AutoGlowing) | GTexf_Glowing; GlowColor = color; }
void SetDisableBrightmap() { flags |= GTexf_BrightmapChecked; Brightmap = nullptr; }
bool isNoMipmap() const { return !!(flags & GTexf_NoMipmap); }
void SetNoMipmap(bool set) { if (set) flags |= GTexf_NoMipmap; else flags &= ~GTexf_NoMipmap; }
bool isUserContent() const;
int CheckRealHeight() { return xs_RoundToInt(Base->CheckRealHeight() / ScaleY); }
void SetSize(int x, int y)

View file

@ -54,7 +54,7 @@
// MACROS ------------------------------------------------------------------
// The maximum size of an IDAT chunk ZDoom will write. This is also the
// size of the compression buffer it allocates on the stack.
// size of the compression buffer it allocates on the heap.
#define PNG_WRITE_SIZE 32768
// Set this to 1 to use a simple heuristic to select the filter to apply
@ -926,8 +926,7 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
temprow[i] = &temprow_storage[temprow_size * i];
}
TArray<Byte> array(PNG_WRITE_SIZE, true);
auto buffer = array.data();
TArray<Byte> buffer(PNG_WRITE_SIZE, true);
z_stream stream;
int err;
int y;
@ -944,8 +943,8 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
}
y = height;
stream.next_out = buffer;
stream.avail_out = sizeof(buffer);
stream.next_out = buffer.data();
stream.avail_out = buffer.size();
temprow[0][0] = 0;
#if USE_FILTER_HEURISTIC
@ -1007,12 +1006,12 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
}
while (stream.avail_out == 0)
{
if (!WriteIDAT (file, buffer, sizeof(buffer)))
if (!WriteIDAT (file, buffer.data(), buffer.size()))
{
return false;
}
stream.next_out = buffer;
stream.avail_out = sizeof(buffer);
stream.next_out = buffer.data();
stream.avail_out = buffer.size();
if (stream.avail_in != 0)
{
err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
@ -1033,12 +1032,12 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
}
if (stream.avail_out == 0)
{
if (!WriteIDAT (file, buffer, sizeof(buffer)))
if (!WriteIDAT (file, buffer.data(), buffer.size()))
{
return false;
}
stream.next_out = buffer;
stream.avail_out = sizeof(buffer);
stream.next_out = buffer.data();
stream.avail_out = buffer.size();
}
}
@ -1048,7 +1047,7 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
{
return false;
}
return WriteIDAT (file, buffer, sizeof(buffer)-stream.avail_out);
return WriteIDAT (file, buffer.data(), buffer.size() - stream.avail_out);
}
//==========================================================================

View file

@ -924,7 +924,7 @@ public:
return *this;
}
~TDeletingArray<T, TT> ()
~TDeletingArray()
{
for (unsigned int i = 0; i < TArray<T,TT>::Size(); ++i)
{

View file

@ -64,6 +64,12 @@ bool NetStartWindow::RunMessageLoop(bool (*newtimer_callback)(void*), void* newu
return Instance->exitreason;
}
void NetStartWindow::NetClose()
{
if (Instance != nullptr)
Instance->OnClose();
}
NetStartWindow::NetStartWindow() : Widget(nullptr, WidgetType::Window)
{
SetWindowBackground(Colorf::fromRgba8(51, 51, 51));

View file

@ -14,6 +14,7 @@ public:
static void HideNetStartPane();
static void SetNetStartProgress(int pos);
static bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
static void NetClose();
private:
NetStartWindow();

View file

@ -11,13 +11,13 @@
#include <zwidget/window/window.h>
#include <zwidget/widgets/tabwidget/tabwidget.h>
int LauncherWindow::ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs)
int LauncherWindow::ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString* extraArgs)
{
Size screenSize = GetScreenSize();
double windowWidth = 615.0;
double windowHeight = 700.0;
auto launcher = std::make_unique<LauncherWindow>(wads, numwads, defaultiwad, autoloadflags, extraArgs);
auto launcher = std::make_unique<LauncherWindow>(wads, numwads, defaultiwad, autoloadflags, *extraArgs);
launcher->SetFrameGeometry((screenSize.width - windowWidth) * 0.5, (screenSize.height - windowHeight) * 0.5, windowWidth, windowHeight);
launcher->Show();

View file

@ -14,7 +14,7 @@ struct WadStuff;
class LauncherWindow : public Widget
{
public:
static int ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs);
static int ExecModal(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString* extraArgs);
LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags, FString& extraArgs);
void UpdateLanguage();