Merge remote-tracking branch 'origin/master' into friendly_window_title

This commit is contained in:
Rachael Alexanderson 2017-11-18 14:08:33 -05:00
commit f1405f68a7
46 changed files with 1346 additions and 305 deletions

View file

@ -11,10 +11,23 @@ git:
matrix: matrix:
include: include:
- os: osx - os: osx
osx_image: xcode9 osx_image: xcode9.1
env: env:
- CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.7" - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.7"
- os: linux
compiler: gcc
env:
- GCC_VERSION=4.9
- CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=-Wno-maybe-uninitialized"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.9
- libsdl2-dev
- os: linux - os: linux
compiler: gcc compiler: gcc
env: env:

373
docs/licenses/mpl.txt Normal file
View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -958,7 +958,6 @@ set (PCH_SOURCES
stats.cpp stats.cpp
stringtable.cpp stringtable.cpp
teaminfo.cpp teaminfo.cpp
tempfiles.cpp
v_blend.cpp v_blend.cpp
v_collection.cpp v_collection.cpp
v_draw.cpp v_draw.cpp
@ -1179,6 +1178,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE
${PCH_SOURCES} ${PCH_SOURCES}
x86.cpp x86.cpp
strnatcmp.c strnatcmp.c
tmpfileplus.c
zstring.cpp zstring.cpp
math/asin.c math/asin.c
math/atan.c math/atan.c

View file

@ -583,7 +583,7 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
// We have a -iwad parameter. Pick the first usable IWAD we found through that. // We have a -iwad parameter. Pick the first usable IWAD we found through that.
for (unsigned i = numFoundWads; i < mFoundWads.Size(); i++) for (unsigned i = numFoundWads; i < mFoundWads.Size(); i++)
{ {
if (mFoundWads[i].mInfoIndex > 0) if (mFoundWads[i].mInfoIndex >= 0)
{ {
picks.Push(mFoundWads[i]); picks.Push(mFoundWads[i]);
break; break;

View file

@ -1441,6 +1441,10 @@ void ParseCVarInfo()
{ {
cvarflags |= CVAR_CHEAT; cvarflags |= CVAR_CHEAT;
} }
else if (stricmp(sc.String, "latch") == 0)
{
cvarflags |= CVAR_LATCH;
}
else else
{ {
sc.ScriptError("Unknown cvar attribute '%s'", sc.String); sc.ScriptError("Unknown cvar attribute '%s'", sc.String);

View file

@ -128,7 +128,7 @@ int starttime;
extern FString BackupSaveName; extern FString BackupSaveName;
bool savegamerestore; bool savegamerestore;
int finishstate; int finishstate = FINISH_NoHub;
extern int mousex, mousey; extern int mousex, mousey;
extern bool sendpause, sendsave, sendturn180, SendLand; extern bool sendpause, sendsave, sendturn180, SendLand;
@ -851,6 +851,8 @@ void G_DoCompleted (void)
level.maptime = 0; level.maptime = 0;
} }
finishstate = mode;
if (!deathmatch && if (!deathmatch &&
((level.flags & LEVEL_NOINTERMISSION) || ((level.flags & LEVEL_NOINTERMISSION) ||
((nextcluster == thiscluster) && (thiscluster->flags & CLUSTER_HUB) && !(thiscluster->flags & CLUSTER_ALLOWINTERMISSION)))) ((nextcluster == thiscluster) && (thiscluster->flags & CLUSTER_HUB) && !(thiscluster->flags & CLUSTER_ALLOWINTERMISSION))))
@ -860,7 +862,6 @@ void G_DoCompleted (void)
} }
gamestate = GS_INTERMISSION; gamestate = GS_INTERMISSION;
finishstate = mode;
viewactive = false; viewactive = false;
automapactive = false; automapactive = false;
@ -1038,12 +1039,20 @@ void G_DoLoadLevel (int position, bool autosave)
{ {
players[ii].camera = players[ii].mo; players[ii].camera = players[ii].mo;
} }
if (!savegamerestore)
if (savegamerestore)
{ {
E_PlayerEntered(ii, finishstate == FINISH_SameHub); continue;
} }
const bool fromSnapshot = level.FromSnapshot;
E_PlayerEntered(ii, fromSnapshot && finishstate == FINISH_SameHub);
if (fromSnapshot)
{
// ENTER scripts are being handled when the player gets spawned, this cannot be changed due to its effect on voodoo dolls. // ENTER scripts are being handled when the player gets spawned, this cannot be changed due to its effect on voodoo dolls.
if (level.FromSnapshot && !savegamerestore) FBehavior::StaticStartTypedScripts(SCRIPT_Return, players[ii].mo, true); FBehavior::StaticStartTypedScripts(SCRIPT_Return, players[ii].mo, true);
}
} }
} }

View file

@ -484,3 +484,213 @@ VSMatrix::multMatrix(FLOATTYPE *resMat, const FLOATTYPE *aMatrix)
} }
memcpy(resMat, res, 16 * sizeof(FLOATTYPE)); memcpy(resMat, res, 16 * sizeof(FLOATTYPE));
} }
static double mat3Determinant(const FLOATTYPE *mMat3x3)
{
return mMat3x3[0] * (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) +
mMat3x3[1] * (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) +
mMat3x3[2] * (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]);
}
static double mat4Determinant(const FLOATTYPE *matrix)
{
FLOATTYPE mMat3x3_a[9] =
{
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_b[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_c[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_d[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE a, b, c, d;
FLOATTYPE value;
a = mat3Determinant(mMat3x3_a);
b = mat3Determinant(mMat3x3_b);
c = mat3Determinant(mMat3x3_c);
d = mat3Determinant(mMat3x3_d);
value = matrix[0 * 4 + 0] * a;
value -= matrix[0 * 4 + 1] * b;
value += matrix[0 * 4 + 2] * c;
value -= matrix[0 * 4 + 3] * d;
return value;
}
static void mat4Adjoint(const FLOATTYPE *matrix, FLOATTYPE *result)
{
FLOATTYPE mMat3x3_a[9] =
{
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_b[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_c[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_d[9] =
{
matrix[1 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[1 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[1 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE mMat3x3_e[9] =
{
matrix[0 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_f[9] =
{
matrix[0 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_g[9] =
{
matrix[0 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 3], matrix[2 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_h[9] =
{
matrix[0 * 4 + 0], matrix[2 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[2 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[2 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE mMat3x3_i[9] =
{
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_j[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[3 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_k[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[3 * 4 + 3]
};
FLOATTYPE mMat3x3_l[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[3 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[3 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[3 * 4 + 2]
};
FLOATTYPE mMat3x3_m[9] =
{
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[2 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[2 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[2 * 4 + 3]
};
FLOATTYPE mMat3x3_n[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[2 * 4 + 0],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[2 * 4 + 2],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[2 * 4 + 3]
};
FLOATTYPE mMat3x3_o[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[2 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[2 * 4 + 1],
matrix[0 * 4 + 3], matrix[1 * 4 + 3], matrix[2 * 4 + 3]
};
FLOATTYPE mMat3x3_p[9] =
{
matrix[0 * 4 + 0], matrix[1 * 4 + 0], matrix[2 * 4 + 0],
matrix[0 * 4 + 1], matrix[1 * 4 + 1], matrix[2 * 4 + 1],
matrix[0 * 4 + 2], matrix[1 * 4 + 2], matrix[2 * 4 + 2]
};
result[0 * 4 + 0] = mat3Determinant(mMat3x3_a);
result[1 * 4 + 0] = -mat3Determinant(mMat3x3_b);
result[2 * 4 + 0] = mat3Determinant(mMat3x3_c);
result[3 * 4 + 0] = -mat3Determinant(mMat3x3_d);
result[0 * 4 + 1] = -mat3Determinant(mMat3x3_e);
result[1 * 4 + 1] = mat3Determinant(mMat3x3_f);
result[2 * 4 + 1] = -mat3Determinant(mMat3x3_g);
result[3 * 4 + 1] = mat3Determinant(mMat3x3_h);
result[0 * 4 + 2] = mat3Determinant(mMat3x3_i);
result[1 * 4 + 2] = -mat3Determinant(mMat3x3_j);
result[2 * 4 + 2] = mat3Determinant(mMat3x3_k);
result[3 * 4 + 2] = -mat3Determinant(mMat3x3_l);
result[0 * 4 + 3] = -mat3Determinant(mMat3x3_m);
result[1 * 4 + 3] = mat3Determinant(mMat3x3_n);
result[2 * 4 + 3] = -mat3Determinant(mMat3x3_o);
result[3 * 4 + 3] = mat3Determinant(mMat3x3_p);
}
bool VSMatrix::inverseMatrix(VSMatrix &result)
{
// Calculate mat4 determinant
FLOATTYPE det = mat4Determinant(mMatrix);
// Inverse unknown when determinant is close to zero
if (fabs(det) < 1e-15)
{
for (int i = 0; i < 16; i++)
result.mMatrix[i] = FLOATTYPE(0.0);
return false;
}
else
{
mat4Adjoint(mMatrix, result.mMatrix);
FLOATTYPE invDet = FLOATTYPE(1.0) / det;
for (int i = 0; i < 16; i++)
{
result.mMatrix[i] = result.mMatrix[i] * invDet;
}
}
return true;
}

View file

@ -59,7 +59,7 @@ struct FDynLightData
bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &data); bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &data);
void gl_AddLightToList(int group, ADynamicLight * light, FDynLightData &ldata, bool hudmodel); void gl_AddLightToList(int group, ADynamicLight * light, FDynLightData &ldata);
void gl_UploadLights(FDynLightData &data); void gl_UploadLights(FDynLightData &data);

View file

@ -77,7 +77,7 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FD
return false; return false;
} }
gl_AddLightToList(group, light, ldata, false); gl_AddLightToList(group, light, ldata);
return true; return true;
} }
@ -86,38 +86,13 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FD
// Add one dynamic light to the light data list // Add one dynamic light to the light data list
// //
//========================================================================== //==========================================================================
void gl_AddLightToList(int group, ADynamicLight * light, FDynLightData &ldata, bool hudmodel) void gl_AddLightToList(int group, ADynamicLight * light, FDynLightData &ldata)
{ {
int i = 0; int i = 0;
DVector3 pos = light->PosRelative(group); DVector3 pos = light->PosRelative(group);
float radius = light->GetRadius(); float radius = light->GetRadius();
if (hudmodel)
{
// HUD model is already translated and rotated. We must rotate the lights into that view space.
DVector3 rotation;
DVector3 localpos = pos - r_viewpoint.Pos;
rotation.X = localpos.X * r_viewpoint.Angles.Yaw.Sin() - localpos.Y * r_viewpoint.Angles.Yaw.Cos();
rotation.Y = localpos.X * r_viewpoint.Angles.Yaw.Cos() + localpos.Y * r_viewpoint.Angles.Yaw.Sin();
rotation.Z = localpos.Z;
localpos = rotation;
rotation.X = localpos.X;
rotation.Y = localpos.Y * r_viewpoint.Angles.Pitch.Sin() - localpos.Z * r_viewpoint.Angles.Pitch.Cos();
rotation.Z = localpos.Y * r_viewpoint.Angles.Pitch.Cos() + localpos.Z * r_viewpoint.Angles.Pitch.Sin();
localpos = rotation;
rotation.Y = localpos.Y;
rotation.Z = localpos.Z * r_viewpoint.Angles.Roll.Sin() - localpos.X * r_viewpoint.Angles.Roll.Cos();
rotation.X = localpos.Z * r_viewpoint.Angles.Roll.Cos() + localpos.X * r_viewpoint.Angles.Roll.Sin();
localpos = rotation;
pos = localpos;
}
float cs; float cs;
if (light->IsAdditive()) if (light->IsAdditive())
{ {

View file

@ -1095,33 +1095,33 @@ void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY)
glFrontFace(GL_CCW); glFrontFace(GL_CCW);
} }
// [BB] The model has to be drawn independently from the position of the player, // The model position and orientation has to be drawn independently from the position of the player,
// so we have to reset the view matrix. // but we need to position it correctly in the world for light to work properly.
gl_RenderState.mViewMatrix.loadIdentity(); VSMatrix objectToWorldMatrix;
gl_RenderState.mViewMatrix.inverseMatrix(objectToWorldMatrix);
// Need to reset the normal matrix too
gl_RenderState.mNormalViewMatrix.loadIdentity();
// Scaling model (y scale for a sprite means height, i.e. z in the world!). // Scaling model (y scale for a sprite means height, i.e. z in the world!).
gl_RenderState.mViewMatrix.scale(smf->xscale, smf->zscale, smf->yscale); objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale);
// Aplying model offsets (model offsets do not depend on model scalings). // Aplying model offsets (model offsets do not depend on model scalings).
gl_RenderState.mViewMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale);
// [BB] Weapon bob, very similar to the normal Doom weapon bob. // [BB] Weapon bob, very similar to the normal Doom weapon bob.
gl_RenderState.mViewMatrix.rotate(ofsX/4, 0, 1, 0); objectToWorldMatrix.rotate(ofsX/4, 0, 1, 0);
gl_RenderState.mViewMatrix.rotate((ofsY-WEAPONTOP)/-4., 1, 0, 0); objectToWorldMatrix.rotate((ofsY-WEAPONTOP)/-4., 1, 0, 0);
// [BB] For some reason the jDoom models need to be rotated. // [BB] For some reason the jDoom models need to be rotated.
gl_RenderState.mViewMatrix.rotate(90.f, 0, 1, 0); objectToWorldMatrix.rotate(90.f, 0, 1, 0);
// Applying angleoffset, pitchoffset, rolloffset. // Applying angleoffset, pitchoffset, rolloffset.
gl_RenderState.mViewMatrix.rotate(-smf->angleoffset, 0, 1, 0); objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0);
gl_RenderState.mViewMatrix.rotate(smf->pitchoffset, 0, 0, 1); objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1);
gl_RenderState.mViewMatrix.rotate(-smf->rolloffset, 1, 0, 0); objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0);
gl_RenderState.ApplyMatrices();
gl_RenderState.mModelMatrix = objectToWorldMatrix;
gl_RenderState.EnableModelMatrix(true);
gl_RenderFrameModels( smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0 ); gl_RenderFrameModels( smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0 );
gl_RenderState.EnableModelMatrix(false);
glDepthFunc(GL_LESS); glDepthFunc(GL_LESS);
if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] ))

View file

@ -335,7 +335,7 @@ void GLSprite::Draw(int pass)
if (gl_lights && GLRenderer->mLightCount && mDrawer->FixedColormap == CM_DEFAULT && !fullbright) if (gl_lights && GLRenderer->mLightCount && mDrawer->FixedColormap == CM_DEFAULT && !fullbright)
{ {
if (modelframe && !particle) if (modelframe && !particle)
gl_SetDynModelLight(gl_light_sprites ? actor : NULL, false); gl_SetDynModelLight(gl_light_sprites ? actor : NULL);
else else
gl_SetDynSpriteLight(gl_light_sprites ? actor : NULL, gl_light_particles ? particle : NULL); gl_SetDynSpriteLight(gl_light_sprites ? actor : NULL, gl_light_particles ? particle : NULL);
} }

View file

@ -177,7 +177,7 @@ void BSPWalkCircle(float x, float y, float radiusSquared, const Callback &callba
BSPNodeWalkCircle(level.HeadNode(), x, y, radiusSquared, callback); BSPNodeWalkCircle(level.HeadNode(), x, y, radiusSquared, callback);
} }
void gl_SetDynModelLight(AActor *self, bool hudmodel) void gl_SetDynModelLight(AActor *self)
{ {
// Legacy and deferred render paths gets the old flat model light // Legacy and deferred render paths gets the old flat model light
if (gl.lightmethod != LM_DIRECT) if (gl.lightmethod != LM_DIRECT)
@ -209,16 +209,16 @@ void gl_SetDynModelLight(AActor *self, bool hudmodel)
{ {
int group = subsector->sector->PortalGroup; int group = subsector->sector->PortalGroup;
DVector3 pos = light->PosRelative(group); DVector3 pos = light->PosRelative(group);
float radius = light->GetRadius(); float radius = light->GetRadius() + self->renderradius;
double dx = pos.X - x; double dx = pos.X - x;
double dy = pos.Y - y; double dy = pos.Y - y;
double dz = pos.Z - z; double dz = pos.Z - z;
double distSquared = dx * dx + dy * dy + dz * dz; double distSquared = dx * dx + dy * dy + dz * dz;
if (distSquared < radiusSquared + radius * radius) // Light and actor touches if (distSquared < radius * radius) // Light and actor touches
{ {
if (std::find(addedLights.begin(), addedLights.end(), light) == addedLights.end()) // Check if we already added this light from a different subsector if (std::find(addedLights.begin(), addedLights.end(), light) == addedLights.end()) // Check if we already added this light from a different subsector
{ {
gl_AddLightToList(group, light, modellightdata, hudmodel); gl_AddLightToList(group, light, modellightdata);
addedLights.push_back(light); addedLights.push_back(light);
} }
} }

View file

@ -422,6 +422,6 @@ inline float Dist2(float x1,float y1,float x2,float y2)
void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t *subsec); void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t *subsec);
void gl_SetDynSpriteLight(AActor *actor, particle_t *particle); void gl_SetDynSpriteLight(AActor *actor, particle_t *particle);
void gl_SetDynModelLight(AActor *self, bool hudmodel); void gl_SetDynModelLight(AActor *self);
#endif #endif

View file

@ -422,7 +422,7 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep)
{ {
FSpriteModelFrame *smf = playermo->player->ReadyWeapon ? gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false) : nullptr; FSpriteModelFrame *smf = playermo->player->ReadyWeapon ? gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false) : nullptr;
if (smf) if (smf)
gl_SetDynModelLight(playermo, true); gl_SetDynModelLight(playermo);
else else
gl_SetDynSpriteLight(playermo, NULL); gl_SetDynSpriteLight(playermo, NULL);
} }

View file

@ -93,8 +93,8 @@ void FHardwareTexture::Resize(int width, int height, unsigned char *src_data, un
// down we will need to gather a grid of pixels of the size of the scale // down we will need to gather a grid of pixels of the size of the scale
// factor in each direction and then do an averaging of the pixels. // factor in each direction and then do an averaging of the pixels.
TArray<BoxPrecalc> vPrecalcs(height); TArray<BoxPrecalc> vPrecalcs(height, true);
TArray<BoxPrecalc> hPrecalcs(width); TArray<BoxPrecalc> hPrecalcs(width, true);
ResampleBoxPrecalc(vPrecalcs, texheight); ResampleBoxPrecalc(vPrecalcs, texheight);
ResampleBoxPrecalc(hPrecalcs, texwidth); ResampleBoxPrecalc(hPrecalcs, texwidth);

View file

@ -322,18 +322,16 @@ bool PClassActor::SetReplacement(FName replaceName)
void AActor::Finalize(FStateDefinitions &statedef) void AActor::Finalize(FStateDefinitions &statedef)
{ {
AActor *defaults = this;
try try
{ {
statedef.FinishStates(GetClass(), defaults); statedef.FinishStates(GetClass());
} }
catch (CRecoverableError &) catch (CRecoverableError &)
{ {
statedef.MakeStateDefines(nullptr); statedef.MakeStateDefines(nullptr);
throw; throw;
} }
statedef.InstallStates(GetClass(), defaults); statedef.InstallStates(GetClass(), this);
statedef.MakeStateDefines(nullptr); statedef.MakeStateDefines(nullptr);
} }

View file

@ -324,15 +324,17 @@ enum // P_AimLineAttack flags
enum // P_LineAttack flags enum // P_LineAttack flags
{ {
LAF_ISMELEEATTACK = 1, LAF_ISMELEEATTACK = 1,
LAF_NORANDOMPUFFZ = 2, LAF_NORANDOMPUFFZ = 1 << 1,
LAF_NOIMPACTDECAL = 4, LAF_NOIMPACTDECAL = 1 << 2,
LAF_NOINTERACT = 8, LAF_NOINTERACT = 1 << 3,
LAF_TARGETISSOURCE = 16, LAF_TARGETISSOURCE= 1 << 4,
LAF_OVERRIDEZ = 32, LAF_OVERRIDEZ = 1 << 5,
LAF_ABSOFFSET = 1 << 6,
LAF_ABSPOSITION = 1 << 7,
}; };
AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL, double sz = 0.0); AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL, double sz = 0.0, double offsetforward = 0.0, double offsetside = 0.0);
AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, FName pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL, double sz = 0.0); AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, FName pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL, double sz = 0.0, double offsetforward = 0.0, double offsetside = 0.0);
void P_TraceBleed(int damage, const DVector3 &pos, AActor *target, DAngle angle, DAngle pitch); void P_TraceBleed(int damage, const DVector3 &pos, AActor *target, DAngle angle, DAngle pitch);
void P_TraceBleed(int damage, AActor *target, DAngle angle, DAngle pitch); void P_TraceBleed(int damage, AActor *target, DAngle angle, DAngle pitch);

View file

@ -4395,7 +4395,8 @@ static ETraceStatus CheckForActor(FTraceResults &res, void *userdata)
//========================================================================== //==========================================================================
AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, FTranslatedLineTarget*victim, int *actualdamage, double sz) DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, FTranslatedLineTarget*victim, int *actualdamage,
double sz, double offsetforward, double offsetside)
{ {
bool nointeract = !!(flags & LAF_NOINTERACT); bool nointeract = !!(flags & LAF_NOINTERACT);
DVector3 direction; DVector3 direction;
@ -4496,7 +4497,34 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags = TRACE_NoSky; if (nointeract || (puffDefaults && puffDefaults->flags6 & MF6_NOTRIGGER)) tflags = TRACE_NoSky;
else tflags = TRACE_NoSky | TRACE_Impact; else tflags = TRACE_NoSky | TRACE_Impact;
if (!Trace(t1->PosAtZ(shootz), t1->Sector, direction, distance, MF_SHOOTABLE, // [MC] Check the flags and set the position according to what is desired.
// LAF_ABSPOSITION: Treat the offset parameters as direct coordinates.
// LAF_ABSOFFSET: Ignore the angle.
DVector3 tempos;
if (flags & LAF_ABSPOSITION)
{
tempos = DVector3(offsetforward, offsetside, sz);
}
else if (flags & LAF_ABSOFFSET)
{
tempos = t1->Vec2OffsetZ(offsetforward, offsetside, shootz);
}
else if (0.0 == offsetforward && 0.0 == offsetside)
{
// Default case so exact comparison is enough
tempos = t1->PosAtZ(shootz);
}
else
{
const double s = angle.Sin();
const double c = angle.Cos();
tempos = t1->Vec2OffsetZ(offsetforward * c + offsetside * s, offsetforward * s - offsetside * c, shootz);
}
// Perform the trace.
if (!Trace(tempos, t1->Sector, direction, distance, MF_SHOOTABLE,
ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN, t1, trace, tflags, CheckForActor, &TData)) ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN, t1, trace, tflags, CheckForActor, &TData))
{ // hit nothing { // hit nothing
if (!nointeract && puffDefaults && puffDefaults->ActiveSound) if (!nointeract && puffDefaults && puffDefaults->ActiveSound)
@ -4702,7 +4730,8 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
} }
AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
DAngle pitch, int damage, FName damageType, FName pufftype, int flags, FTranslatedLineTarget *victim, int *actualdamage, double sz) DAngle pitch, int damage, FName damageType, FName pufftype, int flags, FTranslatedLineTarget *victim, int *actualdamage,
double sz, double offsetforward, double offsetside)
{ {
PClassActor *type = PClass::FindActor(pufftype); PClassActor *type = PClass::FindActor(pufftype);
if (type == NULL) if (type == NULL)
@ -4716,7 +4745,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
} }
else else
{ {
return P_LineAttack(t1, angle, distance, pitch, damage, damageType, type, flags, victim, actualdamage, sz); return P_LineAttack(t1, angle, distance, pitch, damage, damageType, type, flags, victim, actualdamage, sz, offsetforward, offsetside);
} }
} }
@ -4732,10 +4761,12 @@ DEFINE_ACTION_FUNCTION(AActor, LineAttack)
PARAM_INT_DEF(flags); PARAM_INT_DEF(flags);
PARAM_POINTER_DEF(victim, FTranslatedLineTarget); PARAM_POINTER_DEF(victim, FTranslatedLineTarget);
PARAM_FLOAT_DEF(offsetz); PARAM_FLOAT_DEF(offsetz);
PARAM_FLOAT_DEF(offsetforward);
PARAM_FLOAT_DEF(offsetside);
int acdmg; int acdmg;
if (puffType == nullptr) puffType = PClass::FindActor("BulletPuff"); // P_LineAttack does not work without a puff to take info from. if (puffType == nullptr) puffType = PClass::FindActor("BulletPuff"); // P_LineAttack does not work without a puff to take info from.
auto puff = P_LineAttack(self, angle, distance, pitch, damage, damageType, puffType, flags, victim, &acdmg, offsetz); auto puff = P_LineAttack(self, angle, distance, pitch, damage, damageType, puffType, flags, victim, &acdmg, offsetz, offsetforward, offsetside);
if (numret > 0) ret[0].SetObject(puff); if (numret > 0) ret[0].SetObject(puff);
if (numret > 1) ret[1].SetInt(acdmg), numret = 2; if (numret > 1) ret[1].SetInt(acdmg), numret = 2;
return numret; return numret;

View file

@ -721,7 +721,7 @@ void FStateDefinitions::RetargetStates (intptr_t count, const char *target)
// //
//========================================================================== //==========================================================================
FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, PClassActor *mytype, char *name) FState *FStateDefinitions::ResolveGotoLabel (PClassActor *mytype, char *name)
{ {
PClassActor *type = mytype; PClassActor *type = mytype;
FState *state; FState *state;
@ -741,7 +741,6 @@ FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, PClassActor *mytype,
if (stricmp (classname, "Super") == 0) if (stricmp (classname, "Super") == 0)
{ {
type = ValidateActor(type->ParentClass); type = ValidateActor(type->ParentClass);
actor = GetDefaultByType(type);
} }
else else
{ {
@ -763,7 +762,6 @@ FState *FStateDefinitions::ResolveGotoLabel (AActor *actor, PClassActor *mytype,
if (type != stype) if (type != stype)
{ {
type = static_cast<PClassActor *>(stype); type = static_cast<PClassActor *>(stype);
actor = GetDefaultByType (type);
} }
} }
} }
@ -836,16 +834,16 @@ void FStateDefinitions::FixStatePointers (PClassActor *actor, TArray<FStateDefin
// //
//========================================================================== //==========================================================================
void FStateDefinitions::ResolveGotoLabels (PClassActor *actor, AActor *defaults, TArray<FStateDefine> & list) void FStateDefinitions::ResolveGotoLabels (PClassActor *actor, TArray<FStateDefine> & list)
{ {
for (unsigned i = 0; i < list.Size(); i++) for (unsigned i = 0; i < list.Size(); i++)
{ {
if (list[i].State != NULL && list[i].DefineFlags == SDF_LABEL) if (list[i].State != NULL && list[i].DefineFlags == SDF_LABEL)
{ // It's not a valid state, so it must be a label string. Resolve it. { // It's not a valid state, so it must be a label string. Resolve it.
list[i].State = ResolveGotoLabel (defaults, actor, (char *)list[i].State); list[i].State = ResolveGotoLabel (actor, (char *)list[i].State);
list[i].DefineFlags = SDF_STATE; list[i].DefineFlags = SDF_STATE;
} }
if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, defaults, list[i].Children); if (list[i].Children.Size() > 0) ResolveGotoLabels(actor, list[i].Children);
} }
} }
@ -1004,7 +1002,7 @@ int FStateDefinitions::AddStates(FState *state, const char *framechars, const FS
// //
//========================================================================== //==========================================================================
int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults) int FStateDefinitions::FinishStates(PClassActor *actor)
{ {
int count = StateArray.Size(); int count = StateArray.Size();
@ -1023,7 +1021,7 @@ int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults)
FixStatePointers(actor, StateLabels); FixStatePointers(actor, StateLabels);
// Fix state pointers that are gotos // Fix state pointers that are gotos
ResolveGotoLabels(actor, defaults, StateLabels); ResolveGotoLabels(actor, StateLabels);
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
@ -1047,7 +1045,7 @@ int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults)
break; break;
case SDF_LABEL: case SDF_LABEL:
realstates[i].NextState = ResolveGotoLabel(defaults, actor, (char *)realstates[i].NextState); realstates[i].NextState = ResolveGotoLabel(actor, (char *)realstates[i].NextState);
break; break;
} }
} }
@ -1055,7 +1053,7 @@ int FStateDefinitions::FinishStates(PClassActor *actor, AActor *defaults)
else else
{ {
// Fix state pointers that are gotos // Fix state pointers that are gotos
ResolveGotoLabels(actor, defaults, StateLabels); ResolveGotoLabels(actor, StateLabels);
} }
return count; return count;
} }

View file

@ -945,3 +945,25 @@ int P_Thing_Warp(AActor *caller, AActor *reference, double xofs, double yofs, do
caller->SetOrigin(old, true); caller->SetOrigin(old, true);
return false; return false;
} }
//==========================================================================
//
// A_Warp
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, Warp)
{
PARAM_SELF_PROLOGUE(AActor)
PARAM_OBJECT_DEF(destination, AActor)
PARAM_FLOAT_DEF(xofs)
PARAM_FLOAT_DEF(yofs)
PARAM_FLOAT_DEF(zofs)
PARAM_ANGLE_DEF(angle)
PARAM_INT_DEF(flags)
PARAM_FLOAT_DEF(heightoffset)
PARAM_FLOAT_DEF(radiusoffset)
PARAM_ANGLE_DEF(pitch)
ACTION_RETURN_INT(!!P_Thing_Warp(self, destination, xofs, yofs, zofs, angle, flags, heightoffset, radiusoffset, pitch));
}

View file

@ -551,6 +551,16 @@ int main(int argc, char** argv)
{ {
s_restartedFromWADPicker = true; s_restartedFromWADPicker = true;
} }
#if _DEBUG
else if (0 == strcmp(argument, "-wait_for_debugger"))
{
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:@GAMENAME];
[alert setInformativeText:@"Waiting for debugger..."];
[alert addButtonWithTitle:@"Continue"];
[alert runModal];
}
#endif // _DEBUG
else else
{ {
s_argvStorage.Push(argument); s_argvStorage.Push(argument);

View file

@ -38,6 +38,7 @@
#include "templates.h" #include "templates.h"
#include "i_system.h" #include "i_system.h"
#include "i_video.h" #include "i_video.h"
#include "m_argv.h"
#include "v_video.h" #include "v_video.h"
#include "v_pfx.h" #include "v_pfx.h"
#include "stats.h" #include "stats.h"
@ -343,7 +344,7 @@ bool SDLGLVideo::SetResolution (int width, int height, int bits)
// //
//========================================================================== //==========================================================================
bool SDLGLVideo::SetupPixelFormat(bool allowsoftware, int multisample) void SDLGLVideo::SetupPixelFormat(bool allowsoftware, int multisample, const int *glver)
{ {
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
@ -365,24 +366,18 @@ bool SDLGLVideo::SetupPixelFormat(bool allowsoftware, int multisample)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
} }
else if (glver[0] > 2)
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool SDLGLVideo::InitHardware (bool allowsoftware, int multisample)
{ {
if (!SetupPixelFormat(allowsoftware, multisample)) SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
{ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glver[0]);
Printf ("R_OPENGL: Reverting to software mode...\n"); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glver[1]);
return false; }
else
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
} }
return true;
} }
@ -391,32 +386,66 @@ bool SDLGLVideo::InitHardware (bool allowsoftware, int multisample)
SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen, bool bgra) SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen, bool bgra)
: SDLBaseFB (width, height, bgra) : SDLBaseFB (width, height, bgra)
{ {
// NOTE: Core profiles were added with GL 3.2, so there's no sense trying
// to set core 3.1 or 3.0. We could try a forward-compatible context
// instead, but that would be too restrictive (w.r.t. shaders).
static const int glvers[][2] = {
{ 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 },
{ 3, 3 }, { 3, 2 }, { 2, 0 },
{ 0, 0 },
};
int glveridx = 0;
int i; int i;
m_Lock=0; m_Lock=0;
UpdatePending = false; UpdatePending = false;
if (!static_cast<SDLGLVideo*>(Video)->InitHardware(false, 0)) const char *version = Args->CheckValue("-glversion");
if (version != NULL)
{ {
vid_renderer = 0; double gl_version = strtod(version, NULL) + 0.01;
return; int vermaj = (int)gl_version;
int vermin = (int)(gl_version*10.0) % 10;
while (glvers[glveridx][0] > vermaj || (glvers[glveridx][0] == vermaj &&
glvers[glveridx][1] > vermin))
{
glveridx++;
if (glvers[glveridx][0] == 0)
{
glveridx = 0;
break;
}
}
} }
FString caption; FString caption;
caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime());
for ( ; glvers[glveridx][0] > 0; ++glveridx)
{
static_cast<SDLGLVideo*>(Video)->SetupPixelFormat(false, 0, glvers[glveridx]);
Screen = SDL_CreateWindow (caption, Screen = SDL_CreateWindow (caption,
SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter), SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter),
width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)|SDL_WINDOW_OPENGL); SDL_WINDOWPOS_UNDEFINED_DISPLAY(vid_adapter),
width, height, (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0)|SDL_WINDOW_OPENGL
if (Screen == NULL) );
return; if (Screen != NULL)
{
GLContext = SDL_GL_CreateContext(Screen); GLContext = SDL_GL_CreateContext(Screen);
if (GLContext == NULL) if (GLContext != NULL)
{
m_supportsGamma = -1 != SDL_GetWindowGammaRamp(Screen,
m_origGamma[0], m_origGamma[1], m_origGamma[2]
);
return; return;
}
m_supportsGamma = -1 != SDL_GetWindowGammaRamp(Screen, m_origGamma[0], m_origGamma[1], m_origGamma[2]); SDL_DestroyWindow(Screen);
Screen = NULL;
}
}
} }
SDLGLFB::~SDLGLFB () SDLGLFB::~SDLGLFB ()

View file

@ -27,8 +27,7 @@ class SDLGLVideo : public IVideo
bool NextMode (int *width, int *height, bool *letterbox); bool NextMode (int *width, int *height, bool *letterbox);
bool SetResolution (int width, int height, int bits); bool SetResolution (int width, int height, int bits);
bool SetupPixelFormat(bool allowsoftware, int multisample); void SetupPixelFormat(bool allowsoftware, int multisample, const int *glver);
bool InitHardware (bool allowsoftware, int multisample);
private: private:
int IteratorMode; int IteratorMode;

View file

@ -1761,6 +1761,12 @@ void S_RelinkSound (AActor *from, AActor *to)
bool S_ChangeSoundVolume(AActor *actor, int channel, float volume) bool S_ChangeSoundVolume(AActor *actor, int channel, float volume)
{ {
// don't let volume get out of bounds
if (volume < 0.0)
volume = 0.0;
else if (volume > 1.0)
volume = 1.0;
for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan)
{ {
if (chan->SourceType == SOURCE_Actor && if (chan->SourceType == SOURCE_Actor &&

View file

@ -147,4 +147,5 @@ xx(TK_Offset, "'offset'")
xx(TK_Slow, "'slow'") xx(TK_Slow, "'slow'")
xx(TK_Bright, "'bright'") xx(TK_Bright, "'bright'")
xx(TK_Let, "'let'") xx(TK_Let, "'let'")
xx(TK_StaticConst, "'static const'")
#undef xx #undef xx

View file

@ -319,8 +319,6 @@ protected:
} }
public: public:
FxExpression *CheckIntForName();
virtual ~FxExpression() {} virtual ~FxExpression() {}
virtual FxExpression *Resolve(FCompileContext &ctx); virtual FxExpression *Resolve(FCompileContext &ctx);
@ -2132,7 +2130,6 @@ class FxLocalVariableDeclaration : public FxExpression
public: public:
int StackOffset = -1; int StackOffset = -1;
int RegNum = -1; int RegNum = -1;
bool constructed = false;
FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, int varflags, const FScriptPosition &p); FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, int varflags, const FScriptPosition &p);
~FxLocalVariableDeclaration(); ~FxLocalVariableDeclaration();

View file

@ -113,9 +113,9 @@ class FStateDefinitions
FStateDefine *FindStateAddress(const char *name); FStateDefine *FindStateAddress(const char *name);
FState *FindState(const char *name); FState *FindState(const char *name);
FState *ResolveGotoLabel(AActor *actor, PClassActor *mytype, char *name); FState *ResolveGotoLabel(PClassActor *mytype, char *name);
static void FixStatePointers(PClassActor *actor, TArray<FStateDefine> & list); static void FixStatePointers(PClassActor *actor, TArray<FStateDefine> & list);
void ResolveGotoLabels(PClassActor *actor, AActor *defaults, TArray<FStateDefine> & list); void ResolveGotoLabels(PClassActor *actor, TArray<FStateDefine> & list);
public: public:
FStateDefinitions() FStateDefinitions()
@ -129,7 +129,7 @@ public:
void AddStateLabel(const char *statename); void AddStateLabel(const char *statename);
int GetStateLabelIndex (FName statename); int GetStateLabelIndex (FName statename);
void InstallStates(PClassActor *info, AActor *defaults); void InstallStates(PClassActor *info, AActor *defaults);
int FinishStates(PClassActor *actor, AActor *defaults); int FinishStates(PClassActor *actor);
void MakeStateDefines(const PClassActor *cls); void MakeStateDefines(const PClassActor *cls);
void AddStateDefines(const FStateLabels *list); void AddStateDefines(const FStateLabels *list);

View file

@ -1225,6 +1225,15 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Truncate)
return 0; return 0;
} }
DEFINE_ACTION_FUNCTION(FStringStruct, Remove)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_UINT(index);
PARAM_UINT(remlen);
self->Remove(index, remlen);
return 0;
}
// CharAt and CharCodeAt is how JS does it, and JS is similar here in that it doesn't have char type as int. // CharAt and CharCodeAt is how JS does it, and JS is similar here in that it doesn't have char type as int.
DEFINE_ACTION_FUNCTION(FStringStruct, CharAt) DEFINE_ACTION_FUNCTION(FStringStruct, CharAt)
{ {
@ -1264,7 +1273,8 @@ DEFINE_ACTION_FUNCTION(FStringStruct, LastIndexOf)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(substr); PARAM_STRING(substr);
ACTION_RETURN_INT(self->LastIndexOf(substr)); PARAM_INT_DEF(endIndex);
ACTION_RETURN_INT(self->LastIndexOf(substr, endIndex));
} }
DEFINE_ACTION_FUNCTION(FStringStruct, ToUpper) DEFINE_ACTION_FUNCTION(FStringStruct, ToUpper)

View file

@ -899,6 +899,11 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret)
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", BC, reg.d[a]); ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", BC, reg.d[a]);
return 0; return 0;
} }
else if (reg.d[a] < 0)
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", reg.d[a]);
return 0;
}
NEXTOP; NEXTOP;
OP(BOUND_K): OP(BOUND_K):
@ -908,6 +913,11 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret)
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", konstd[BC], reg.d[a]); ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", konstd[BC], reg.d[a]);
return 0; return 0;
} }
else if (reg.d[a] < 0)
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", reg.d[a]);
return 0;
}
NEXTOP; NEXTOP;
OP(BOUND_R): OP(BOUND_R):
@ -917,6 +927,11 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret)
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", reg.d[B], reg.d[a]); ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", reg.d[B], reg.d[a]);
return 0; return 0;
} }
else if (reg.d[a] < 0)
{
ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Negative current index = %i\n", reg.d[a]);
return 0;
}
NEXTOP; NEXTOP;
OP(CONCAT): OP(CONCAT):

View file

@ -122,9 +122,9 @@ xx(THROW, throw, THROW, NOP, 0, 0), // A == 0: Throw exception object pB
// A == 2: (pB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH // A == 2: (pB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
// A == 3: (pkB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH // A == 3: (pkB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
// for A > 0, exception is stored in pC // for A > 0, exception is stored in pC
xx(BOUND, bound, RII16, NOP, 0, 0), // if rA >= BC, throw exception xx(BOUND, bound, RII16, NOP, 0, 0), // if rA < 0 or rA >= BC, throw exception
xx(BOUND_K, bound, LKI, NOP, 0, 0), // if rA >= const[BC], throw exception xx(BOUND_K, bound, LKI, NOP, 0, 0), // if rA < 0 or rA >= const[BC], throw exception
xx(BOUND_R, bound, RIRI, NOP, 0, 0), // if rA >= rB, throw exception xx(BOUND_R, bound, RIRI, NOP, 0, 0), // if rA < 0 or rA >= rB, throw exception
// String instructions. // String instructions.
xx(CONCAT, concat, RSRSRS, NOP, 0, 0), // sA = sB..sC xx(CONCAT, concat, RSRSRS, NOP, 0, 0), // sA = sB..sC

View file

@ -3495,7 +3495,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
case AST_IfStmt: case AST_IfStmt:
{ {
auto iff = static_cast<ZCC_IfStmt *>(ast); auto iff = static_cast<ZCC_IfStmt *>(ast);
return new FxIfStatement(ConvertNode(iff->Condition), ConvertNode(iff->TruePath), ConvertNode(iff->FalsePath), *ast); FxExpression *const truePath = ConvertImplicitScopeNode(ast, iff->TruePath);
FxExpression *const falsePath = ConvertImplicitScopeNode(ast, iff->FalsePath);
return new FxIfStatement(ConvertNode(iff->Condition), truePath, falsePath, *ast);
} }
case AST_IterationStmt: case AST_IterationStmt:
@ -3504,7 +3506,8 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
if (iter->CheckAt == ZCC_IterationStmt::End) if (iter->CheckAt == ZCC_IterationStmt::End)
{ {
assert(iter->LoopBumper == nullptr); assert(iter->LoopBumper == nullptr);
return new FxDoWhileLoop(ConvertNode(iter->LoopCondition), ConvertNode(iter->LoopStatement), *ast); FxExpression *const loop = ConvertImplicitScopeNode(ast, iter->LoopStatement);
return new FxDoWhileLoop(ConvertNode(iter->LoopCondition), loop, *ast);
} }
else if (iter->LoopBumper != nullptr) else if (iter->LoopBumper != nullptr)
{ {
@ -3520,7 +3523,8 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
} }
else else
{ {
return new FxWhileLoop(ConvertNode(iter->LoopCondition), ConvertNode(iter->LoopStatement), *ast); FxExpression *const loop = ConvertImplicitScopeNode(ast, iter->LoopStatement);
return new FxWhileLoop(ConvertNode(iter->LoopCondition), loop, *ast);
} }
} }
@ -3584,6 +3588,45 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
return nullptr; return nullptr;
} }
//==========================================================================
//
// Wrapper around ConvertNode() that adds a scope (a compound statement)
// when needed to avoid leaking of variable or contant to an outer scope:
//
// if (true) int i; else bool b[1];
// while (false) readonly<Actor> a;
// do static const float f[] = {0}; while (false);
//
// Accessing such variables outside of their statements is now an error
//
//==========================================================================
FxExpression *ZCCCompiler::ConvertImplicitScopeNode(ZCC_TreeNode *node, ZCC_Statement *nested)
{
assert(nullptr != node);
if (nullptr == nested)
{
return nullptr;
}
FxExpression *nestedExpr = ConvertNode(nested);
assert(nullptr != nestedExpr);
const EZCCTreeNodeType nestedType = nested->NodeType;
const bool needScope = AST_LocalVarStmt == nestedType || AST_StaticArrayStatement == nestedType;
if (needScope)
{
FxCompoundStatement *implicitCompound = new FxCompoundStatement(*node);
implicitCompound->Add(nestedExpr);
nestedExpr = implicitCompound;
}
return nestedExpr;
}
FArgumentList &ZCCCompiler::ConvertNodeList(FArgumentList &args, ZCC_TreeNode *head) FArgumentList &ZCCCompiler::ConvertNodeList(FArgumentList &args, ZCC_TreeNode *head)
{ {

View file

@ -145,6 +145,7 @@ private:
FxExpression *ConvertAST(PContainerType *cclass, ZCC_TreeNode *ast); FxExpression *ConvertAST(PContainerType *cclass, ZCC_TreeNode *ast);
FxExpression *ConvertNode(ZCC_TreeNode *node); FxExpression *ConvertNode(ZCC_TreeNode *node);
FxExpression *ConvertImplicitScopeNode(ZCC_TreeNode *node, ZCC_Statement *nested);
FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head); FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head);
DObject *Outer; DObject *Outer;

View file

@ -211,6 +211,7 @@ static void InitTokenMap()
TOKENDEF2(TK_Color, ZCC_COLOR, NAME_Color); TOKENDEF2(TK_Color, ZCC_COLOR, NAME_Color);
TOKENDEF2(TK_Sound, ZCC_SOUND, NAME_Sound); TOKENDEF2(TK_Sound, ZCC_SOUND, NAME_Sound);
TOKENDEF2(TK_Let, ZCC_LET, NAME_let); TOKENDEF2(TK_Let, ZCC_LET, NAME_let);
TOKENDEF2(TK_StaticConst, ZCC_STATICCONST,NAME_Staticconst);
TOKENDEF (TK_Identifier, ZCC_IDENTIFIER); TOKENDEF (TK_Identifier, ZCC_IDENTIFIER);
TOKENDEF (TK_StringConst, ZCC_STRCONST); TOKENDEF (TK_StringConst, ZCC_STRCONST);

View file

@ -59,7 +59,6 @@ extern void ChildSigHandler (int signum);
#include "s_sound.h" #include "s_sound.h"
#include "m_swap.h" #include "m_swap.h"
#include "i_cd.h" #include "i_cd.h"
#include "tempfiles.h"
#include "templates.h" #include "templates.h"
#include "stats.h" #include "stats.h"
#include "timidity/timidity.h" #include "timidity/timidity.h"

View file

@ -1,5 +1,4 @@
#include "tempfiles.h"
#include "oplsynth/opl_mus_player.h" #include "oplsynth/opl_mus_player.h"
#include "c_cvars.h" #include "c_cvars.h"
#include "mus2midi.h" #include "mus2midi.h"

View file

@ -42,6 +42,7 @@
#include "cmdlib.h" #include "cmdlib.h"
#include "templates.h" #include "templates.h"
#include "version.h" #include "version.h"
#include "tmpfileplus.h"
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> #include <unistd.h>
@ -81,13 +82,12 @@ public:
protected: protected:
bool LaunchTimidity(); bool LaunchTimidity();
FTempFileName DiskName; char* DiskName;
#ifdef _WIN32 #ifdef _WIN32
HANDLE ReadWavePipe; HANDLE ReadWavePipe;
HANDLE WriteWavePipe; HANDLE WriteWavePipe;
HANDLE ChildProcess; HANDLE ChildProcess;
FString CommandLine; FString CommandLine;
size_t LoopPos;
bool Validated; bool Validated;
bool ValidateTimidity(); bool ValidateTimidity();
#else // _WIN32 #else // _WIN32
@ -166,7 +166,7 @@ CUSTOM_CVAR (Int, timidity_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//========================================================================== //==========================================================================
TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args) TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args)
: DiskName("zmid"), : DiskName(nullptr),
#ifdef _WIN32 #ifdef _WIN32
ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE), ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE),
ChildProcess(INVALID_HANDLE_VALUE), ChildProcess(INVALID_HANDLE_VALUE),
@ -194,12 +194,6 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args)
CommandLine += "\" "; CommandLine += "\" ";
} }
#endif #endif
if (DiskName == NULL)
{
Printf(PRINT_BOLD, "Could not create temp music file\n");
return;
}
} }
//========================================================================== //==========================================================================
@ -210,6 +204,12 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args)
TimidityPPMIDIDevice::~TimidityPPMIDIDevice () TimidityPPMIDIDevice::~TimidityPPMIDIDevice ()
{ {
if (nullptr != DiskName)
{
remove(DiskName);
free(DiskName);
}
#if _WIN32 #if _WIN32
if (WriteWavePipe != INVALID_HANDLE_VALUE) if (WriteWavePipe != INVALID_HANDLE_VALUE)
{ {
@ -252,17 +252,12 @@ bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
return false; return false;
} }
// Tell TiMidity++ whether it should loop or not
#ifdef _WIN32
CommandLine.LockBuffer()[LoopPos] = looping ? 'l' : ' ';
CommandLine.UnlockBuffer();
#endif
Looping = looping; Looping = looping;
// Write MIDI song to temporary file // Write MIDI song to temporary file
song->CreateSMF(midi, looping ? 0 : 1); song->CreateSMF(midi, looping ? 0 : 1);
f = fopen(DiskName, "wb"); f = tmpfileplus(nullptr, "zmid", &DiskName, 1);
if (f == NULL) if (f == NULL)
{ {
Printf(PRINT_BOLD, "Could not open temp music file\n"); Printf(PRINT_BOLD, "Could not open temp music file\n");
@ -275,6 +270,16 @@ bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
{ {
Printf(PRINT_BOLD, "Could not write temp music file\n"); Printf(PRINT_BOLD, "Could not write temp music file\n");
} }
#ifdef _WIN32
CommandLine.AppendFormat("-o - -Ors%c%c%c -id%c %s",
timidity_stereo ? 'S' : 'M',
timidity_8bit ? '8' : '1',
timidity_byteswap ? 'x' : ' ',
looping ? 'l' : ' ',
DiskName);
#endif
return false; return false;
} }
@ -342,20 +347,6 @@ int TimidityPPMIDIDevice::Open(MidiCallback callback, void *userdata)
} }
} }
#ifdef _WIN32
CommandLine += "-o - -Ors";
CommandLine += timidity_stereo ? 'S' : 'M';
CommandLine += timidity_8bit ? '8' : '1';
if (timidity_byteswap)
{
CommandLine += 'x';
}
LoopPos = CommandLine.Len() + 4;
CommandLine += " -idl ";
CommandLine += DiskName.GetName();
#endif
return 0; return 0;
} }
@ -463,12 +454,12 @@ bool TimidityPPMIDIDevice::ValidateTimidity()
bool TimidityPPMIDIDevice::LaunchTimidity () bool TimidityPPMIDIDevice::LaunchTimidity ()
{ {
#ifdef _WIN32 if (ExeName.IsEmpty() || nullptr == DiskName)
if (CommandLine.IsEmpty())
{ {
return false; return false;
} }
#ifdef _WIN32
DPrintf (DMSG_NOTIFY, "cmd: \x1cG%s\n", CommandLine.GetChars()); DPrintf (DMSG_NOTIFY, "cmd: \x1cG%s\n", CommandLine.GetChars());
STARTUPINFO startup = { sizeof(startup), }; STARTUPINFO startup = { sizeof(startup), };
@ -516,11 +507,6 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
} }
return false; return false;
#else #else
if (ExeName.IsEmpty())
{
return false;
}
if (WavePipe[0] != -1 && WavePipe[1] == -1 && Stream != NULL) if (WavePipe[0] != -1 && WavePipe[1] == -1 && Stream != NULL)
{ {
// Timidity was previously launched, so the write end of the pipe // Timidity was previously launched, so the write end of the pipe
@ -579,7 +565,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity ()
arglist.push_back("-"); arglist.push_back("-");
arglist.push_back(outmodearg.c_str()); arglist.push_back(outmodearg.c_str());
arglist.push_back(ifacearg.c_str()); arglist.push_back(ifacearg.c_str());
arglist.push_back(DiskName.GetName()); arglist.push_back(DiskName);
DPrintf(DMSG_NOTIFY, "Timidity EXE: \x1cG%s\n", exename); DPrintf(DMSG_NOTIFY, "Timidity EXE: \x1cG%s\n", exename);
int i = 1; int i = 1;

View file

@ -57,7 +57,6 @@
#include "i_module.h" #include "i_module.h"
#include "i_music.h" #include "i_music.h"
#include "i_musicinterns.h" #include "i_musicinterns.h"
#include "tempfiles.h"
#include "cmdlib.h" #include "cmdlib.h"
FModule OpenALModule{"OpenAL"}; FModule OpenALModule{"OpenAL"};

View file

@ -39,6 +39,7 @@
#include <string.h> #include <string.h>
#include <new> #include <new>
#include <utility> #include <utility>
#include <iterator>
#if !defined(_WIN32) #if !defined(_WIN32)
#include <inttypes.h> // for intptr_t #include <inttypes.h> // for intptr_t
@ -48,13 +49,37 @@
#include "m_alloc.h" #include "m_alloc.h"
template<typename T> class TIterator template<typename T> class TIterator : public std::iterator<std::random_access_iterator_tag, T>
{ {
public: public:
typedef typename TIterator::value_type value_type;
typedef typename TIterator::difference_type difference_type;
typedef typename TIterator::pointer pointer;
typedef typename TIterator::reference reference;
typedef typename TIterator::iterator_category iterator_category;
TIterator(T* ptr = nullptr) { m_ptr = ptr; } TIterator(T* ptr = nullptr) { m_ptr = ptr; }
bool operator==(const TIterator<T>& other) const { return (m_ptr == other.m_ptr); }
bool operator!=(const TIterator<T>& other) const { return (m_ptr != other.m_ptr); } // Comparison operators
TIterator<T> &operator++() { ++m_ptr; return (*this); } bool operator==(const TIterator &other) const { return m_ptr == other.m_ptr; }
bool operator!=(const TIterator &other) const { return m_ptr != other.m_ptr; }
bool operator< (const TIterator &other) const { return m_ptr < other.m_ptr; }
bool operator<=(const TIterator &other) const { return m_ptr <= other.m_ptr; }
bool operator> (const TIterator &other) const { return m_ptr > other.m_ptr; }
bool operator>=(const TIterator &other) const { return m_ptr >= other.m_ptr; }
// Arithmetic operators
TIterator &operator++() { ++m_ptr; return *this; }
TIterator operator++(int) { pointer tmp = m_ptr; ++*this; return TIterator(tmp); }
TIterator &operator--() { --m_ptr; return *this; }
TIterator operator--(int) { pointer tmp = m_ptr; --*this; return TIterator(tmp); }
TIterator &operator+=(difference_type offset) { m_ptr += offset; return *this; }
TIterator operator+(difference_type offset) const { return TIterator(m_ptr + offset); }
friend TIterator operator+(difference_type offset, const TIterator &other) { return TIterator(offset + other.m_ptr); }
TIterator &operator-=(difference_type offset) { m_ptr -= offset; return *this; }
TIterator operator-(difference_type offset) const { return TIterator(m_ptr - offset); }
difference_type operator-(const TIterator &other) const { return m_ptr - other.m_ptr; }
T &operator*() { return *m_ptr; } T &operator*() { return *m_ptr; }
const T &operator*() const { return *m_ptr; } const T &operator*() const { return *m_ptr; }
T* operator->() { return m_ptr; } T* operator->() { return m_ptr; }
@ -132,10 +157,10 @@ public:
Count = 0; Count = 0;
Array = NULL; Array = NULL;
} }
TArray (int max) TArray (int max, bool reserve = false)
{ {
Most = max; Most = max;
Count = 0; Count = reserve? max : 0;
Array = (T *)M_Malloc (sizeof(T)*max); Array = (T *)M_Malloc (sizeof(T)*max);
} }
TArray (const TArray<T,TT> &other) TArray (const TArray<T,TT> &other)

View file

@ -1,57 +0,0 @@
/*
** tempfiles.cpp
** Temporary name generator. Deletes the temporary file when deconstructed.
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
#include <stdio.h>
#include <stdlib.h>
#include "tempfiles.h"
FTempFileName::FTempFileName (const char *prefix)
{
// Under Linux, ld will complain that tempnam is dangerous, and
// mkstemp should be used instead. However, there is no mkstemp
// under VC++, and even if there was, I still need to know the
// file name so that it can be used as input to Timidity.
Name = tempnam (NULL, prefix);
}
FTempFileName::~FTempFileName ()
{
if (Name != NULL)
{
remove (Name);
free (Name);
Name = NULL;
}
}

View file

@ -1,61 +0,0 @@
/*
** tempfiles.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
#ifndef __TEMPFILES_H__
#define __TEMPFILES_H__
#ifdef _MSC_VER
#pragma once
#endif
#include <stdlib.h>
// Returns a file name suitable for use as a temp file.
// If you create a file with this name (and presumably you
// will), it will be deleted automatically by this class's
// destructor.
class FTempFileName
{
public:
FTempFileName (const char *prefix=NULL);
~FTempFileName ();
operator const char * () { return Name; }
const char * GetName () const { return Name; }
private:
char *Name;
};
#endif //__TEMPFILES_H__

337
src/tmpfileplus.c Normal file
View file

@ -0,0 +1,337 @@
/* $Id: tmpfileplus.c $ */
/*
* $Date: 2016-06-01 03:31Z $
* $Revision: 2.0.0 $
* $Author: dai $
*/
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2012-16 David Ireland, DI Management Services Pty Ltd
* <http://www.di-mgt.com.au/contact/>.
*/
/*
* NAME
* tmpfileplus - create a unique temporary file
*
* SYNOPSIS
* FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep)
*
* DESCRIPTION
* The tmpfileplus() function opens a unique temporary file in binary
* read/write (w+b) mode. The file is opened with the O_EXCL flag,
* guaranteeing that the caller is the only user. The filename will consist
* of the string given by `prefix` followed by 10 random characters. If
* `prefix` is NULL, then the string "tmp." will be used instead. The file
* will be created in an appropriate directory chosen by the first
* successful attempt in the following sequence:
*
* a) The directory given by the `dir` argument (so the caller can specify
* a secure directory to take precedence).
*
* b) The directory name in the environment variables:
*
* (i) "TMP" [Windows only]
* (ii) "TEMP" [Windows only]
* (iii) "TMPDIR" [Unix only]
*
* c) `P_tmpdir` as defined in <stdio.h> [Unix only] (in Windows, this is
* usually "\", which is no good).
*
* d) The current working directory.
*
* If a file cannot be created in any of the above directories, then the
* function fails and NULL is returned.
*
* If the argument `pathname` is not a null pointer, then it will point to
* the full pathname of the file. The pathname is allocated using `malloc`
* and therefore should be freed by `free`.
*
* If `keep` is nonzero and `pathname` is not a null pointer, then the file
* will be kept after it is closed. Otherwise the file will be
* automatically deleted when it is closed or the program terminates.
*
*
* RETURN VALUE
* The tmpfileplus() function returns a pointer to the open file stream,
* or NULL if a unique file cannot be opened.
*
*
* ERRORS
* ENOMEM Not enough memory to allocate filename.
*
*/
/* ADDED IN v2.0 */
/*
* NAME
* tmpfileplus_f - create a unique temporary file with filename stored in a fixed-length buffer
*
* SYNOPSIS
* FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep);
*
* DESCRIPTION
* Same as tmpfileplus() except receives filename in a fixed-length buffer. No allocated memory to free.
* ERRORS
* E2BIG Resulting filename is too big for the buffer `pathnamebuf`.
*/
#include "tmpfileplus.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
/* Non-ANSI include files that seem to work in both MSVC and Linux */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _WIN32
#include <io.h>
#endif
#ifdef _WIN32
/* MSVC nags to enforce ISO C++ conformant function names with leading "_",
* so we define our own function names to avoid whingeing compilers...
*/
#define OPEN_ _open
#define FDOPEN_ _fdopen
#else
#define OPEN_ open
#define FDOPEN_ fdopen
#endif
/* DEBUGGING STUFF */
#if defined(_DEBUG) && defined(SHOW_DPRINTF)
#define DPRINTF1(s, a1) printf(s, a1)
#else
#define DPRINTF1(s, a1)
#endif
#ifdef _WIN32
#define FILE_SEPARATOR "\\"
#else
#define FILE_SEPARATOR "/"
#endif
#define RANDCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
#define NRANDCHARS (sizeof(RANDCHARS) - 1)
/** Replace each byte in string s with a random character from TEMPCHARS */
static char *set_randpart(char *s)
{
size_t i;
unsigned int r;
static unsigned int seed; /* NB static */
if (seed == 0)
{ /* First time set our seed using current time and clock */
seed = ((unsigned)time(NULL)<<8) ^ (unsigned)clock();
}
srand(seed++);
for (i = 0; i < strlen(s); i++)
{
r = rand() % NRANDCHARS;
s[i] = (RANDCHARS)[r];
}
return s;
}
/** Return 1 if path is a valid directory otherwise 0 */
static int is_valid_dir(const char *path)
{
struct stat st;
if ((stat(path, &st) == 0) && (st.st_mode & S_IFDIR))
return 1;
return 0;
}
/** Call getenv and save a copy in buf */
static char *getenv_save(const char *varname, char *buf, size_t bufsize)
{
char *ptr = getenv(varname);
buf[0] = '\0';
if (ptr)
{
strncpy(buf, ptr, bufsize);
buf[bufsize-1] = '\0';
return buf;
}
return NULL;
}
/**
* Try and create a randomly-named file in directory `tmpdir`.
* If successful, allocate memory and set `tmpname_ptr` to full filepath, and return file pointer;
* otherwise return NULL.
* If `keep` is zero then create the file as temporary and it should not exist once closed.
*/
static FILE *mktempfile_internal(const char *tmpdir, const char *pfx, char **tmpname_ptr, int keep)
/* PRE:
* pfx is not NULL and points to a valid null-terminated string
* tmpname_ptr is not NULL.
*/
{
FILE *fp;
int fd;
char randpart[] = "1234567890";
size_t lentempname;
int i;
char *tmpname = NULL;
int oflag, pmode;
/* In Windows, we use the _O_TEMPORARY flag with `open` to ensure the file is deleted when closed.
* In Unix, we use the unlink function after opening the file. (This does not work in Windows,
* which does not allow an open file to be unlinked.)
*/
#ifdef _WIN32
/* MSVC flags */
oflag = _O_BINARY|_O_CREAT|_O_RDWR;
if (!keep)
oflag |= _O_TEMPORARY;
pmode = _S_IREAD | _S_IWRITE;
#else
/* Standard POSIX flags */
oflag = O_CREAT|O_RDWR;
pmode = S_IRUSR|S_IWUSR;
#endif
if (!tmpdir || !is_valid_dir(tmpdir)) {
errno = ENOENT;
return NULL;
}
lentempname = strlen(tmpdir) + strlen(FILE_SEPARATOR) + strlen(pfx) + strlen(randpart);
DPRINTF1("lentempname=%d\n", lentempname);
tmpname = malloc(lentempname + 1);
if (!tmpname)
{
errno = ENOMEM;
return NULL;
}
/* If we don't manage to create a file after 10 goes, there is something wrong... */
for (i = 0; i < 10; i++)
{
sprintf(tmpname, "%s%s%s%s", tmpdir, FILE_SEPARATOR, pfx, set_randpart(randpart));
DPRINTF1("[%s]\n", tmpname);
fd = OPEN_(tmpname, oflag, pmode);
if (fd != -1) break;
}
DPRINTF1("strlen(tmpname)=%d\n", strlen(tmpname));
if (fd != -1)
{ /* Success, so return user a proper ANSI C file pointer */
fp = FDOPEN_(fd, "w+b");
errno = 0;
#ifndef _WIN32
/* [Unix only] And make sure the file will be deleted once closed */
if (!keep) remove(tmpname);
#endif
}
else
{ /* We failed */
fp = NULL;
}
if (!fp)
{
free(tmpname);
tmpname = NULL;
}
*tmpname_ptr = tmpname;
return fp;
}
/**********************/
/* EXPORTED FUNCTIONS */
/**********************/
FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep)
{
FILE *fp = NULL;
char *tmpname = NULL;
char *tmpdir = NULL;
const char *pfx = (prefix ? prefix : "tmp.");
char *tempdirs[12] = { 0 };
char env1[FILENAME_MAX+1] = { 0 };
char env2[FILENAME_MAX+1] = { 0 };
char env3[FILENAME_MAX+1] = { 0 };
int ntempdirs = 0;
int i;
/* Set up a list of temp directories we will try in order */
i = 0;
tempdirs[i++] = (char *)dir;
#ifdef _WIN32
tempdirs[i++] = getenv_save("TMP", env1, sizeof(env1));
tempdirs[i++] = getenv_save("TEMP", env2, sizeof(env2));
#else
tempdirs[i++] = getenv_save("TMPDIR", env3, sizeof(env3));
tempdirs[i++] = P_tmpdir;
#endif
tempdirs[i++] = ".";
ntempdirs = i;
errno = 0;
/* Work through list we set up before, and break once we are successful */
for (i = 0; i < ntempdirs; i++)
{
tmpdir = tempdirs[i];
DPRINTF1("Trying tmpdir=[%s]\n", tmpdir);
fp = mktempfile_internal(tmpdir, pfx, &tmpname, keep);
if (fp) break;
}
/* If we succeeded and the user passed a pointer, set it to the alloc'd pathname: the user must free this */
if (fp && pathname)
*pathname = tmpname;
else /* Otherwise, free the alloc'd memory */
free(tmpname);
return fp;
}
/* Same as tmpfileplus() but with fixed length buffer for output filename and no memory allocation */
FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep)
{
char *tmpbuf = NULL;
FILE *fp;
/* If no buffer provided, do the normal way */
if (!pathnamebuf || (int)pathsize <= 0) {
return tmpfileplus(dir, prefix, NULL, keep);
}
/* Call with a temporary buffer */
fp = tmpfileplus(dir, prefix, &tmpbuf, keep);
if (fp && strlen(tmpbuf) > pathsize - 1) {
/* Succeeded but not enough room in output buffer, so clean up and return an error */
pathnamebuf[0] = 0;
fclose(fp);
if (keep) remove(tmpbuf);
free(tmpbuf);
errno = E2BIG;
return NULL;
}
/* Copy name into buffer */
strcpy(pathnamebuf, tmpbuf);
free(tmpbuf);
return fp;
}

61
src/tmpfileplus.h Normal file
View file

@ -0,0 +1,61 @@
/* $Id: tmpfileplus.h $ */
/*
* $Date: 2016-06-01 03:31Z $
* $Revision: 2.0.0 $
* $Author: dai $
*/
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2012-16 David Ireland, DI Management Services Pty Ltd
* <http://www.di-mgt.com.au/contact/>.
*/
#if _MSC_VER > 1000
#pragma once
#endif
#ifndef TMPFILEPLUS_H_
#define TMPFILEPLUS_H_
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Create a unique temporary file.
@param dir (optional) directory to create file. If NULL use default TMP directory.
@param prefix (optional) prefix for file name. If NULL use "tmp.".
@param pathname (optional) pointer to a buffer to receive the temp filename.
Allocated using `malloc()`; user to free. Ignored if NULL.
@param keep If `keep` is nonzero and `pathname` is not NULL, then keep the file after closing.
Otherwise file is automatically deleted when closed.
@return Pointer to stream opened in binary read/write (w+b) mode, or a null pointer on error.
@exception ENOMEM Not enough memory to allocate filename.
*/
FILE *tmpfileplus(const char *dir, const char *prefix, char **pathname, int keep);
/** Create a unique temporary file with filename stored in a fixed-length buffer.
@param dir (optional) directory to create file. If NULL use default directory.
@param prefix (optional) prefix for file name. If NULL use "tmp.".
@param pathnamebuf (optional) buffer to receive full pathname of temporary file. Ignored if NULL.
@param pathsize Size of buffer to receive filename and its terminating null character.
@param keep If `keep` is nonzero and `pathname` is not NULL, then keep the file after closing.
Otherwise file is automatically deleted when closed.
@return Pointer to stream opened in binary read/write (w+b) mode, or a null pointer on error.
@exception E2BIG Resulting filename is too big for the buffer `pathnamebuf`.
*/
FILE *tmpfileplus_f(const char *dir, const char *prefix, char *pathnamebuf, size_t pathsize, int keep);
#define TMPFILE_KEEP 1
#ifdef __cplusplus
}
#endif
#endif /* end TMPFILEPLUS_H_ */

View file

@ -345,6 +345,7 @@ F481922F4881F74760F3C0437FD5EDD0 // map03
8B2AC8D4DB4A49A5DCCBB067E04434D6 // The Hell Factory Hub One, map04 8B2AC8D4DB4A49A5DCCBB067E04434D6 // The Hell Factory Hub One, map04
65A1EB4C87386F290816660A52932FF1 // Master Levels, garrison.wad 65A1EB4C87386F290816660A52932FF1 // Master Levels, garrison.wad
3DEE4EFEFAF3260C800A30734F54CE75 // Hellbound, map14 3DEE4EFEFAF3260C800A30734F54CE75 // Hellbound, map14
5FAA25F5A6AAB3409CAE0AF87F910341 // DOOM.wad e1m6
{ {
rebuildnodes rebuildnodes
} }

View file

@ -598,7 +598,7 @@ class Actor : Thinker native
native virtual int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0); native virtual int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0);
native void PoisonMobj (Actor inflictor, Actor source, int damage, int duration, int period, Name type); native void PoisonMobj (Actor inflictor, Actor source, int damage, int duration, int period, Name type);
native double AimLineAttack(double angle, double distance, out FTranslatedLineTarget pLineTarget = null, double vrange = 0., int flags = 0, Actor target = null, Actor friender = null); native double AimLineAttack(double angle, double distance, out FTranslatedLineTarget pLineTarget = null, double vrange = 0., int flags = 0, Actor target = null, Actor friender = null);
native Actor, int LineAttack(double angle, double distance, double pitch, int damage, Name damageType, class<Actor> pufftype, int flags = 0, out FTranslatedLineTarget victim = null, double offsetz = 0.); native Actor, int LineAttack(double angle, double distance, double pitch, int damage, Name damageType, class<Actor> pufftype, int flags = 0, out FTranslatedLineTarget victim = null, double offsetz = 0., double offsetforward = 0., double offsetside = 0.);
native bool CheckSight(Actor target, int flags = 0); native bool CheckSight(Actor target, int flags = 0);
native bool IsVisible(Actor other, bool allaround, LookExParams params = null); native bool IsVisible(Actor other, bool allaround, LookExParams params = null);
native bool HitFriend(); native bool HitFriend();
@ -683,6 +683,7 @@ class Actor : Thinker native
native float AccuracyFactor(); native float AccuracyFactor();
native bool MorphMonster (Class<Actor> spawntype, int duration, int style, Class<Actor> enter_flash, Class<Actor> exit_flash); native bool MorphMonster (Class<Actor> spawntype, int duration, int style, Class<Actor> enter_flash, Class<Actor> exit_flash);
action native void SetCamera(Actor cam, bool revert = false); action native void SetCamera(Actor cam, bool revert = false);
native bool Warp(Actor dest, double xofs = 0, double yofs = 0, double zofs = 0, double angle = 0, int flags = 0, double heightoffset = 0, double radiusoffset = 0, double pitch = 0);
// DECORATE compatible functions // DECORATE compatible functions
native clearscope int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT) const; native clearscope int CountInv(class<Inventory> itemtype, int ptr_select = AAPTR_DEFAULT) const;

View file

@ -684,11 +684,12 @@ struct StringStruct native
native String Left(int len) const; native String Left(int len) const;
native String Mid(int pos = 0, int len = 2147483647) const; native String Mid(int pos = 0, int len = 2147483647) const;
native void Truncate(int newlen); native void Truncate(int newlen);
native void Remove(int index, int remlen);
native String CharAt(int pos) const; native String CharAt(int pos) const;
native int CharCodeAt(int pos) const; native int CharCodeAt(int pos) const;
native String Filter(); native String Filter();
native int IndexOf(String substr, int startIndex = 0) const; native int IndexOf(String substr, int startIndex = 0) const;
native int LastIndexOf(String substr) const; native int LastIndexOf(String substr, int endIndex = 2147483647) const;
native void ToUpper(); native void ToUpper();
native void ToLower(); native void ToLower();
native int ToInt(int base = 0) const; native int ToInt(int base = 0) const;

View file

@ -879,10 +879,13 @@ enum EAimFlags
enum ELineAttackFlags enum ELineAttackFlags
{ {
LAF_ISMELEEATTACK = 1, LAF_ISMELEEATTACK = 1,
LAF_NORANDOMPUFFZ = 2, LAF_NORANDOMPUFFZ = 1 << 1,
LAF_NOIMPACTDECAL = 4, LAF_NOIMPACTDECAL = 1 << 2,
LAF_NOINTERACT = 8, LAF_NOINTERACT = 1 << 3,
LAF_OVERRIDEZ = 32, LAF_TARGETISSOURCE = 1 << 4,
LAF_OVERRIDEZ = 1 << 5,
LAF_ABSOFFSET = 1 << 6,
LAF_ABSPOSITION = 1 << 7,
} }
const DEFMELEERANGE = 64; const DEFMELEERANGE = 64;