- Update to ZDoom r718

- Added ModPlug as an option to play MOD music because FMOD is not that good for it.


git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@4 b0f79afe-0144-0410-b225-9a4edf0717df
This commit is contained in:
Christoph Oelckers 2008-01-27 15:34:47 +00:00
parent f18564933d
commit 179ea921f9
214 changed files with 46506 additions and 12448 deletions

View file

@ -1,4 +1,344 @@
January 27, 2008 (Changes by Graf Zahl)
- Changed license for r_data.cpp because there isn't anything of id's original
code left in that file.
- Cleaned up r_data.cpp.
- Fixed: FTextureManager::FindTexture should not print error messages if it
doesn't find the texture.
- Added Karate Chris's patch for fixing Strife quit messages.
January 26, 2008
- Added preloading of fonts to reduce the chance that characters from a single
font will require more than one hardware texture to display.
- Fixed: P_RailAttack() crashed if you didn't specify a puff for a rail.
- Decided that allowing arbitrary alpha values for color remaps isn't so hot.
Changed it back the way it was.
January 26, 2008 (Changes by Graf Zahl)
- Got rid of most TexMan.AddPatch calls because they are no longer needed.
- Got rid of R_InitPatches because the new texture init code needs to preload
everything to work correctly.
- Rewrote texture manager initialization to order textures primarily by WAD
rather than by type. This way later textures will always override earlier
ones. The only exception is that TEX_MiscPatch are only used as a fallback
if nothing else can be found.
- Optimized the tryany case of FTextureManager::CheckForTexture. It is not
necessary to scan the hash chain twice. The required information can be
retrieved during the first pass as easily and even offers a little more
control.
- Made FFont destructor virtual.
- Added 'Ice' translation to DECORATE.
- Added Karate Chris's patch for teamplay MAPINFO option.
- Added Karate Chris's patch for custom damage color per player class.
- Added Karate Chris's patch for respawnlimit skill property.
- Added Karate Chris's patch for Strife quit messages.
- Fixed: P_RailAttack ignored the puff's damage type.
- Fixed: ACS used incompatible values for APROP_RenderStyle. It needs to use
the exact same values as previous ZDoom versions
- Added a DECORATE 'stencilcolor' property so that the stencil render style
can be used.
- Added some NULL pointer checks to the font loading code.
January 25, 2008
- Undid some of the changes from lempar.c v1.30->v1.31, because it broke
error handling.
- Fixed: dehsupp/scanner.re defined "}" as the token RPAREN. dehsupp/parse.y
also defined action_list_def as needing a RBARCE. I'm surprised it worked
at all before. I guess Lemon really was too accepting. I have verified that
the lump it outputs is unchanged from before.
- Changed the way that xlatcc handles include statements so that I don't need
to modify the logic of lempar.c. I also discovered that the grammar was
improperly defined and only accepted the first statement. It worked before
because Lemon used to accept multiple times before reaching the EOF token.
I have also verified that it is still generating the proper lumps.
- Removed some unused wadsrc files from the repository.
- Fixed my re2c upgrade.
- Back to lemon.c v1.53 now. The bad change was v1.51: "Changes lemon so
that the generated parser does not accept prior to seeing the EOF token."
This seems like a valid change, which means I need to rethink my strategy
for handling include statements, since this change breaks what I was
doing before.
- Lemon is now producing parsers that don't accept anything, so I'm going
back to v1.43 and trying each intermediate version to see where things
went wrong.
January 24, 2008
- Updated Lemon to its latest versions as of today.
(lemon.c: v1.53, lempar.c: v1.31)
- Added .txt files to the list of types (wad, zip, and pk3) that can be
loaded without listing them after -file.
- Fonts that are created by the ACS setfont command to wrap a texture now
support animated textures.
- FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn
with the hardware 2D path instead of being restricted to the game palette.
January 23, 2008
- Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1
on a Radeon 9000.
- Added back the off-by-one palette handling, but in a much more limited
scope than before. The skipped entry is assumed to always be at 248, and
it is assumed that all Shader Model 1.4 cards suffer from this. That's
because all SM1.4 cards are based on variants of the ATI R200 core, and the
RV250 in a Radeon 9000 craps up like this. I see no reason to assume that
other flavors of the R200 are any different. (Interesting note: With the
Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the
debug Direct3D 9 runtime, but it works perfectly fine with the retail
Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its
math inside pixel shaders. That would explain perfectly why I can't use
constants greater than 1 with PS1.4 and why it can't do an exact mapping to
every entry in the color palette.
- Fixed: The software shaded drawer did not work for 2D, because its selected
"color"map was replaced with the identitymap before being used.
- Fixed: I cannot use Printf to output messages before the framebuffer was
completely setup, meaning that Shader Model 1.4 cards could not change
resolution.
- I have decided to let remap palettes specify variable alpha values for
their colors. D3DFB no longer forces them to 255.
January 22, 2008
- Updated re2c to version 0.12.3.
- Fixed: A_Wander used threshold as a timer, when it should have used
reactiontime.
- Fixed: A_CustomRailgun would not fire at all for actors without a target
when the aim parameter was disabled.
- Made the warp command work in multiplayer, again courtesy of Karate Chris.
- Fixed: Trying to spawn a bot while not in a game made for a crashing time.
(Patch courtesy of Karate Chris.)
January 21, 2008
- Removed some floating point math from hu_scores.cpp that somebody's GCC
gave warnings for (not mine, though).
- Fixed: The SBarInfo drawbar command crashed if the sprite image was
unavailable.
- Fixed: FString::operator=(const char *) did not release its old buffer when
being assigned to the null string.
- The scanner no longer has an upper limit on the length of strings it
accepts, though short strings will be faster than long ones.
- Moved all the text scanning functions into a class. Mainly, this means that
multiple script scanner states can be stored without being forced to do so
recursively. I think I might be taking advantage of that in the near
future. Possibly. Maybe.
- Removed some potential buffer overflows from the decal parser.
January 18, 2008
- Applied Blzut3's SBARINFO update #9:
* Fixed: When using even length values in drawnumber it would cap to a 98
value instead of a 99 as intended.
* The SBarInfo parser can now accept negatives for coordinates. This
doesn't allow much right now, but later I plan to add better fullscreen
hud support in which the negatives will be more useful. This also cleans
up the source a bit since all calls for (x, y) coordinates are with the
function getCoordinates().
- Added support for stencilling actors.
- Added support for non-black colors specified with DTA_ColorOverlay to the
software renderer.
- Fixed: The inverse, gold, red, and green fixed colormaps each allocated
space for 32 different colormaps, even though each only used the first one.
- Added two new blending flags to make reverse subtract blending more useful:
STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that
gets blended with the background, since that seems like a good idea for
reverse subtraction. They also work with the other two blending operations.
January 17, 2008
- Added subtract and reverse subtract blending operations to the renderer.
Since the ERenderStyle enumeration was getting rather unwieldy, I converted
it into a new FRenderStyle structure that lets each parameter of the
blending equation be set separately. This simplified the set up for the
blend quite a bit, and it means a number of new combinations are available
by setting the parameters properly.
January 16, 2008 (Changes by Graf Zahl)
- Fixed: The StatusBar pointer was not NULLed after being deleted.
January 15, 2008
- Plugged more leaks in SBARINFO.
- Spawned actors that have MF2_DORMANT set in their default instance now have
Deactivate() called on them immediately after spawning.
January 15, 2008 (Changes by Graf Zahl)
- Fixed: Skip_Super didn't clear the current state label list.
January 14, 2008
- Fixed: ACS translations were loaded into the wrong slot after restoring a
savegame.
- Fixed exploit: Dropping ammo at baby and nightmare skill levels would give
you back more than you dropped.
January 13, 2008 (Changes by Graf Zahl)
- Moved A_Punch from Inventory to Actor. Apparently there are WADs that use it
for monsters. The function works fine for monsters as it is.
January 12, 2008
- Applied Blzut3's SBARINFO update #8:
* Fixed: Drawbar had a few memory leaks.
- Applied Blzut3's SBARINFO update #7:
* Added drawstring to SBARINFO.
* Added animatedgodmode flag to drawmugshot, also changed the flags to use the
standard named arguments instead of numbers. The old way is considered
deprecated.
* Added kills, monsters, items, totalitems, secrets, and totalsecrets to
drawnumber. Drawbar can also use kills, items, and secrets.
* Added weaponicon to drawimage which will display the inventory.icon of the
currently selected weapon.
* Fixed: I apparently forgot to add the "rampage" face to drawmugshot.
January 12, 2008 (Changes by Graf Zahl)
- Moved renderer dependent part of savegame pic creation into DFrameBuffer
as a virtual function so that it can be overridden.
- Fixed: M_SaveBitmap::prior was too small. It must be 3 bytes per pixel,
not 1.
- Replaced INVGEM** graphics with PNG versions so that they have the
correct colors when used on the AltHUD in Strife.
January 11, 2008
- Added support for 24-bit screenshots, so now accelerated 2D screenshots
can work.
- Tweaked the box splitting algorithm for packed textures to hopefully
produce less wasted space.
- For compatibility with the software renderer, D3DFB::DrawTextureV needs to
truncate the coordinates to integers before sending them to the hardware.
Otherwise, there can be one pixel gaps compared to the software renderer,
because the hardware is rounding to nearest but the software renderer is
simply truncating the fractional part of the coordinate. This is the real
cause of the gap above the status bar at 1152x864 (and another gap to the
left of the status bar at 800x500).
- Fixed: When D3DFB::DrawTextureV had to clip a tile, it adjusted the
texture coordinates erroneously, still using the old calculations from
before texture packing was implemented.
- Moved thingdef_codeptr.cpp into thingdef/ with the other thingdef files.
January 11, 2008 (Changes by Graf Zahl)
- Added Skulltag's pickup sprite and related information to the pistol. This
will also double as the pistol's icon in the AltHUD.
- Added a generic log display that can show Strife's log messages in all games
regardless of the current game, active status bar and HUD mode.
- Added GZDoom's alt HUD.
January 10, 2008
- Fixed: After loading a savegame, G_UnSnapshotLevel() destroyed the
unmorphed versions of players because it thought they were extras.
- Fixed: Weapon positions were only adjusted in 1280x1024 if the status
bar was hidden.
- Fixed: If you died in co-op and somebody else ended the map before you
respawned, you would start the next map with only default inventory, no
matter what dmflags was set to.
- Applied Karate Chris's TEAMINFO logo patch.
January 10, 2008 (Changes by Graf Zahl)
- Moved the code that renders the view from D_Display into a virtual function
of the frame buffer so I can get rid of the last remaining renderer check
outside the display code in GZDoom.
- made V_SetResolution a virtual function in IVideo. It's probably not a perfect
solution but at least it allows overriding it (which I need in GZDoom.)
Note: There's a lot of redundancy between hardware.cpp/h in the SDL and Win32
folders so some cleaning up might be a good idea.
- defined a constant for the crosshair menu entry's index to get a direct
reference out of the function code. In GZDoom the menu contains different
information.
- Fixed: The BrainExplosions should not perform any collision detection. To
achieve this their Damage value must be set to 0 when being spawned.
January 9, 2008
- Added Blzut3's SBARINFO update #6:
* Fixed: A tutti-frutti like effect would occur if transparent images were
used in bars.
* Fixed: drawswitchableimage didn't count having 0 of an item as not having
the said item.
* Added alwaysshowcounter flag to both drawinventorybar and
drawselectedinventory.
* Added armoricon image type to drawimage.
* Added low-on secondary coloring and high-on coloring to drawnumber.
- Added texture packing to D3DFB so that textures that are temporally related
can share the same hardware texture. This greatly reduces the number of
DrawPrimitive calls that need to be made when drawing text (or any 2D
graphics in general), so now hardware text is much faster than software text
all around. (As an example, one scenario went from 315 fps to over 1635 fps
for hardware, compared to 540 fps for software.)
- Fixed: The mouse was being grabbed in windowed mode again.
- Modified M_DrawFrame() and R_DrawTopBorder() so that they call FlatFill() to
draw the edges of the frames. This at least seems a bit faster for hardware
2D.
- Implemented FlatFill() for D3DFB. It seems to be exactly as fast as the
default implementation that just calls DrawTexture() to tile the pieces onto
the screen, so I'm not sure it was worth the bother.
- Merged the separate line and quad vertex buffers in D3DFB back into a single
vertex buffer, made line batching automatic, and added an index buffer for
use when batching quads. The index buffer actually offered more of a
performance boost than simply batching the quads alone did.
January 9, 2008 (Changes by Graf Zahl)
- Added a safety check to savegame versioning so that an invalid revision
(reported as 0) will be handled without breaking savegame compatibility.
- Fixed: 'Painchance' in DECORATE failed when reading custom damage type names.
- Added Karate Chris's patch for menu opening console commands.
January 8, 2008
- Fixed: When starting a teamplay netgame, players who did not specify a team
were not informed about which team they ended up joining.
- Added Skulltag's DF2_SAME_SPAWN_SPOT flags.
- Fixed: DF2_YES_DEGENERATION was pretty much guaranteed to go out of sync
because it used gametic for timing.
- Added DoubleAmmoFactor as a skill property for the DF2_YES_DOUBLEAMMO flag.
- Renumbered the dmflags2 entries to match Skulltag's again.
- Added Karate Chris's infinite inventory patch.
January 7, 2008
- Added support for user-defined crosshairs in the Display Options menu. See
xhairs.txt in zdoom.pk3. It's pretty simple.
- Fixed: P_BounceWall() cannot assume that BlockingLine is the line the actor
should bounce off of. Since the order lines in a blockmap cell are checked
for collision is essentially undefined, there could be another line closer to
the actor that it should bounce off of instead.
- Fixed: Thing_SetTranslation still used a 16-bit word to hold the translation.
- Bumped the maximum resolution up to 2560x1600.
- Fixed: DCanvas::DrawTexture() only expanded virtual screen sizes for widescreen
resolutions but left 5:4 modes alone. This fix neccessitated the addition of
DTA_Bottom320x200 for the status bar to use so that it could be flush with the
bottom of the screen rather than sitting slightly above it.
- Fixed: FConfigFile::ReadConfig()'s definition of whitespace was too broad.
- Fixed: Defining custom translation ranges in descending order and/or with gaps
made for crashes.
January 7, 2008 (Changes by Graf Zahl)
- Added fix for Heretic IDKFA cheat by Karate Chris.
- Added fix for Strife's AlienSpectre obituary by Karate Chris.
January 6, 2008 (Changes by Graf Zahl)
- Re-converted icebreak.flac because the old version apparently didn't work.
- Added SBARINFO update #5 by blzut3:
- Fixed: Playerclass still didn't work due to comparing of improper numbers.
- Fixed: The arrows on drawinventorybar had a hard coded location instead of
relative to the specified coordinates.
- Added noarrows flag to drawinventorybar to remove the blue arrows drawn when
there are more items to the left or right of the viewable bar.
- Added forcescaled flag to the statusbar command. This is ignored on the
inventory and inventoryfullscreen types.
- Added obituary fix for Strife peasants by Karate Chris.
- Added fix for loading during demo playback by Karate Chris.
- Added scoreboard fix by Karate Chris.
- Added teamgame fix for menu by Karate Chris.
- Added GZDoom's Sector_Outside sector type which forces outside fog
regardless of ceiling texture.
January 6, 2008
- Fixed: Since the minimum size for a texture is 2x2, FBarShader can't use
a one-dimensional texture.
- Added back the code to allow some variation to the players' shades when
players are on teams.
- Set TEAM_None back to 255. Since a player's team has already been accessible
through ACS, needlessly redefining this is a bad thing to do, since it can
break existing maps. 255 different teams should still be more than enough.
- Fixed: At certain resolutions, there was a one pixel row between the status
bar and the rest of the screen, thanks to rounding error.
- Added automatic batching of quads to D3DFB. Screens with a lot of text are
ever-so-slightly faster now, though still only about half the speed of
sofware-only text. I suppose the only way to see a marked improvement is
going to be by stuffing multiple glyphs in a single texture.
- Fixed: Crosshairgrow's animation was not framerate-independent.
January 5, 2008 (Changes by Graf Zahl)
- Added: Automap markers are stored in savegames now. Also moved the call
to AM_LevelInit to its proper place in G_DoLoadLevel, right after
the call to P_SetupLevel to make it work as intended.
- Changed savegame versioning to use the SVN revision number instead of
an arbitrarily defined value. This reduces the amount of relevant values
that have to be defined in version.h to 1 (the minimum compatible savegame

View file

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual C++ Express 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = " zdoom", "gzdoom.vcproj", "{8049475B-5C87-46F9-9358-635218A4EF18}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zdoom", "gzdoom.vcproj", "{8049475B-5C87-46F9-9358-635218A4EF18}"
ProjectSection(ProjectDependencies) = postProject
{AC3F5340-40CB-4C3A-8AA7-CB7158DB4466} = {AC3F5340-40CB-4C3A-8AA7-CB7158DB4466}
{DA47396F-60C1-4BDE-A977-7F7DE461CF77} = {DA47396F-60C1-4BDE-A977-7F7DE461CF77}
@ -32,6 +32,10 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xlatcc", "tools\xlatcc\xlatcc.vcproj", "{3FFA68B3-9449-4B03-ADEE-194C3638623B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dehsupp", "tools\dehsupp\dehsupp.vcproj", "{AC64EE8F-F019-4A3E-BCAF-BD1FD072B9C8}"
ProjectSection(ProjectDependencies) = postProject
{0F80ACBF-460E-44F0-B28E-B3272D1774A7} = {0F80ACBF-460E-44F0-B28E-B3272D1774A7}
{667D2EE7-C357-49E2-9BAB-0A4A45F0F76E} = {667D2EE7-C357-49E2-9BAB-0A4A45F0F76E}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg-6b", "jpeg-6b\jpeg-6b.vcproj", "{AC3F5340-40CB-4C3A-8AA7-CB7158DB4466}"
EndProject

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Name=" zdoom"
Version="8.00"
Name="zdoom"
ProjectGUID="{8049475B-5C87-46F9-9358-635218A4EF18}"
RootNamespace=" zdoom"
>
@ -3696,7 +3696,7 @@
<Tool
Name="VCCustomBuildTool"
Description="Creating $(InputName).h from src/$(InputFileName)"
CommandLine="tools\re2c\re2c -s -o &quot;src/$(InputName).h&quot; &quot;src/$(InputFileName)&quot;&#x0D;&#x0A;"
CommandLine="tools\re2c\re2c --no-generation-date -s -o &quot;src/$(InputName).h&quot; &quot;src/$(InputFileName)&quot;&#x0D;&#x0A;"
Outputs="&quot;src/$(InputName).h&quot;"
/>
</FileConfiguration>
@ -3706,7 +3706,7 @@
<Tool
Name="VCCustomBuildTool"
Description="Creating $(InputName).h from src/$(InputFileName)"
CommandLine="tools\re2c\re2c -s -o &quot;src/$(InputName).h&quot; &quot;src/$(InputFileName)&quot;&#x0D;&#x0A;"
CommandLine="tools\re2c\re2c --no-generation-date -s -o &quot;src/$(InputName).h&quot; &quot;src/$(InputFileName)&quot;&#x0D;&#x0A;"
Outputs="&quot;src/$(InputName).h&quot;"
/>
</FileConfiguration>
@ -8990,6 +8990,10 @@
<Filter
Name="Render Headers"
>
<File
RelativePath=".\src\r_blend.h"
>
</File>
<File
RelativePath=".\src\r_bsp.h"
>
@ -9102,6 +9106,10 @@
RelativePath=".\src\textures\texture.cpp"
>
</File>
<File
RelativePath=".\src\textures\texturemanager.cpp"
>
</File>
<File
RelativePath=".\src\textures\tgatexture.cpp"
>
@ -9272,6 +9280,10 @@
RelativePath="src\sound\music_mod.cpp"
>
</File>
<File
RelativePath=".\src\sound\music_modplug.cpp"
>
</File>
<File
RelativePath="src\sound\music_mus_midiout.cpp"
>
@ -9352,6 +9364,158 @@
>
</File>
</Filter>
<Filter
Name="Modplug"
>
<File
RelativePath=".\src\modplug\fastmix.cpp"
>
</File>
<File
RelativePath=".\src\modplug\it_defs.h"
>
</File>
<File
RelativePath=".\src\modplug\load_669.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_amf.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_ams.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_dbm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_dmf.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_dsm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_far.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_it.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_j2b.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_mdl.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_med.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_mod.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_mt2.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_mtm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_okt.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_pat.h"
>
</File>
<File
RelativePath=".\src\modplug\load_psm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_ptm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_s3m.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_stm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_ult.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_umx.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_wav.cpp"
>
</File>
<File
RelativePath=".\src\modplug\load_xm.cpp"
>
</File>
<File
RelativePath=".\src\modplug\mmcmp.cpp"
>
</File>
<File
RelativePath=".\src\modplug\modplug.cpp"
>
</File>
<File
RelativePath=".\src\modplug\modplug.h"
>
</File>
<File
RelativePath=".\src\modplug\snd_dsp.cpp"
>
</File>
<File
RelativePath=".\src\modplug\snd_flt.cpp"
>
</File>
<File
RelativePath=".\src\modplug\snd_fx.cpp"
>
</File>
<File
RelativePath=".\src\modplug\sndfile.cpp"
>
</File>
<File
RelativePath=".\src\modplug\sndfile.h"
>
</File>
<File
RelativePath=".\src\modplug\sndmix.cpp"
>
</File>
<File
RelativePath=".\src\modplug\stdafx.h"
>
</File>
<File
RelativePath=".\src\modplug\tables_mp.cpp"
>
</File>
</Filter>
</Filter>
<Filter
Name="SDL Files"
@ -9428,26 +9592,6 @@
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\sdl\glstubs.cpp"
>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\sdl\hardware.cpp"
>

View file

@ -40,6 +40,7 @@
#include "info.h"
#include "doomdef.h"
#include "r_blend.h"
struct subsector_s;
//
@ -333,25 +334,6 @@ enum
MF_STRIFEx8000000 = 0, // seems related to MF_SHADOW
};
enum ERenderStyle
{
STYLE_None, // Do not draw
STYLE_Normal, // Normal; just copy the image to the screen
STYLE_Fuzzy, // Draw silhouette using "fuzz" effect
STYLE_SoulTrans, // Draw translucent with amount in r_transsouls
STYLE_OptFuzzy, // Draw as fuzzy or translucent, based on user preference
STYLE_Stencil, // Fill image interior with alphacolor
// The following styles can affect visibility in P_CheckSight()
STYLE_Translucent=64, // Draw translucent
STYLE_Add, // Draw additive
STYLE_Shaded, // Treat patch data as alpha values for alphacolor
STYLE_Transparent, // [BB] For smooth particles in GL.
STYLE_TranslucentStencil,
STYLE_Count
};
#define TRANSLUC25 (FRACUNIT/4)
#define TRANSLUC33 (FRACUNIT/3)
#define TRANSLUC50 (FRACUNIT/2)
@ -627,13 +609,13 @@ public:
WORD sprite; // used to find patch_t and flip value
BYTE frame; // sprite frame to draw
fixed_t scaleX, scaleY; // Scaling values; FRACUNIT is normal size
BYTE RenderStyle; // Style to draw this actor with
FRenderStyle RenderStyle; // Style to draw this actor with
DWORD renderflags; // Different rendering flags
int picnum; // Draw this instead of sprite if != 0xffff
SWORD TIDtoHate; // TID of things to hate (0 if none)
DWORD effects; // [RH] see p_effect.h
fixed_t alpha;
DWORD alphacolor; // Color to draw when STYLE_Shaded
DWORD fillcolor; // Color to draw when STYLE_Shaded
// interaction info
fixed_t pitch, roll;
@ -678,6 +660,7 @@ public:
} LastLook;
WORD SpawnPoint[3]; // For nightmare respawn
WORD SpawnAngle;
int skillrespawncount;
AActor *tracer; // Thing being chased/attacked for tracers
AActor *master; // Thing which spawned this one (prevents mutual attacks)
fixed_t floorclip; // value to use for floor clipping

View file

@ -1653,7 +1653,7 @@ void AM_drawThings ()
}
static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust,
INTBOOL flip, fixed_t xscale, fixed_t yscale, int translation, fixed_t alpha, DWORD alphacolor, int renderstyle)
INTBOOL flip, fixed_t xscale, fixed_t yscale, int translation, fixed_t alpha, DWORD fillcolor, FRenderStyle renderstyle)
{
if (tex == NULL || tex->UseType == FTexture::TEX_Null)
{
@ -1673,8 +1673,8 @@ static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust,
DTA_FlipX, flip,
DTA_Translation, TranslationToTable(translation),
DTA_Alpha, alpha,
DTA_FillColor, alphacolor,
DTA_RenderStyle, renderstyle,
DTA_FillColor, fillcolor,
DTA_RenderStyle, DWORD(renderstyle),
TAG_DONE);
}
@ -1684,7 +1684,8 @@ void AM_drawMarks ()
{
if (markpoints[i].x != -1)
{
DrawMarker (TexMan(marknums[i]), markpoints[i].x, markpoints[i].y, -3, 0, FRACUNIT, FRACUNIT, 0, FRACUNIT, 0, STYLE_Normal);
DrawMarker (TexMan(marknums[i]), markpoints[i].x, markpoints[i].y, -3, 0,
FRACUNIT, FRACUNIT, 0, FRACUNIT, 0, LegacyRenderStyles[STYLE_Normal]);
}
}
}
@ -1743,7 +1744,7 @@ void AM_drawAuthorMarkers ()
{
DrawMarker (tex, marked->x >> FRACTOMAPBITS, marked->y >> FRACTOMAPBITS, 0,
flip, mark->scaleX, mark->scaleY, mark->Translation,
mark->alpha, mark->alphacolor, mark->RenderStyle);
mark->alpha, mark->fillcolor, mark->RenderStyle);
}
marked = mark->args[0] != 0 ? it.Next() : NULL;
}

View file

@ -25,6 +25,12 @@ END_POINTERS
CCMD (addbot)
{
if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION)
{
Printf ("Bots cannot be added when not in a game!\n");
return;
}
if (consoleplayer != Net_Arbitrator)
{
Printf ("Only player %d can add bots\n", Net_Arbitrator + 1);
@ -36,14 +42,7 @@ CCMD (addbot)
Printf ("addbot [botname] : add a bot to the game\n");
return;
}
/*
if (argc == 3) //Used force colornum
{
color = atoi (argv[2]);
if (color<0) color=0;
if (color>10) color=10;
}
*/
if (argv.argc() > 1)
bglobal.SpawnBot (argv[1]);
else

View file

@ -504,6 +504,7 @@ void DCajunMaster::ForgetBots ()
bool DCajunMaster::LoadBots ()
{
FScanner sc;
FString tmp;
bool gotteam = false;
@ -526,19 +527,19 @@ bool DCajunMaster::LoadBots ()
return false;
}
else
SC_OpenFile (SHARE_DIR BOTFILENAME);
sc.OpenFile (SHARE_DIR BOTFILENAME);
}
#endif
else
{
SC_OpenFile (tmp);
sc.OpenFile (tmp);
}
while (SC_GetString ())
while (sc.GetString ())
{
if (!SC_Compare ("{"))
if (!sc.Compare ("{"))
{
SC_ScriptError ("Unexpected token '%s'\n", sc_String);
sc.ScriptError ("Unexpected token '%s'\n", sc.String);
}
botinfo_t *newinfo = new botinfo_t;
@ -550,37 +551,37 @@ bool DCajunMaster::LoadBots ()
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
break;
switch (SC_MatchString (BotConfigStrings))
switch (sc.MatchString (BotConfigStrings))
{
case BOTCFG_NAME:
SC_MustGetString ();
sc.MustGetString ();
appendinfo (newinfo->info, "name");
appendinfo (newinfo->info, sc_String);
newinfo->name = copystring (sc_String);
appendinfo (newinfo->info, sc.String);
newinfo->name = copystring (sc.String);
break;
case BOTCFG_AIMING:
SC_MustGetNumber ();
newinfo->skill.aiming = sc_Number;
sc.MustGetNumber ();
newinfo->skill.aiming = sc.Number;
break;
case BOTCFG_PERFECTION:
SC_MustGetNumber ();
newinfo->skill.perfection = sc_Number;
sc.MustGetNumber ();
newinfo->skill.perfection = sc.Number;
break;
case BOTCFG_REACTION:
SC_MustGetNumber ();
newinfo->skill.reaction = sc_Number;
sc.MustGetNumber ();
newinfo->skill.reaction = sc.Number;
break;
case BOTCFG_ISP:
SC_MustGetNumber ();
newinfo->skill.isp = sc_Number;
sc.MustGetNumber ();
newinfo->skill.isp = sc.Number;
break;
case BOTCFG_TEAM:
@ -588,10 +589,10 @@ bool DCajunMaster::LoadBots ()
char teamstr[16];
BYTE teamnum;
SC_MustGetString ();
if (IsNum (sc_String))
sc.MustGetString ();
if (IsNum (sc.String))
{
teamnum = atoi (sc_String);
teamnum = atoi (sc.String);
if (!TEAMINFO_IsValidTeam (teamnum))
{
teamnum = TEAM_None;
@ -602,7 +603,7 @@ bool DCajunMaster::LoadBots ()
teamnum = TEAM_None;
for (int i = 0; i < int(teams.Size()); ++i)
{
if (stricmp (teams[i].name, sc_String) == 0)
if (stricmp (teams[i].name, sc.String) == 0)
{
teamnum = i;
break;
@ -617,13 +618,13 @@ bool DCajunMaster::LoadBots ()
}
default:
if (stricmp (sc_String, "playerclass") == 0)
if (stricmp (sc.String, "playerclass") == 0)
{
gotclass = true;
}
appendinfo (newinfo->info, sc_String);
SC_MustGetString ();
appendinfo (newinfo->info, sc_String);
appendinfo (newinfo->info, sc.String);
sc.MustGetString ();
appendinfo (newinfo->info, sc.String);
break;
}
}
@ -642,10 +643,7 @@ bool DCajunMaster::LoadBots ()
bglobal.botinfo = newinfo;
bglobal.loaded_bots++;
}
SC_Close ();
Printf ("%d bots read from %s\n", bglobal.loaded_bots, BOTFILENAME);
return true;
}

View file

@ -636,16 +636,15 @@ CCMD (r_visibility)
CCMD (warp)
{
if (CheckCheatmode ())
{
return;
}
if (gamestate != GS_LEVEL)
{
Printf ("You can only warp inside a level.\n");
return;
}
if (netgame)
{
Printf ("You cannot warp in a net game!\n");
return;
}
if (argv.argc() != 3)
{
Printf ("Usage: warp <x> <y>\n");

View file

@ -382,10 +382,11 @@ void CreatePath(const char * fn)
}
// [RH] Replaces the escape sequences in a string with actual escaped characters.
// This operation is done in-place.
// This operation is done in-place. The result is the new length of the string.
void strbin (char *str)
int strbin (char *str)
{
char *start = str;
char *p = str, c;
int i;
@ -468,4 +469,5 @@ void strbin (char *str)
}
}
*str = 0;
return str - start;
}

View file

@ -52,7 +52,7 @@ void FormatGUID (char *text, const GUID &guid);
const char *myasctime ();
void strbin (char *str);
int strbin (char *str);
void CreatePath(const char * fn);

View file

@ -811,7 +811,7 @@ static int PatchThing (int thingy)
int style = FindStyle (Line2);
if (style >= 0)
{
info->RenderStyle = style;
info->RenderStyle = ERenderStyle(style);
hadStyle = true;
}
}

View file

@ -2130,7 +2130,8 @@ void D_DoomMain (void)
DArgs *files = Args.GatherFiles ("-file", ".wad", true);
DArgs *files1 = Args.GatherFiles (NULL, ".zip", false);
DArgs *files2 = Args.GatherFiles (NULL, ".pk3", false);
if (files->NumArgs() > 0 || files1->NumArgs() > 0 || files2->NumArgs() > 0)
DArgs *files3 = Args.GatherFiles (NULL, ".txt", false);
if (files->NumArgs() > 0 || files1->NumArgs() > 0 || files2->NumArgs() > 0 || files3->NumArgs() > 0)
{
// Check for -file in shareware
if (gameinfo.flags & GI_SHAREWARE)
@ -2151,10 +2152,15 @@ void D_DoomMain (void)
{
D_AddWildFile (files2->GetArg (i));
}
for (int i = 0; i < files3->NumArgs(); i++)
{
D_AddWildFile (files3->GetArg (i));
}
}
delete files;
delete files1;
delete files2;
delete files3;
Printf ("W_Init: Init WADfiles.\n");
Wads.InitMultipleFiles (&wadfiles);

View file

@ -118,6 +118,12 @@ public:
FNameNoInit MorphWeapon;
fixed_t AttackZOffset; // attack height, relative to player center
// [CW] Fades for when you are being damaged.
bool HasDamageFade;
float RedDamageFade;
float GreenDamageFade;
float BlueDamageFade;
bool UpdateWaterLevel (fixed_t oldz, bool splash);
bool ResetAirSupply ();

View file

@ -48,7 +48,7 @@
FDecalLib DecalLibrary;
static fixed_t ReadScale ();
static fixed_t ReadScale (FScanner &sc);
static TArray<BYTE> DecalTranslations;
// A decal group holds multiple decals and returns one randomly
@ -358,9 +358,8 @@ void FDecalLib::ReadAllDecals ()
while ((lump = Wads.FindLump ("DECALDEF", &lastlump)) != -1)
{
SC_OpenLumpNum (lump, "DECALDEF");
ReadDecals ();
SC_Close ();
FScanner sc(lump, "DECALDEF");
ReadDecals (sc);
}
// Supporting code to allow specifying decals directly in the DECORATE lump
for (i = 0; i < PClass::m_RuntimeActors.Size(); i++)
@ -375,79 +374,79 @@ void FDecalLib::ReadAllDecals ()
}
}
void FDecalLib::ReadDecals ()
void FDecalLib::ReadDecals(FScanner &sc)
{
while (SC_GetString ())
while (sc.GetString())
{
if (SC_Compare ("decal"))
if (sc.Compare("decal"))
{
ParseDecal ();
ParseDecal(sc);
}
else if (SC_Compare ("decalgroup"))
else if (sc.Compare("decalgroup"))
{
ParseDecalGroup ();
ParseDecalGroup(sc);
}
else if (SC_Compare ("generator"))
else if (sc.Compare("generator"))
{
ParseGenerator ();
ParseGenerator(sc);
}
else if (SC_Compare ("fader"))
else if (sc.Compare("fader"))
{
ParseFader ();
ParseFader(sc);
}
else if (SC_Compare ("stretcher"))
else if (sc.Compare("stretcher"))
{
ParseStretcher ();
ParseStretcher(sc);
}
else if (SC_Compare ("slider"))
else if (sc.Compare("slider"))
{
ParseSlider ();
ParseSlider(sc);
}
else if (SC_Compare ("combiner"))
else if (sc.Compare("combiner"))
{
ParseCombiner ();
ParseCombiner(sc);
}
else if (SC_Compare ("colorchanger"))
else if (sc.Compare("colorchanger"))
{
ParseColorchanger ();
ParseColorchanger(sc);
}
else
{
SC_ScriptError (NULL);
sc.MustGetStringName(NULL);
}
}
}
BYTE FDecalLib::GetDecalID ()
BYTE FDecalLib::GetDecalID (FScanner &sc)
{
SC_MustGetString ();
if (!IsNum (sc_String))
sc.MustGetString ();
if (!IsNum (sc.String))
{
SC_UnGet ();
sc.UnGet ();
return 0;
}
else
{
unsigned long num = strtoul (sc_String, NULL, 10);
unsigned long num = strtoul (sc.String, NULL, 10);
if (num < 1 || num > 255)
{
SC_ScriptError ("Decal ID must be between 1 and 255");
sc.MustGetStringName ("Decal ID must be between 1 and 255");
}
return (BYTE)num;
}
}
void FDecalLib::ParseDecal ()
void FDecalLib::ParseDecal (FScanner &sc)
{
char decalName[64];
FString decalName;
BYTE decalNum;
FDecalTemplate newdecal;
int code, picnum;
SC_MustGetString ();
strcpy (decalName, sc_String);
decalNum = GetDecalID ();
SC_MustGetStringName ("{");
sc.MustGetString ();
decalName = sc.String;
decalNum = GetDecalID (sc);
sc.MustGetStringName ("{");
memset (&newdecal, 0, sizeof(newdecal));
newdecal.PicNum = 0xffff;
@ -458,26 +457,26 @@ void FDecalLib::ParseDecal ()
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
{
AddDecal (decalName, decalNum, newdecal);
break;
}
switch ((code = SC_MustMatchString (DecalKeywords)))
switch ((code = sc.MustMatchString (DecalKeywords)))
{
case DECAL_XSCALE:
newdecal.ScaleX = ReadScale ();
newdecal.ScaleX = ReadScale (sc);
break;
case DECAL_YSCALE:
newdecal.ScaleY = ReadScale ();
newdecal.ScaleY = ReadScale (sc);
break;
case DECAL_PIC:
SC_MustGetString ();
picnum = TexMan.CheckForTexture (sc_String, FTexture::TEX_Any);
if (picnum < 0 && (picnum = Wads.CheckNumForName (sc_String, ns_graphics)) >= 0)
sc.MustGetString ();
picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Any);
if (picnum < 0 && (picnum = Wads.CheckNumForName (sc.String, ns_graphics)) >= 0)
{
picnum = TexMan.CreateTexture (picnum, FTexture::TEX_Decal);
}
@ -489,14 +488,14 @@ void FDecalLib::ParseDecal ()
break;
case DECAL_ADD:
SC_MustGetFloat ();
newdecal.Alpha = (WORD)(32768.f * sc_Float);
sc.MustGetFloat ();
newdecal.Alpha = (WORD)(32768.f * sc.Float);
newdecal.RenderStyle = STYLE_Add;
break;
case DECAL_TRANSLUCENT:
SC_MustGetFloat ();
newdecal.Alpha = (WORD)(32768.f * sc_Float);
sc.MustGetFloat ();
newdecal.Alpha = (WORD)(32768.f * sc.Float);
newdecal.RenderStyle = STYLE_Translucent;
break;
@ -525,9 +524,9 @@ void FDecalLib::ParseDecal ()
break;
case DECAL_SHADE:
SC_MustGetString ();
sc.MustGetString ();
newdecal.RenderStyle = STYLE_Shaded;
newdecal.ShadeColor = V_GetColor (NULL, sc_String);
newdecal.ShadeColor = V_GetColor (NULL, sc.String);
newdecal.ShadeColor |=
ColorMatcher.Pick (RPART(newdecal.ShadeColor),
GPART(newdecal.ShadeColor), BPART(newdecal.ShadeColor)) << 24;
@ -536,42 +535,42 @@ void FDecalLib::ParseDecal ()
case DECAL_COLORS:
DWORD startcolor, endcolor;
SC_MustGetString (); startcolor = V_GetColor (NULL, sc_String);
SC_MustGetString (); endcolor = V_GetColor (NULL, sc_String);
sc.MustGetString (); startcolor = V_GetColor (NULL, sc.String);
sc.MustGetString (); endcolor = V_GetColor (NULL, sc.String);
newdecal.Translation = GenerateTranslation (startcolor, endcolor)->Index;
break;
case DECAL_ANIMATOR:
SC_MustGetString ();
newdecal.Animator = FindAnimator (sc_String);
sc.MustGetString ();
newdecal.Animator = FindAnimator (sc.String);
break;
case DECAL_LOWERDECAL:
SC_MustGetString ();
newdecal.LowerDecal = GetDecalByName (sc_String);
sc.MustGetString ();
newdecal.LowerDecal = GetDecalByName (sc.String);
break;
}
}
}
void FDecalLib::ParseDecalGroup ()
void FDecalLib::ParseDecalGroup (FScanner &sc)
{
char groupName[64];
FString groupName;
BYTE decalNum;
FDecalBase *targetDecal;
FDecalGroup *group;
SC_MustGetString ();
strcpy (groupName, sc_String);
decalNum = GetDecalID ();
SC_MustGetStringName ("{");
sc.MustGetString ();
groupName = sc.String;
decalNum = GetDecalID (sc);
sc.MustGetStringName ("{");
group = new FDecalGroup;
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
{
group->Name = groupName;
group->SpawnID = decalNum;
@ -579,44 +578,44 @@ void FDecalLib::ParseDecalGroup ()
break;
}
targetDecal = ScanTreeForName (sc_String, Root);
targetDecal = ScanTreeForName (sc.String, Root);
if (targetDecal == NULL)
{
SC_ScriptError ("%s has not been defined", sc_String);
sc.ScriptError ("%s has not been defined", sc.String);
}
SC_MustGetNumber ();
sc.MustGetNumber ();
group->AddDecal (targetDecal, sc_Number);
group->AddDecal (targetDecal, sc.Number);
}
}
void FDecalLib::ParseGenerator ()
void FDecalLib::ParseGenerator (FScanner &sc)
{
const PClass *type;
FDecalBase *decal;
AActor *actor;
// Get name of generator (actor)
SC_MustGetString ();
type = PClass::FindClass (sc_String);
sc.MustGetString ();
type = PClass::FindClass (sc.String);
if (type == NULL || type->ActorInfo == NULL)
{
SC_ScriptError ("%s is not an actor.", sc_String);
sc.ScriptError ("%s is not an actor.", sc.String);
}
actor = (AActor *)type->Defaults;
// Get name of generated decal
SC_MustGetString ();
if (stricmp (sc_String, "None") == 0)
sc.MustGetString ();
if (stricmp (sc.String, "None") == 0)
{
decal = NULL;
}
else
{
decal = ScanTreeForName (sc_String, Root);
decal = ScanTreeForName (sc.String, Root);
if (decal == NULL)
{
SC_ScriptError ("%s has not been defined.", sc_String);
sc.ScriptError ("%s has not been defined.", sc.String);
}
}
@ -624,19 +623,19 @@ void FDecalLib::ParseGenerator ()
decal->Users.Push (type);
}
void FDecalLib::ParseFader ()
void FDecalLib::ParseFader (FScanner &sc)
{
char faderName[64];
FString faderName;
int startTime = 0, decayTime = 0;
SC_MustGetString ();
strcpy (faderName, sc_String);
SC_MustGetStringName ("{");
sc.MustGetString ();
faderName = sc.String;
sc.MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
{
FDecalFaderAnim *fader = new FDecalFaderAnim (faderName);
fader->DecayStart = startTime;
@ -644,37 +643,37 @@ void FDecalLib::ParseFader ()
Animators.Push (fader);
break;
}
else if (SC_Compare ("DecayStart"))
else if (sc.Compare ("DecayStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
startTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("DecayTime"))
else if (sc.Compare ("DecayTime"))
{
SC_MustGetFloat ();
decayTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
decayTime = (int)(sc.Float * TICRATE);
}
else
{
SC_ScriptError ("Unknown fader parameter '%s'", sc_String);
sc.ScriptError ("Unknown fader parameter '%s'", sc.String);
}
}
}
void FDecalLib::ParseStretcher ()
void FDecalLib::ParseStretcher (FScanner &sc)
{
char stretcherName[64];
FString stretcherName;
fixed_t goalX = -1, goalY = -1;
int startTime = 0, takeTime = 0;
SC_MustGetString ();
strcpy (stretcherName, sc_String);
SC_MustGetStringName ("{");
sc.MustGetString ();
stretcherName = sc.String;
sc.MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
{
if (goalX >= 0 || goalY >= 0)
{
@ -687,45 +686,45 @@ void FDecalLib::ParseStretcher ()
}
break;
}
else if (SC_Compare ("StretchStart"))
else if (sc.Compare ("StretchStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
startTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("StretchTime"))
else if (sc.Compare ("StretchTime"))
{
SC_MustGetFloat ();
takeTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
takeTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("GoalX"))
else if (sc.Compare ("GoalX"))
{
goalX = ReadScale ();
goalX = ReadScale (sc);
}
else if (SC_Compare ("GoalY"))
else if (sc.Compare ("GoalY"))
{
goalY = ReadScale ();
goalY = ReadScale (sc);
}
else
{
SC_ScriptError ("Unknown stretcher parameter '%s'", sc_String);
sc.ScriptError ("Unknown stretcher parameter '%s'", sc.String);
}
}
}
void FDecalLib::ParseSlider ()
void FDecalLib::ParseSlider (FScanner &sc)
{
char sliderName[64];
FString sliderName;
fixed_t distX = 0, distY = 0;
int startTime = 0, takeTime = 0;
SC_MustGetString ();
strcpy (sliderName, sc_String);
SC_MustGetStringName ("{");
sc.MustGetString ();
sliderName = sc.String;
sc.MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
{
if ((/*distX |*/ distY) != 0)
{
@ -738,48 +737,48 @@ void FDecalLib::ParseSlider ()
}
break;
}
else if (SC_Compare ("SlideStart"))
else if (sc.Compare ("SlideStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
startTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("SlideTime"))
else if (sc.Compare ("SlideTime"))
{
SC_MustGetFloat ();
takeTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
takeTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("DistX"))
else if (sc.Compare ("DistX"))
{
SC_MustGetFloat ();
distX = (fixed_t)(sc_Float * FRACUNIT);
Printf ("DistX in slider decal %s is unsupported\n", sliderName);
sc.MustGetFloat ();
distX = (fixed_t)(sc.Float * FRACUNIT);
Printf ("DistX in slider decal %s is unsupported\n", sliderName.GetChars());
}
else if (SC_Compare ("DistY"))
else if (sc.Compare ("DistY"))
{
SC_MustGetFloat ();
distY = (fixed_t)(sc_Float * FRACUNIT);
sc.MustGetFloat ();
distY = (fixed_t)(sc.Float * FRACUNIT);
}
else
{
SC_ScriptError ("Unknown slider parameter '%s'", sc_String);
sc.ScriptError ("Unknown slider parameter '%s'", sc.String);
}
}
}
void FDecalLib::ParseColorchanger ()
void FDecalLib::ParseColorchanger (FScanner &sc)
{
char faderName[64];
FString faderName;
int startTime = 0, decayTime = 0;
PalEntry goal = 0;
SC_MustGetString ();
strcpy (faderName, sc_String);
SC_MustGetStringName ("{");
sc.MustGetString ();
faderName = sc.String;
sc.MustGetStringName ("{");
for (;;)
{
SC_MustGetString ();
if (SC_Compare ("}"))
sc.MustGetString ();
if (sc.Compare ("}"))
{
FDecalColorerAnim *fader = new FDecalColorerAnim (faderName);
fader->DecayStart = startTime;
@ -788,46 +787,46 @@ void FDecalLib::ParseColorchanger ()
Animators.Push (fader);
break;
}
else if (SC_Compare ("FadeStart"))
else if (sc.Compare ("FadeStart"))
{
SC_MustGetFloat ();
startTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
startTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("FadeTime"))
else if (sc.Compare ("FadeTime"))
{
SC_MustGetFloat ();
decayTime = (int)(sc_Float * TICRATE);
sc.MustGetFloat ();
decayTime = (int)(sc.Float * TICRATE);
}
else if (SC_Compare ("Color"))
else if (sc.Compare ("Color"))
{
SC_MustGetString ();
goal = V_GetColor (NULL, sc_String);
sc.MustGetString ();
goal = V_GetColor (NULL, sc.String);
}
else
{
SC_ScriptError ("Unknown color changer parameter '%s'", sc_String);
sc.ScriptError ("Unknown color changer parameter '%s'", sc.String);
}
}
}
void FDecalLib::ParseCombiner ()
void FDecalLib::ParseCombiner (FScanner &sc)
{
char combinerName[64];
FString combinerName;
size_t first = FDecalCombinerAnim::AnimatorList.Size ();
SC_MustGetString ();
strcpy (combinerName, sc_String);
SC_MustGetStringName ("{");
SC_MustGetString ();
while (!SC_Compare ("}"))
sc.MustGetString ();
combinerName = sc.String;
sc.MustGetStringName ("{");
sc.MustGetString ();
while (!sc.Compare ("}"))
{
FDecalAnimator *anim = FindAnimator (sc_String);
FDecalAnimator *anim = FindAnimator (sc.String);
if (anim == NULL)
{
SC_ScriptError ("Undefined animator %s", sc_String);
sc.ScriptError ("Undefined animator %s", sc.String);
}
FDecalCombinerAnim::AnimatorList.Push (anim);
SC_MustGetString ();
sc.MustGetString ();
}
size_t last = FDecalCombinerAnim::AnimatorList.Size ();
@ -1022,7 +1021,7 @@ FDecalBase::~FDecalBase ()
void FDecalTemplate::ApplyToDecal (DBaseDecal *decal, side_t *wall) const
{
if (RenderStyle == STYLE_Shaded)
if (RenderStyle.Flags & STYLEF_ColorIsFixed)
{
decal->SetShade (ShadeColor);
}
@ -1165,11 +1164,6 @@ void DDecalFader::Tick ()
int distanceToEnd = TimeToEndDecay - level.maptime;
int fadeDistance = TimeToEndDecay - TimeToStartDecay;
TheDecal->Alpha = Scale (StartTrans, distanceToEnd, fadeDistance);
if (TheDecal->RenderStyle < STYLE_Translucent)
{
TheDecal->RenderStyle = STYLE_Translucent;
}
}
}
@ -1364,7 +1358,7 @@ void DDecalColorer::Serialize (FArchive &arc)
void DDecalColorer::Tick ()
{
if (TheDecal == NULL || TheDecal->RenderStyle != STYLE_Shaded)
if (TheDecal == NULL || !(TheDecal->RenderStyle.Flags & STYLEF_ColorIsFixed))
{
Destroy ();
}
@ -1413,8 +1407,8 @@ DThinker *FDecalColorerAnim::CreateThinker (DBaseDecal *actor, side_t *wall) con
return Colorer;
}
static fixed_t ReadScale ()
static fixed_t ReadScale (FScanner &sc)
{
SC_MustGetFloat ();
return fixed_t(clamp (sc_Float * FRACUNIT, 256.0, 256.0*FRACUNIT));
sc.MustGetFloat ();
return fixed_t(clamp (sc.Float * FRACUNIT, 256.0, 256.0*FRACUNIT));
}

View file

@ -39,8 +39,9 @@
#include "doomtype.h"
#include "tarray.h"
#include "name.h"
#include "actor.h"
class AActor;
class FScanner;
class FDecalTemplate;
struct FDecalAnimator;
struct PClass;
@ -77,7 +78,7 @@ public:
fixed_t ScaleX, ScaleY;
DWORD ShadeColor;
DWORD Translation;
BYTE RenderStyle;
FRenderStyle RenderStyle;
WORD PicNum;
WORD RenderFlags;
WORD Alpha; // same as (actor->alpha >> 1)
@ -94,7 +95,7 @@ public:
~FDecalLib ();
void Clear ();
void ReadDecals (); // SC_Open() should have just been called
void ReadDecals (FScanner &sc);
void ReadAllDecals ();
const FDecalTemplate *GetDecalByNum (BYTE num) const;
@ -112,15 +113,15 @@ private:
void AddDecal (FDecalBase *decal);
FDecalAnimator *FindAnimator (const char *name);
BYTE GetDecalID ();
void ParseDecal ();
void ParseDecalGroup ();
void ParseGenerator ();
void ParseFader ();
void ParseStretcher ();
void ParseSlider ();
void ParseCombiner ();
void ParseColorchanger ();
BYTE GetDecalID (FScanner &sc);
void ParseDecal (FScanner &sc);
void ParseDecalGroup (FScanner &sc);
void ParseGenerator (FScanner &sc);
void ParseFader (FScanner &sc);
void ParseStretcher (FScanner &sc);
void ParseSlider (FScanner &sc);
void ParseCombiner (FScanner &sc);
void ParseColorchanger (FScanner &sc);
FDecalBase *Root;
FTranslation *Translations;

View file

@ -180,7 +180,7 @@ struct PalEntry
PalEntry (DWORD argb) { *(DWORD *)this = argb; }
operator DWORD () const { return *(DWORD *)this; }
PalEntry &operator= (DWORD other) { *(DWORD *)this = other; return *this; }
PalEntry InverseColor() const { PalEntry nc; nc.a = a; nc.r = 255 - r; nc.g = 255 - g; nc.b = 255 - b; return nc; }
#ifdef WORDS_BIGENDIAN
PalEntry (BYTE ir, BYTE ig, BYTE ib) : a(0), r(ir), g(ig), b(ib) {}
PalEntry (BYTE ia, BYTE ir, BYTE ig, BYTE ib) : a(ia), r(ir), g(ig), b(ib) {}

View file

@ -1234,7 +1234,6 @@ void F_Drawer (void)
break;
case END_Pic:
picname = EndSequences[FinaleSequence].PicName;
TexMan.AddPatch (picname); // make sure it exists!
break;
case END_Bunny:
case END_BuyStrife:

View file

@ -75,29 +75,29 @@ static void FS_Gimme(const char * what)
}
void FS_MapCmd()
void FS_MapCmd(FScanner &sc)
{
char nextmap[9];
int NextSkill = -1;
bool resetplayers=true;
bool nomonsters = !!(dmflags & DF_NO_MONSTERS);
SC_MustGetString();
strncpy (nextmap, sc_String, 8);
sc.MustGetString();
strncpy (nextmap, sc.String, 8);
nextmap[8]=0;
while (SC_GetString())
while (sc.GetString())
{
if (SC_Compare("-skill"))
if (sc.Compare("-skill"))
{
SC_MustGetNumber();
NextSkill = clamp<int>(sc_Number-1, 0, AllSkills.Size()-1);
sc.MustGetNumber();
NextSkill = clamp<int>(sc.Number-1, 0, AllSkills.Size()-1);
}
else if (SC_Compare("-monsters"))
else if (sc.Compare("-monsters"))
{
SC_MustGetNumber();
nomonsters = !!sc_Number;
sc.MustGetNumber();
nomonsters = !!sc.Number;
}
else if (SC_Compare("-noresetplayers"))
else if (sc.Compare("-noresetplayers"))
{
resetplayers=false;
}
@ -108,73 +108,74 @@ void FS_MapCmd()
void FS_EmulateCmd(char * string)
{
SC_OpenMem("RUNCMD", string, (int)strlen(string));
while (SC_GetString())
FScanner sc;
sc.OpenMem("RUNCMD", string, (int)strlen(string));
while (sc.GetString())
{
if (SC_Compare("GIMME"))
if (sc.Compare("GIMME"))
{
while (SC_GetString())
while (sc.GetString())
{
if (!SC_Compare(";")) FS_Gimme(sc_String);
if (!sc.Compare(";")) FS_Gimme(sc.String);
else break;
}
}
else if (SC_Compare("ALLOWJUMP"))
else if (sc.Compare("ALLOWJUMP"))
{
SC_MustGetNumber();
if (sc_Number) dmflags = dmflags & ~DF_NO_JUMP;
sc.MustGetNumber();
if (sc.Number) dmflags = dmflags & ~DF_NO_JUMP;
else dmflags=dmflags | DF_NO_JUMP;
while (SC_GetString())
while (sc.GetString())
{
if (SC_Compare(";")) break;
if (sc.Compare(";")) break;
}
}
else if (SC_Compare("gravity"))
else if (sc.Compare("gravity"))
{
SC_MustGetFloat();
level.gravity=(float)(sc_Float*800);
while (SC_GetString())
sc.MustGetFloat();
level.gravity=(float)(sc.Float*800);
while (sc.GetString())
{
if (SC_Compare(";")) break;
if (sc.Compare(";")) break;
}
}
else if (SC_Compare("viewheight"))
else if (sc.Compare("viewheight"))
{
SC_MustGetFloat();
fixed_t playerviewheight = (fixed_t)(sc_Float*FRACUNIT);
sc.MustGetFloat();
fixed_t playerviewheight = (fixed_t)(sc.Float*FRACUNIT);
for(int i=0;i<MAXPLAYERS;i++)
{
// No, this is not correct. But this is the way Legacy WADs expect it to be handled!
if (players[i].mo != NULL) players[i].mo->ViewHeight = playerviewheight;
players[i].Uncrouch();
}
while (SC_GetString())
while (sc.GetString())
{
if (SC_Compare(";")) break;
if (sc.Compare(";")) break;
}
}
else if (SC_Compare("map"))
else if (sc.Compare("map"))
{
FS_MapCmd();
FS_MapCmd(sc);
}
else if (SC_Compare("gr_fogdensity"))
else if (sc.Compare("gr_fogdensity"))
{
SC_MustGetNumber();
sc.MustGetNumber();
// Using this disables most MAPINFO fog options!
gl_SetFogParams(sc_Number*70/400, 0xff000000, 0, 0);
gl_SetFogParams(sc.Number*70/400, 0xff000000, 0, 0);
}
else if (SC_Compare("gr_fogcolor"))
else if (sc.Compare("gr_fogcolor"))
{
SC_MustGetString();
level.fadeto = strtol(sc_String, NULL, 16);
sc.MustGetString();
level.fadeto = strtol(sc.String, NULL, 16);
}
else
{
// Skip unhandled commands
while (SC_GetString())
while (sc.GetString())
{
if (SC_Compare(";")) break;
if (sc.Compare(";")) break;
}
}
}

View file

@ -585,7 +585,10 @@ void SF_Include(void)
if (T_CheckArgs(1))
{
if(t_argv[0].type == svt_string)
{
strncpy(tempstr, t_argv[0].value.s, 8);
tempstr[8]=0;
}
else
sprintf(tempstr, "%i", (int)t_argv[0].value.i);

View file

@ -108,10 +108,11 @@ static void ParseInfoCmd(char *line)
{
// Read the usable parts of the level info header
// and ignore the rest.
SC_OpenMem("LEVELINFO", line, (int)strlen(line));
SC_SetCMode(true);
SC_MustGetString();
if (SC_Compare("levelname"))
FScanner sc;
sc.OpenMem("LEVELINFO", line, (int)strlen(line));
sc.SetCMode(true);
sc.MustGetString();
if (sc.Compare("levelname"))
{
char * beg = strchr(line, '=')+1;
while (*beg<=' ') beg++;
@ -120,68 +121,68 @@ static void ParseInfoCmd(char *line)
strncpy(level.level_name, beg, 63);
level.level_name[63]=0;
}
else if (SC_Compare("partime"))
else if (sc.Compare("partime"))
{
SC_MustGetStringName("=");
SC_MustGetNumber();
level.partime=sc_Number;
sc.MustGetStringName("=");
sc.MustGetNumber();
level.partime=sc.Number;
}
else if (SC_Compare("music"))
else if (sc.Compare("music"))
{
bool FS_ChangeMusic(const char * string);
SC_MustGetStringName("=");
SC_MustGetString();
if (!FS_ChangeMusic(sc_String))
sc.MustGetStringName("=");
sc.MustGetString();
if (!FS_ChangeMusic(sc.String))
{
S_ChangeMusic(level.music, level.musicorder);
}
}
else if (SC_Compare("skyname"))
else if (sc.Compare("skyname"))
{
SC_MustGetStringName("=");
SC_MustGetString();
sc.MustGetStringName("=");
sc.MustGetString();
strncpy(level.skypic1, sc_String, 8);
strncpy(level.skypic2, sc_String, 8);
strncpy(level.skypic1, sc.String, 8);
strncpy(level.skypic2, sc.String, 8);
level.skypic1[8]=level.skypic2[8]=0;
sky2texture = sky1texture = TexMan.GetTexture (sc_String, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
sky2texture = sky1texture = TexMan.GetTexture (sc.String, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
R_InitSkyMap ();
}
else if (SC_Compare("interpic"))
else if (sc.Compare("interpic"))
{
SC_MustGetStringName("=");
SC_MustGetString();
strncpy(level.info->exitpic, sc_String, 8);
sc.MustGetStringName("=");
sc.MustGetString();
strncpy(level.info->exitpic, sc.String, 8);
level.info->exitpic[8]=0;
}
else if (SC_Compare("gravity"))
else if (sc.Compare("gravity"))
{
SC_MustGetStringName("=");
SC_MustGetNumber();
level.gravity=sc_Number*8.f;
sc.MustGetStringName("=");
sc.MustGetNumber();
level.gravity=sc.Number*8.f;
}
else if (SC_Compare("nextlevel"))
else if (sc.Compare("nextlevel"))
{
SC_MustGetStringName("=");
SC_MustGetString();
strncpy(level.nextmap, sc_String, 8);
sc.MustGetStringName("=");
sc.MustGetString();
strncpy(level.nextmap, sc.String, 8);
level.nextmap[8]=0;
}
else if (SC_Compare("nextsecret"))
else if (sc.Compare("nextsecret"))
{
SC_MustGetStringName("=");
SC_MustGetString();
strncpy(level.secretmap, sc_String, 8);
sc.MustGetStringName("=");
sc.MustGetString();
strncpy(level.secretmap, sc.String, 8);
level.secretmap[8]=0;
}
else if (SC_Compare("drown"))
else if (sc.Compare("drown"))
{
SC_MustGetStringName("=");
SC_MustGetNumber();
drownflag=!!sc_Number;
sc.MustGetStringName("=");
sc.MustGetNumber();
drownflag=!!sc.Number;
}
else if (SC_Compare("consolecmd"))
else if (sc.Compare("consolecmd"))
{
char * beg = strchr(line, '=')+1;
while (*beg<' ') beg++;
@ -190,7 +191,7 @@ static void ParseInfoCmd(char *line)
FS_EmulateCmd(beg);
}
// Ignore anything unknows
SC_Close();
sc.Close();
}
}

View file

@ -1043,7 +1043,7 @@ private:
FDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture ()
{
BaseTexture = TexMan[TexMan.AddPatch("STBAR")];
BaseTexture = TexMan["STBAR"];
if (BaseTexture==NULL)
{
I_Error("Fatal error: STBAR not found");

View file

@ -99,13 +99,13 @@ EXTERN_CVAR (Int, disableautosave)
static int FindEndSequence (int type, const char *picname);
static void SetEndSequence (char *nextmap, int type);
static void InitPlayerClasses ();
static void ParseEpisodeInfo ();
static void ParseEpisodeInfo (FScanner &sc);
static void G_DoParseMapInfo (int lump);
static void SetLevelNum (level_info_t *info, int num);
static void ClearEpisodes ();
static void ClearLevelInfoStrings (level_info_t *linfo);
static void ClearClusterInfoStrings (cluster_info_t *cinfo);
static void ParseSkill ();
static void ParseSkill (FScanner &sc);
static void G_VerifySkill();
static FRandom pr_classchoice ("RandomPlayerClassChoice");
@ -306,6 +306,8 @@ static const char *MapInfoMapLevel[] =
"fogdensity",
"outsidefogdensity",
"skyfog",
"teamplayon",
"teamplayoff",
NULL
};
@ -451,6 +453,8 @@ MapHandlers[] =
{ MITYPE_INT, lioffset(fogdensity), 0 },
{ MITYPE_INT, lioffset(outsidefogdensity), 0 },
{ MITYPE_INT, lioffset(skyfog), 0 },
{ MITYPE_SCFLAGS, LEVEL_FORCETEAMPLAYON, ~LEVEL_FORCETEAMPLAYOFF },
{ MITYPE_SCFLAGS, LEVEL_FORCETEAMPLAYOFF, ~LEVEL_FORCETEAMPLAYON },
};
static const char *MapInfoClusterLevel[] =
@ -484,7 +488,8 @@ MapInfoHandler ClusterHandlers[] =
{ MITYPE_STRING, cioffset(clustername), 0 },
};
static void ParseMapInfoLower (MapInfoHandler *handlers,
static void ParseMapInfoLower (FScanner &sc,
MapInfoHandler *handlers,
const char *strings[],
level_info_t *levelinfo,
cluster_info_t *clusterinfo,
@ -609,28 +614,29 @@ static void G_DoParseMapInfo (int lump)
int clusterindex;
QWORD levelflags;
FScanner sc(lump, Wads.GetLumpFullName(lump));
SetLevelDefaults (&defaultinfo);
SC_OpenLumpNum (lump, Wads.GetLumpFullName(lump));
HexenHack = false;
while (SC_GetString ())
while (sc.GetString ())
{
switch (SC_MustMatchString (MapInfoTopLevel))
switch (sc.MustMatchString (MapInfoTopLevel))
{
case MITL_DEFAULTMAP:
if (defaultinfo.music != NULL) delete [] defaultinfo.music;
if (defaultinfo.intermusic != NULL) delete [] defaultinfo.intermusic;
SetLevelDefaults (&defaultinfo);
ParseMapInfoLower (MapHandlers, MapInfoMapLevel, &defaultinfo, NULL, defaultinfo.flags);
ParseMapInfoLower (sc, MapHandlers, MapInfoMapLevel, &defaultinfo, NULL, defaultinfo.flags);
break;
case MITL_MAP: // map <MAPNAME> <Nice Name>
levelflags = defaultinfo.flags;
SC_MustGetString ();
if (IsNum (sc_String))
sc.MustGetString ();
if (IsNum (sc.String))
{ // MAPNAME is a number; assume a Hexen wad
int map = atoi (sc_String);
sprintf (sc_String, "MAP%02d", map);
int map = atoi (sc.String);
sprintf (sc.String, "MAP%02d", map);
HexenHack = true;
// Hexen levels are automatically nointermission,
// no auto sound sequences, falling damage,
@ -643,7 +649,7 @@ static void G_DoParseMapInfo (int lump)
| LEVEL_MISSILESACTIVATEIMPACT
| LEVEL_INFINITE_FLIGHT;
}
levelindex = FindWadLevelInfo (sc_String);
levelindex = FindWadLevelInfo (sc.String);
if (levelindex == -1)
{
levelindex = wadlevelinfos.Reserve(1);
@ -666,17 +672,17 @@ static void G_DoParseMapInfo (int lump)
{
levelinfo->WallHorizLight = levelinfo->WallVertLight = 0;
}
uppercopy (levelinfo->mapname, sc_String);
SC_MustGetString ();
if (SC_Compare ("lookup"))
uppercopy (levelinfo->mapname, sc.String);
sc.MustGetString ();
if (sc.Compare ("lookup"))
{
SC_MustGetString ();
ReplaceString (&levelinfo->level_name, sc_String);
sc.MustGetString ();
ReplaceString (&levelinfo->level_name, sc.String);
levelflags |= LEVEL_LOOKUPLEVELNAME;
}
else
{
ReplaceString (&levelinfo->level_name, sc_String);
ReplaceString (&levelinfo->level_name, sc.String);
}
// Set up levelnum now so that you can use Teleport_NewMap specials
// to teleport to maps with standard names without needing a levelnum.
@ -696,7 +702,7 @@ static void G_DoParseMapInfo (int lump)
int mapnum = levelinfo->mapname[3] - '0';
levelinfo->levelnum = epinum*10 + mapnum;
}
ParseMapInfoLower (MapHandlers, MapInfoMapLevel, levelinfo, NULL, levelflags);
ParseMapInfoLower (sc, MapHandlers, MapInfoMapLevel, levelinfo, NULL, levelflags);
// When the second sky is -NOFLAT-, make it a copy of the first sky
if (strcmp (levelinfo->skypic2, "-NOFLAT-") == 0)
{
@ -717,8 +723,8 @@ static void G_DoParseMapInfo (int lump)
break;
case MITL_CLUSTERDEF: // clusterdef <clusternum>
SC_MustGetNumber ();
clusterindex = FindWadClusterInfo (sc_Number);
sc.MustGetNumber ();
clusterindex = FindWadClusterInfo (sc.Number);
if (clusterindex == -1)
{
clusterindex = wadclusterinfos.Reserve(1);
@ -745,20 +751,20 @@ static void G_DoParseMapInfo (int lump)
}
}
memset (clusterinfo, 0, sizeof(cluster_info_t));
clusterinfo->cluster = sc_Number;
ParseMapInfoLower (ClusterHandlers, MapInfoClusterLevel, NULL, clusterinfo, 0);
clusterinfo->cluster = sc.Number;
ParseMapInfoLower (sc, ClusterHandlers, MapInfoClusterLevel, NULL, clusterinfo, 0);
break;
case MITL_EPISODE:
ParseEpisodeInfo ();
ParseEpisodeInfo(sc);
break;
case MITL_CLEAREPISODES:
ClearEpisodes ();
ClearEpisodes();
break;
case MITL_SKILL:
ParseSkill();
ParseSkill(sc);
break;
case MITL_CLEARSKILLS:
@ -767,7 +773,6 @@ static void G_DoParseMapInfo (int lump)
}
}
SC_Close ();
if (defaultinfo.music != NULL)
{
delete [] defaultinfo.music;
@ -837,7 +842,8 @@ static void ClearEpisodes()
EpiDef.numitems = 0;
}
static void ParseMapInfoLower (MapInfoHandler *handlers,
static void ParseMapInfoLower (FScanner &sc,
MapInfoHandler *handlers,
const char *strings[],
level_info_t *levelinfo,
cluster_info_t *clusterinfo,
@ -849,52 +855,52 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
info = levelinfo ? (BYTE *)levelinfo : (BYTE *)clusterinfo;
while (SC_GetString ())
while (sc.GetString ())
{
if (SC_MatchString (MapInfoTopLevel) != -1)
if (sc.MatchString (MapInfoTopLevel) != -1)
{
SC_UnGet ();
sc.UnGet ();
break;
}
entry = SC_MustMatchString (strings);
entry = sc.MustMatchString (strings);
handler = handlers + entry;
switch (handler->type)
{
case MITYPE_EATNEXT:
SC_MustGetString ();
sc.MustGetString ();
break;
case MITYPE_IGNORE:
break;
case MITYPE_INT:
SC_MustGetNumber ();
*((int *)(info + handler->data1)) = sc_Number;
sc.MustGetNumber ();
*((int *)(info + handler->data1)) = sc.Number;
break;
case MITYPE_FLOAT:
SC_MustGetFloat ();
*((float *)(info + handler->data1)) = sc_Float;
sc.MustGetFloat ();
*((float *)(info + handler->data1)) = sc.Float;
break;
case MITYPE_HEX:
SC_MustGetString ();
*((int *)(info + handler->data1)) = strtoul (sc_String, NULL, 16);
sc.MustGetString ();
*((int *)(info + handler->data1)) = strtoul (sc.String, NULL, 16);
break;
case MITYPE_COLOR:
SC_MustGetString ();
*((DWORD *)(info + handler->data1)) = V_GetColor (NULL, sc_String);
sc.MustGetString ();
*((DWORD *)(info + handler->data1)) = V_GetColor (NULL, sc.String);
break;
case MITYPE_REDIRECT:
SC_MustGetString ();
levelinfo->RedirectType = sc_String;
sc.MustGetString ();
levelinfo->RedirectType = sc.String;
/*
if (levelinfo->RedirectType == NULL ||
!(levelinfo->RedirectType->IsDescendantOf (RUNTIME_CLASS(AInventory))))
{
SC_ScriptError ("%s is not an inventory item", sc_String);
SC_ScriptError ("%s is not an inventory item", sc.String);
}
*/
// Intentional fall-through
@ -903,24 +909,24 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
EndSequence newSeq;
bool useseq = false;
SC_MustGetString ();
if (IsNum (sc_String))
sc.MustGetString ();
if (IsNum (sc.String))
{
int map = atoi (sc_String);
int map = atoi (sc.String);
if (HexenHack)
{
sprintf (sc_String, "&wt@%02d", map);
sprintf (sc.String, "&wt@%02d", map);
}
else
{
sprintf (sc_String, "MAP%02d", map);
sprintf (sc.String, "MAP%02d", map);
}
}
if (strnicmp (sc_String, "EndGame", 7) == 0)
if (strnicmp (sc.String, "EndGame", 7) == 0)
{
int type;
switch (sc_String[7])
switch (sc.String[7])
{
case '1': type = END_Pic1; break;
case '2': type = END_Pic2; break;
@ -933,47 +939,47 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
newSeq.EndType = type;
useseq = true;
}
else if (SC_Compare ("endpic"))
else if (sc.Compare ("endpic"))
{
SC_MustGetString ();
sc.MustGetString ();
newSeq.EndType = END_Pic;
strncpy (newSeq.PicName, sc_String, 8);
strncpy (newSeq.PicName, sc.String, 8);
newSeq.PicName[8] = 0;
useseq = true;
}
else if (SC_Compare ("endbunny"))
else if (sc.Compare ("endbunny"))
{
newSeq.EndType = END_Bunny;
useseq = true;
}
else if (SC_Compare ("endcast"))
else if (sc.Compare ("endcast"))
{
newSeq.EndType = END_Cast;
useseq = true;
}
else if (SC_Compare ("enddemon"))
else if (sc.Compare ("enddemon"))
{
newSeq.EndType = END_Demon;
useseq = true;
}
else if (SC_Compare ("endchess"))
else if (sc.Compare ("endchess"))
{
newSeq.EndType = END_Chess;
useseq = true;
}
else if (SC_Compare ("endunderwater"))
else if (sc.Compare ("endunderwater"))
{
newSeq.EndType = END_Underwater;
useseq = true;
}
else if (SC_Compare ("endbuystrife"))
else if (sc.Compare ("endbuystrife"))
{
newSeq.EndType = END_BuyStrife;
useseq = true;
}
else
{
strncpy ((char *)(info + handler->data1), sc_String, 8);
strncpy ((char *)(info + handler->data1), sc.String, 8);
}
if (useseq)
{
@ -989,22 +995,22 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
}
case MITYPE_LUMPNAME:
SC_MustGetString ();
uppercopy ((char *)(info + handler->data1), sc_String);
sc.MustGetString ();
uppercopy ((char *)(info + handler->data1), sc.String);
flags |= handler->data2;
break;
case MITYPE_SKY:
SC_MustGetString (); // get texture name;
uppercopy ((char *)(info + handler->data1), sc_String);
SC_MustGetFloat (); // get scroll speed
sc.MustGetString (); // get texture name;
uppercopy ((char *)(info + handler->data1), sc.String);
sc.MustGetFloat (); // get scroll speed
if (HexenHack)
{
sc_Float /= 256;
sc.Float /= 256;
}
// Sky scroll speed is specified as pixels per tic, but we
// want pixels per millisecond.
*((float *)(info + handler->data2)) = sc_Float * 35 / 1000;
*((float *)(info + handler->data2)) = sc.Float * 35 / 1000;
break;
case MITYPE_SETFLAG:
@ -1022,18 +1028,18 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
break;
case MITYPE_CLUSTER:
SC_MustGetNumber ();
*((int *)(info + handler->data1)) = sc_Number;
sc.MustGetNumber ();
*((int *)(info + handler->data1)) = sc.Number;
// If this cluster hasn't been defined yet, add it. This is especially needed
// for Hexen, because it doesn't have clusterdefs. If we don't do this, every
// level on Hexen will sometimes be considered as being on the same hub,
// depending on the check done.
if (FindWadClusterInfo (sc_Number) == -1)
if (FindWadClusterInfo (sc.Number) == -1)
{
unsigned int clusterindex = wadclusterinfos.Reserve(1);
clusterinfo = &wadclusterinfos[clusterindex];
memset (clusterinfo, 0, sizeof(cluster_info_t));
clusterinfo->cluster = sc_Number;
clusterinfo->cluster = sc.Number;
if (gameinfo.gametype == GAME_Hexen)
{
clusterinfo->flags |= CLUSTER_HUB;
@ -1042,36 +1048,36 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
break;
case MITYPE_STRING:
SC_MustGetString ();
if (SC_Compare ("lookup"))
sc.MustGetString ();
if (sc.Compare ("lookup"))
{
flags |= handler->data2;
SC_MustGetString ();
sc.MustGetString ();
}
ReplaceString ((char **)(info + handler->data1), sc_String);
ReplaceString ((char **)(info + handler->data1), sc.String);
break;
case MITYPE_F1:
SC_MustGetString ();
sc.MustGetString ();
{
char *colon = strchr (sc_String, ':');
char *colon = strchr (sc.String, ':');
if (colon)
{
*colon = 0;
}
ReplaceString ((char **)(info + handler->data1), sc_String);
ReplaceString ((char **)(info + handler->data1), sc.String);
}
break;
case MITYPE_MUSIC:
SC_MustGetString ();
sc.MustGetString ();
{
char *colon = strchr (sc_String, ':');
char *colon = strchr (sc.String, ':');
if (colon)
{
*colon = 0;
}
ReplaceString ((char **)(info + handler->data1), sc_String);
ReplaceString ((char **)(info + handler->data1), sc.String);
*((int *)(info + handler->data2)) = colon ? atoi (colon + 1) : 0;
if (levelinfo != NULL)
{
@ -1082,8 +1088,8 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
break;
case MITYPE_RELLIGHT:
SC_MustGetNumber ();
*((SBYTE *)(info + handler->data1)) = (SBYTE)clamp (sc_Number / 2, -128, 127);
sc.MustGetNumber ();
*((SBYTE *)(info + handler->data1)) = (SBYTE)clamp (sc.Number / 2, -128, 127);
break;
case MITYPE_CLRBYTES:
@ -1099,29 +1105,29 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
FSpecialAction *sa = new FSpecialAction;
sa->Next = *so;
*so = sa;
SC_SetCMode(true);
SC_MustGetString();
sa->Type = FName(sc_String);
SC_CheckString(",");
SC_MustGetString();
strlwr(sc_String);
sa->Action = FindLineSpecial(sc_String);
sc.SetCMode(true);
sc.MustGetString();
sa->Type = FName(sc.String);
sc.CheckString(",");
sc.MustGetString();
strlwr(sc.String);
sa->Action = FindLineSpecial(sc.String);
int j = 0;
while (j < 5 && SC_CheckString(","))
while (j < 5 && sc.CheckString(","))
{
SC_MustGetNumber();
sa->Args[j++] = sc_Number;
sc.MustGetNumber();
sa->Args[j++] = sc.Number;
}
SC_SetCMode(false);
sc.SetCMode(false);
}
break;
case MITYPE_COMPATFLAG:
if (!SC_CheckNumber()) sc_Number = 1;
if (!sc.CheckNumber()) sc.Number = 1;
if (levelinfo != NULL)
{
if (sc_Number) levelinfo->compatflags |= (DWORD)handler->data1;
if (sc.Number) levelinfo->compatflags |= (DWORD)handler->data1;
else levelinfo->compatflags &= ~ (DWORD)handler->data1;
levelinfo->compatmask |= (DWORD)handler->data1;
}
@ -1147,7 +1153,7 @@ static void ParseMapInfoLower (MapInfoHandler *handlers,
// noskillmenu
// remove
static void ParseEpisodeInfo ()
static void ParseEpisodeInfo (FScanner &sc)
{
int i;
char map[9];
@ -1155,58 +1161,57 @@ static void ParseEpisodeInfo ()
bool picisgfx = false; // Shut up, GCC!!!!
bool remove = false;
char key = 0;
bool addedgfx = false;
bool noskill = false;
// Get map name
SC_MustGetString ();
uppercopy (map, sc_String);
sc.MustGetString ();
uppercopy (map, sc.String);
map[8] = 0;
SC_MustGetString ();
if (SC_Compare ("teaser"))
sc.MustGetString ();
if (sc.Compare ("teaser"))
{
SC_MustGetString ();
sc.MustGetString ();
if (gameinfo.flags & GI_SHAREWARE)
{
uppercopy (map, sc_String);
uppercopy (map, sc.String);
}
SC_MustGetString ();
sc.MustGetString ();
}
do
{
if (SC_Compare ("name"))
if (sc.Compare ("name"))
{
SC_MustGetString ();
ReplaceString (&pic, sc_String);
sc.MustGetString ();
ReplaceString (&pic, sc.String);
picisgfx = false;
}
else if (SC_Compare ("picname"))
else if (sc.Compare ("picname"))
{
SC_MustGetString ();
ReplaceString (&pic, sc_String);
sc.MustGetString ();
ReplaceString (&pic, sc.String);
picisgfx = true;
}
else if (SC_Compare ("remove"))
else if (sc.Compare ("remove"))
{
remove = true;
}
else if (SC_Compare ("key"))
else if (sc.Compare ("key"))
{
SC_MustGetString ();
key = sc_String[0];
sc.MustGetString ();
key = sc.String[0];
}
else if (SC_Compare("noskillmenu"))
else if (sc.Compare("noskillmenu"))
{
noskill = true;
}
else
{
SC_UnGet ();
sc.UnGet ();
break;
}
}
while (SC_GetString ());
while (sc.GetString ());
for (i = 0; i < EpiDef.numitems; ++i)
{
@ -1262,15 +1267,6 @@ static void ParseEpisodeInfo ()
EpisodeMenu[i].fulltext = !picisgfx;
EpisodeNoSkill[i] = noskill;
strncpy (EpisodeMaps[i], map, 8);
if (picisgfx)
{
if (TexMan.CheckForTexture (pic, FTexture::TEX_MiscPatch, 0) == -1)
{
TexMan.AddPatch (pic);
addedgfx = true;
}
}
}
}
@ -1982,6 +1978,14 @@ void G_DoLoadLevel (int position, bool autosave)
G_InitLevelLocals ();
StatusBar->DetachAllMessages ();
// Force 'teamplay' to 'true' if need be.
if (level.flags & LEVEL_FORCETEAMPLAYON)
teamplay = true;
// Force 'teamplay' to 'false' if need be.
if (level.flags & LEVEL_FORCETEAMPLAYOFF)
teamplay = false;
Printf (
"\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"
@ -3141,7 +3145,7 @@ static void InitPlayerClasses ()
}
static void ParseSkill ()
static void ParseSkill (FScanner &sc)
{
FSkillInfo skill;
@ -3153,6 +3157,7 @@ static void ParseSkill ()
skill.EasyBossBrain = false;
skill.AutoUseHealth = false;
skill.RespawnCounter = 0;
skill.RespawnLimit = 0;
skill.Aggressiveness = FRACUNIT;
skill.SpawnFilter = 0;
skill.ACSReturn = AllSkills.Size();
@ -3161,106 +3166,111 @@ static void ParseSkill ()
skill.Shortcut = 0;
skill.TextColor = "";
SC_MustGetString();
skill.Name = sc_String;
sc.MustGetString();
skill.Name = sc.String;
while (SC_GetString ())
while (sc.GetString ())
{
if (SC_Compare ("ammofactor"))
if (sc.Compare ("ammofactor"))
{
SC_MustGetFloat ();
skill.AmmoFactor = FLOAT2FIXED(sc_Float);
sc.MustGetFloat ();
skill.AmmoFactor = FLOAT2FIXED(sc.Float);
}
else if (SC_Compare ("doubleammofactor"))
else if (sc.Compare ("doubleammofactor"))
{
SC_MustGetFloat ();
skill.DoubleAmmoFactor = FLOAT2FIXED(sc_Float);
sc.MustGetFloat ();
skill.DoubleAmmoFactor = FLOAT2FIXED(sc.Float);
}
else if (SC_Compare ("damagefactor"))
else if (sc.Compare ("damagefactor"))
{
SC_MustGetFloat ();
skill.DamageFactor = FLOAT2FIXED(sc_Float);
sc.MustGetFloat ();
skill.DamageFactor = FLOAT2FIXED(sc.Float);
}
else if (SC_Compare ("fastmonsters"))
else if (sc.Compare ("fastmonsters"))
{
skill.FastMonsters = true;
}
else if (SC_Compare ("disablecheats"))
else if (sc.Compare ("disablecheats"))
{
skill.DisableCheats = true;
}
else if (SC_Compare ("easybossbrain"))
else if (sc.Compare ("easybossbrain"))
{
skill.EasyBossBrain = true;
}
else if (SC_Compare("autousehealth"))
else if (sc.Compare("autousehealth"))
{
skill.AutoUseHealth = true;
}
else if (SC_Compare("respawntime"))
else if (sc.Compare("respawntime"))
{
SC_MustGetFloat ();
skill.RespawnCounter = int(sc_Float*TICRATE);
sc.MustGetFloat ();
skill.RespawnCounter = int(sc.Float*TICRATE);
}
else if (SC_Compare("Aggressiveness"))
else if (sc.Compare("respawnlimit"))
{
SC_MustGetFloat ();
skill.Aggressiveness = FRACUNIT - FLOAT2FIXED(clamp<float>(sc_Float, 0,1));
sc.MustGetNumber ();
skill.RespawnLimit = sc.Number;
}
else if (SC_Compare("SpawnFilter"))
else if (sc.Compare("Aggressiveness"))
{
SC_MustGetString ();
strlwr(sc_String);
if (strstr(sc_String, "easy")) skill.SpawnFilter|=MTF_EASY;
if (strstr(sc_String, "normal")) skill.SpawnFilter|=MTF_NORMAL;
if (strstr(sc_String, "hard")) skill.SpawnFilter|=MTF_HARD;
sc.MustGetFloat ();
skill.Aggressiveness = FRACUNIT - FLOAT2FIXED(clamp<float>(sc.Float, 0,1));
}
else if (SC_Compare("ACSReturn"))
else if (sc.Compare("SpawnFilter"))
{
SC_MustGetNumber ();
skill.ACSReturn = sc_Number;
sc.MustGetString ();
strlwr(sc.String);
if (strstr(sc.String, "easy")) skill.SpawnFilter|=MTF_EASY;
if (strstr(sc.String, "normal")) skill.SpawnFilter|=MTF_NORMAL;
if (strstr(sc.String, "hard")) skill.SpawnFilter|=MTF_HARD;
}
else if (SC_Compare("Name"))
else if (sc.Compare("ACSReturn"))
{
SC_MustGetString ();
skill.MenuName = sc_String;
sc.MustGetNumber ();
skill.ACSReturn = sc.Number;
}
else if (sc.Compare("Name"))
{
sc.MustGetString ();
skill.MenuName = sc.String;
skill.MenuNameIsLump = false;
}
else if (SC_Compare("PlayerClassName"))
else if (sc.Compare("PlayerClassName"))
{
SC_MustGetString ();
FName pc = sc_String;
SC_MustGetString ();
skill.MenuNamesForPlayerClass[pc]=sc_String;
sc.MustGetString ();
FName pc = sc.String;
sc.MustGetString ();
skill.MenuNamesForPlayerClass[pc]=sc.String;
}
else if (SC_Compare("PicName"))
else if (sc.Compare("PicName"))
{
SC_MustGetString ();
skill.MenuName = sc_String;
sc.MustGetString ();
skill.MenuName = sc.String;
skill.MenuNameIsLump = true;
}
else if (SC_Compare("MustConfirm"))
else if (sc.Compare("MustConfirm"))
{
skill.MustConfirm = true;
if (SC_CheckToken(TK_StringConst))
if (sc.CheckToken(TK_StringConst))
{
skill.MustConfirmText = sc_String;
skill.MustConfirmText = sc.String;
}
}
else if (SC_Compare("Key"))
else if (sc.Compare("Key"))
{
SC_MustGetString();
skill.Shortcut = tolower(sc_String[0]);
sc.MustGetString();
skill.Shortcut = tolower(sc.String[0]);
}
else if (SC_Compare("TextColor"))
else if (sc.Compare("TextColor"))
{
SC_MustGetString();
sc.MustGetString();
skill.TextColor = '[';
skill.TextColor << sc_String << ']';
skill.TextColor << sc.String << ']';
}
else
{
SC_UnGet ();
sc.UnGet ();
break;
}
}
@ -3299,6 +3309,9 @@ int G_SkillProperty(ESkillProperty prop)
return TICRATE * (gameinfo.gametype != GAME_Strife ? 12 : 16);
return AllSkills[gameskill].RespawnCounter;
case SKILLP_RespawnLimit:
return AllSkills[gameskill].RespawnLimit;
case SKILLP_Aggressiveness:
return AllSkills[gameskill].Aggressiveness;
@ -3341,6 +3354,7 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other)
AutoUseHealth = other.AutoUseHealth;
EasyBossBrain = other.EasyBossBrain;
RespawnCounter= other.RespawnCounter;
RespawnLimit= other.RespawnLimit;
Aggressiveness= other.Aggressiveness;
SpawnFilter = other.SpawnFilter;
ACSReturn = other.ACSReturn;

View file

@ -115,6 +115,9 @@
#define LEVEL_ALLOWRESPAWN UCONST64(0x4000000000000)
#define LEVEL_FORCETEAMPLAYON UCONST64(0x8000000000000)
#define LEVEL_FORCETEAMPLAYOFF UCONST64(0x10000000000000)
struct acsdefered_s;
struct FSpecialAction
@ -376,6 +379,7 @@ enum ESkillProperty
SKILLP_DamageFactor,
SKILLP_FastMonsters,
SKILLP_Respawn,
SKILLP_RespawnLimit,
SKILLP_Aggressiveness,
SKILLP_DisableCheats,
SKILLP_AutoUseHealth,
@ -397,6 +401,7 @@ struct FSkillInfo
bool AutoUseHealth;
bool EasyBossBrain;
int RespawnCounter;
int RespawnLimit;
fixed_t Aggressiveness;
int SpawnFilter;
int ACSReturn;

View file

@ -61,28 +61,31 @@ IMPLEMENT_CLASS (DImpactDecal)
DBaseDecal::DBaseDecal ()
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), LeftDistance(0), Z(0), ScaleX(FRACUNIT), ScaleY(FRACUNIT), Alpha(FRACUNIT),
AlphaColor(0), Translation(0), PicNum(0xFFFF), RenderFlags(0), RenderStyle(0)
AlphaColor(0), Translation(0), PicNum(0xFFFF), RenderFlags(0)
{
RenderStyle = STYLE_None;
}
DBaseDecal::DBaseDecal (fixed_t z)
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), LeftDistance(0), Z(z), ScaleX(FRACUNIT), ScaleY(FRACUNIT), Alpha(FRACUNIT),
AlphaColor(0), Translation(0), PicNum(0xFFFF), RenderFlags(0), RenderStyle(0)
AlphaColor(0), Translation(0), PicNum(0xFFFF), RenderFlags(0)
{
RenderStyle = STYLE_None;
}
DBaseDecal::DBaseDecal (int statnum, fixed_t z)
: DThinker(statnum),
WallNext(0), WallPrev(0), LeftDistance(0), Z(z), ScaleX(FRACUNIT), ScaleY(FRACUNIT), Alpha(FRACUNIT),
AlphaColor(0), Translation(0), PicNum(0xFFFF), RenderFlags(0), RenderStyle(0)
AlphaColor(0), Translation(0), PicNum(0xFFFF), RenderFlags(0)
{
RenderStyle = STYLE_None;
}
DBaseDecal::DBaseDecal (const AActor *basis)
: DThinker(STAT_DECAL),
WallNext(0), WallPrev(0), LeftDistance(0), Z(basis->z), ScaleX(basis->scaleX), ScaleY(basis->scaleY),
Alpha(basis->alpha), AlphaColor(basis->alphacolor), Translation(basis->Translation), PicNum(basis->picnum),
Alpha(basis->alpha), AlphaColor(basis->fillcolor), Translation(basis->Translation), PicNum(basis->picnum),
RenderFlags(basis->renderflags), RenderStyle(basis->RenderStyle)
{
}

View file

@ -101,7 +101,7 @@ static const char * keywords_lock[]={
//
//===========================================================================
static void AddOneKey(Keygroup * keygroup, const PClass * mi)
static void AddOneKey(Keygroup *keygroup, const PClass *mi, FScanner &sc)
{
if (mi)
{
@ -123,12 +123,12 @@ static void AddOneKey(Keygroup * keygroup, const PClass * mi)
}
else
{
SC_ScriptError("'%s' is not an inventory item", sc_String);
sc.ScriptError("'%s' is not an inventory item", sc.String);
}
}
else
{
SC_ScriptError("Unknown item '%s'", sc_String);
sc.ScriptError("Unknown item '%s'", sc.String);
}
}
@ -138,20 +138,20 @@ static void AddOneKey(Keygroup * keygroup, const PClass * mi)
//
//===========================================================================
static Keygroup * ParseKeygroup()
static Keygroup * ParseKeygroup(FScanner &sc)
{
Keygroup * keygroup;
const PClass * mi;
SC_MustGetStringName("{");
keygroup=new Keygroup;
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
keygroup = new Keygroup;
while (!sc.CheckString("}"))
{
SC_MustGetString();
mi=PClass::FindClass(sc_String);
AddOneKey(keygroup, mi);
sc.MustGetString();
mi = PClass::FindClass(sc.String);
AddOneKey(keygroup, mi, sc);
}
if (keygroup->anykeylist.Size()==0)
if (keygroup->anykeylist.Size() == 0)
{
delete keygroup;
return NULL;
@ -182,7 +182,7 @@ static void PrintMessage (const char *str)
//
//===========================================================================
static void ParseLock()
static void ParseLock(FScanner &sc)
{
int i,r,g,b;
int keynum;
@ -191,84 +191,90 @@ static void ParseLock()
Keygroup * keygroup;
const PClass * mi;
SC_MustGetNumber();
keynum=sc_Number;
sc.MustGetNumber();
keynum = sc.Number;
SC_MustGetString();
if (SC_Compare("DOOM"))
sc.MustGetString();
if (sc.Compare("DOOM"))
{
if (gameinfo.gametype != GAME_Doom) keynum=-1;
}
else if (SC_Compare("HERETIC"))
else if (sc.Compare("HERETIC"))
{
if (gameinfo.gametype != GAME_Heretic) keynum=-1;
}
else if (SC_Compare("HEXEN"))
else if (sc.Compare("HEXEN"))
{
if (gameinfo.gametype != GAME_Hexen) keynum=-1;
}
else if (SC_Compare("STRIFE"))
else if (sc.Compare("STRIFE"))
{
if (gameinfo.gametype != GAME_Strife) keynum=-1;
}
else SC_UnGet();
else sc.UnGet();
ignorekey=true;
if (keynum>0 && keynum<255)
ignorekey = true;
if (keynum > 0 && keynum < 255)
{
lock=new Lock;
if (locks[keynum]) delete locks[keynum];
locks[keynum]=lock;
lock = new Lock;
if (locks[keynum])
{
delete locks[keynum];
}
locks[keynum] = lock;
locks[keynum]->locksound = S_FindSound("misc/keytry");
ignorekey=false;
}
else if (keynum!=-1)
else if (keynum != -1)
{
SC_ScriptError("Lock index %d out of range", keynum);
sc.ScriptError("Lock index %d out of range", keynum);
}
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
SC_MustGetString();
switch(i=SC_MatchString(keywords_lock))
sc.MustGetString();
switch(i = sc.MatchString(keywords_lock))
{
case 0: // Any
keygroup=ParseKeygroup();
if (keygroup) lock->keylist.Push(keygroup);
keygroup = ParseKeygroup(sc);
if (keygroup)
{
lock->keylist.Push(keygroup);
}
break;
case 1: // message
SC_MustGetString();
lock->message=copystring(sc_String);
sc.MustGetString();
lock->message = copystring(sc.String);
break;
case 2: // remotemsg
SC_MustGetString();
lock->remotemsg=copystring(sc_String);
sc.MustGetString();
lock->remotemsg = copystring(sc.String);
break;
case 3: // mapcolor
SC_MustGetNumber();
r=sc_Number;
SC_MustGetNumber();
g=sc_Number;
SC_MustGetNumber();
b=sc_Number;
lock->rgb=MAKERGB(r,g,b);
sc.MustGetNumber();
r = sc.Number;
sc.MustGetNumber();
g = sc.Number;
sc.MustGetNumber();
b = sc.Number;
lock->rgb = MAKERGB(r,g,b);
break;
case 4: // locksound
SC_MustGetString();
lock->locksound = S_FindSound(sc_String);
sc.MustGetString();
lock->locksound = S_FindSound(sc.String);
break;
default:
mi=PClass::FindClass(sc_String);
mi = PClass::FindClass(sc.String);
if (mi)
{
keygroup=new Keygroup;
AddOneKey(keygroup, mi);
keygroup = new Keygroup;
AddOneKey(keygroup, mi, sc);
if (keygroup)
{
keygroup->anykeylist.ShrinkToFit();
@ -279,8 +285,14 @@ static void ParseLock()
}
}
// copy the messages if the other one does not exist
if (!lock->remotemsg && lock->message) lock->remotemsg = copystring(lock->message);
if (!lock->message && lock->remotemsg) lock->message = copystring(lock->remotemsg);
if (!lock->remotemsg && lock->message)
{
lock->remotemsg = copystring(lock->message);
}
if (!lock->message && lock->remotemsg)
{
lock->message = copystring(lock->remotemsg);
}
lock->keylist.ShrinkToFit();
}
@ -332,24 +344,26 @@ void P_InitKeyMessages()
ClearLocks();
while ((lump = Wads.FindLump ("LOCKDEFS", &lastlump)) != -1)
{
SC_OpenLumpNum (lump, "LOCKDEFS");
while (SC_GetString ())
FScanner sc(lump, "LOCKDEFS");
while (sc.GetString ())
{
if (SC_Compare("LOCK"))
if (sc.Compare("LOCK"))
{
ParseLock();
ParseLock(sc);
}
else if (SC_Compare("CLEARLOCKS"))
else if (sc.Compare("CLEARLOCKS"))
{
// clear all existing lock defintions and key numbers
// clear all existing lock definitions and key numbers
ClearLocks();
}
else
SC_ScriptError("Unknown command %s in LockDef", sc_String);
{
sc.ScriptError("Unknown command %s in LockDef", sc.String);
}
SC_Close();
}
keysdone=true;
sc.Close();
}
keysdone = true;
}
//===========================================================================

View file

@ -52,7 +52,7 @@ public:
WORD Translation;
WORD PicNum;
DWORD RenderFlags;
BYTE RenderStyle;
FRenderStyle RenderStyle;
sector_t * Sector; // required for 3D floors
protected:

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@
#include "v_collection.h"
class FBarTexture;
class FScanner;
struct SBarInfoCommand; //we need to be able to use this before it is defined.
@ -19,7 +20,7 @@ struct SBarInfoCommand
{
SBarInfoCommand();
~SBarInfoCommand();
void setString(const char* source, int strnum, int maxlength=-1, bool exact=false);
void setString(FScanner &sc, const char* source, int strnum, int maxlength=-1, bool exact=false);
int type;
int special;
@ -52,10 +53,11 @@ struct SBarInfo
int GetGameType() { return gameType; }
void ParseSBarInfo(int lump);
void ParseSBarInfoBlock(SBarInfoBlock &block);
void ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block);
void getCoordinates(FScanner &sc, SBarInfoCommand &cmd); //retrieves the next two arguments as x and y.
int newImage(const char* patchname);
void Init();
EColorRange GetTranslation(char* translation);
EColorRange GetTranslation(FScanner &sc, char* translation);
SBarInfo();
SBarInfo(int lumpnum);
~SBarInfo();

View file

@ -893,17 +893,17 @@ void HUD_InitHud()
{
case GAME_Heretic:
case GAME_Hexen:
healthpic=TexMan[TexMan.AddPatch("ARTIPTN2", ns_sprites)];
healthpic = TexMan.FindTexture("ARTIPTN2");
HudFont=FFont::FindFont("HUDFONT_RAVEN");
break;
case GAME_Strife:
healthpic=TexMan[TexMan.AddPatch("I_MDKT")];
healthpic = TexMan.FindTexture("I_MDKT");
HudFont=BigFont; // Strife doesn't have anything nice so use the standard font
break;
default:
healthpic=TexMan[TexMan.AddPatch("MEDIA0", ns_sprites)];
healthpic = TexMan.FindTexture("MEDIA0");
HudFont=FFont::FindFont("HUDFONT_DOOM");
break;
}
@ -913,12 +913,12 @@ void HUD_InitHud()
if (HudFont == NULL) HudFont = BigFont;
if (IndexFont == NULL) IndexFont = ConFont; // Emergency fallback
invgems[0] = TexMan[TexMan.AddPatch("INVGEML1")];
invgems[1] = TexMan[TexMan.AddPatch("INVGEML2")];
invgems[2] = TexMan[TexMan.AddPatch("INVGEMR1")];
invgems[3] = TexMan[TexMan.AddPatch("INVGEMR2")];
invgems[0] = TexMan.FindTexture("INVGEML1");
invgems[1] = TexMan.FindTexture("INVGEML2");
invgems[2] = TexMan.FindTexture("INVGEMR1");
invgems[3] = TexMan.FindTexture("INVGEMR2");
fragpic = TexMan[TexMan.AddPatch("HU_FRAGS")]; // Sadly, I don't have anything usable for this. :(
fragpic = TexMan.FindTexture("HU_FRAGS"); // Sadly, I don't have anything usable for this. :(
KeyTypes.Clear();
UnassignedKeyTypes.Clear();
@ -928,42 +928,39 @@ void HUD_InitHud()
while ((lump = Wads.FindLump ("ALTHUDCF", &lastlump)) != -1)
{
SC_OpenLumpNum(lump, "ALTHUDCF");
while (SC_GetString())
FScanner sc(lump, "ALTHUDCF");
while (sc.GetString())
{
if (SC_Compare("Health"))
if (sc.Compare("Health"))
{
SC_MustGetString();
int tex = TexMan.AddPatch(sc_String);
if (tex<=0) tex = TexMan.AddPatch(sc_String, ns_sprites);
if (tex>0) healthpic = TexMan[tex];
sc.MustGetString();
int tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch);
if (tex > 0) healthpic = TexMan[tex];
}
else
{
const PClass * ti = PClass::FindClass(sc_String);
const PClass * ti = PClass::FindClass(sc.String);
if (!ti)
{
Printf("Unknown item class '%s' in ALTHUDCF\n", sc_String);
Printf("Unknown item class '%s' in ALTHUDCF\n", sc.String);
}
else if (!ti->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{
Printf("Invalid item class '%s' in ALTHUDCF\n", sc_String);
Printf("Invalid item class '%s' in ALTHUDCF\n", sc.String);
ti=NULL;
}
SC_MustGetString();
sc.MustGetString();
int tex=0;
if (!SC_Compare("0") && !SC_Compare("NULL") && !SC_Compare(""))
if (!sc.Compare("0") && !sc.Compare("NULL") && !sc.Compare(""))
{
tex = TexMan.AddPatch(sc_String);
if (tex<=0) tex = TexMan.AddPatch(sc_String, ns_sprites);
tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch);
}
else tex=-1;
if (ti) const_cast<PClass*>(ti)->Meta.SetMetaInt(HUMETA_AltIcon, tex);
}
}
SC_Close();
}
}

View file

@ -1437,7 +1437,18 @@ void FBaseStatusBar::BlendView (float blend[4])
if (cnt > 228)
cnt = 228;
AddBlend (1.f, 0.f, 0.f, cnt / 255.f, blend);
APlayerPawn *mo = players[consoleplayer].mo;
// [CW] If no damage fade is specified, assume defaults.
if (!mo->HasDamageFade)
{
mo->HasDamageFade = true;
mo->RedDamageFade = 255;
mo->GreenDamageFade = 0;
mo->BlueDamageFade = 0;
}
AddBlend (mo->RedDamageFade / 255, mo->GreenDamageFade / 255, mo->BlueDamageFade / 255, cnt / 255.f, blend);
}
// Unlike Doom, I did not have any utility source to look at to find the

View file

@ -423,7 +423,7 @@ void A_AcolyteBits (AActor *self)
}
else
{
self->RenderStyle = STYLE_None;
self->RenderStyle.BlendOp = STYLEOP_None;
}
}
}

View file

@ -290,7 +290,7 @@ void A_Beacon (AActor *self)
self->flags &= ~MF_SPECIAL;
static_cast<AInventory *>(self)->DropTime = 0;
// Set up the new rebel.
rebel->threshold = 100;
rebel->threshold = BASETHRESHOLD;
rebel->target = NULL;
rebel->flags4 |= MF4_INCOMBAT;
rebel->LastHeard = owner; // Make sure the rebels look for targets

View file

@ -158,7 +158,7 @@ void GLWall::DrawDecal(DBaseDecal *actor, seg_t *seg, sector_t *frontSector, sec
float red, green, blue;
if (actor->RenderStyle == STYLE_Shaded)
if (actor->RenderStyle.Flags & STYLEF_ColorIsFixed)
{
loadAlpha = true;
p.LightColor.a=CM_SHADE;
@ -314,30 +314,16 @@ void GLWall::DrawDecal(DBaseDecal *actor, seg_t *seg, sector_t *frontSector, sec
// fog is set once per wall in the calling function and not per decal!
gl.Color4f(red, green, blue, a);
switch(actor->RenderStyle)
{
case STYLE_Shaded:
case STYLE_Translucent:
gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.AlphaFunc(GL_GREATER,0.0f);
break;
case STYLE_Add:
gl.BlendFunc(GL_SRC_ALPHA, GL_ONE);
gl.AlphaFunc(GL_GREATER,0.0f);
break;
FRenderStyle style = actor->RenderStyle;
style.Flags &= ~STYLEF_ColorIsFixed; // this is handled differently for decals (see above)
case STYLE_Fuzzy:
gl.BlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
gl.AlphaFunc(GL_GREATER,0.0f);
break;
gl_SetRenderStyle(style, false, false);
default:
gl.BlendFunc(GL_ONE,GL_ZERO);
gl.AlphaFunc(GL_GEQUAL,0.5f);
break;
// If srcalpha is one it looks better with a higher alpha threshold
if (style.SrcAlpha == STYLEALPHA_One) gl.AlphaFunc(GL_GEQUAL, 0.5f);
else gl.AlphaFunc(GL_GREATER, 0.f);
}
gl.Begin(GL_TRIANGLE_FAN);
for(i=0;i<4;i++)
{

View file

@ -59,9 +59,9 @@
EXTERN_CVAR (Float, gl_lights_intensity);
EXTERN_CVAR (Float, gl_lights_size);
int ScriptDepth;
void gl_ParseSkybox();
void gl_InitGlow();
void gl_ParseBrightmap(int);
void gl_ParseSkybox(FScanner &sc);
void gl_InitGlow(FScanner &sc);
void gl_ParseBrightmap(FScanner &sc, int);
//==========================================================================
//
@ -257,35 +257,35 @@ enum {
extern int ScriptDepth;
inline float gl_ParseFloat()
inline float gl_ParseFloat(FScanner &sc)
{
SC_GetFloat();
sc.GetFloat();
return sc_Float;
return sc.Float;
}
inline int gl_ParseInt()
inline int gl_ParseInt(FScanner &sc)
{
SC_GetNumber();
sc.GetNumber();
return sc_Number;
return sc.Number;
}
inline char *gl_ParseString()
inline char *gl_ParseString(FScanner &sc)
{
SC_GetString();
sc.GetString();
return sc_String;
return sc.String;
}
void gl_ParseTriple(float floatVal[3])
void gl_ParseTriple(FScanner &sc, float floatVal[3])
{
for (int i = 0; i < 3; i++)
{
floatVal[i] = gl_ParseFloat();
floatVal[i] = gl_ParseFloat(sc);
}
}
@ -312,7 +312,7 @@ void gl_AddLightDefaults(FLightDefaults *defaults)
// parse thing 9800
void gl_ParsePointLight()
void gl_ParsePointLight(FScanner &sc)
{
int type;
float floatTriple[3];
@ -321,19 +321,19 @@ void gl_ParsePointLight()
FLightDefaults *defaults;
// get name
SC_GetString();
name = sc_String;
sc.GetString();
name = sc.String;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
defaults = new FLightDefaults(name.GetChars(), PointLight);
ScriptDepth++;
while (ScriptDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -344,45 +344,45 @@ void gl_ParsePointLight()
ScriptDepth--;
break;
case LIGHTTAG_COLOR:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255));
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
break;
case LIGHTTAG_OFFSET:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetOffset(FROM_MAP(floatTriple[0]), FROM_MAP(floatTriple[1]), FROM_MAP(floatTriple[2]));
break;
case LIGHTTAG_SIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_INTENSITY, intVal);
break;
case LIGHTTAG_SUBTRACTIVE:
defaults->SetSubtractive(gl_ParseInt() != 0);
defaults->SetSubtractive(gl_ParseInt(sc) != 0);
break;
case LIGHTTAG_ADDITIVE:
defaults->SetAdditive(gl_ParseInt() != 0);
defaults->SetAdditive(gl_ParseInt(sc) != 0);
break;
case LIGHTTAG_HALO:
defaults->SetHalo(gl_ParseInt() != 0);
defaults->SetHalo(gl_ParseInt(sc) != 0);
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
gl_AddLightDefaults(defaults);
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
void gl_ParsePulseLight()
void gl_ParsePulseLight(FScanner &sc)
{
int type;
float floatVal, floatTriple[3];
@ -391,19 +391,19 @@ void gl_ParsePulseLight()
FLightDefaults *defaults;
// get name
SC_GetString();
name = sc_String;
sc.GetString();
name = sc.String;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
defaults = new FLightDefaults(name.GetChars(), PulseLight);
ScriptDepth++;
while (ScriptDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -414,50 +414,50 @@ void gl_ParsePulseLight()
ScriptDepth--;
break;
case LIGHTTAG_COLOR:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255));
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
break;
case LIGHTTAG_OFFSET:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetOffset(FROM_MAP(floatTriple[0]), FROM_MAP(floatTriple[1]), FROM_MAP(floatTriple[2]));
break;
case LIGHTTAG_SIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_INTENSITY, intVal);
break;
case LIGHTTAG_SECSIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
break;
case LIGHTTAG_INTERVAL:
floatVal = gl_ParseFloat();
floatVal = gl_ParseFloat(sc);
defaults->SetAngle(FLOAT_TO_ANGLE(floatVal * TICRATE));
break;
case LIGHTTAG_SUBTRACTIVE:
defaults->SetSubtractive(gl_ParseInt() != 0);
defaults->SetSubtractive(gl_ParseInt(sc) != 0);
break;
case LIGHTTAG_HALO:
defaults->SetHalo(gl_ParseInt() != 0);
defaults->SetHalo(gl_ParseInt(sc) != 0);
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
gl_AddLightDefaults(defaults);
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
void gl_ParseFlickerLight()
void gl_ParseFlickerLight(FScanner &sc)
{
int type;
float floatVal, floatTriple[3];
@ -466,19 +466,19 @@ void gl_ParseFlickerLight()
FLightDefaults *defaults;
// get name
SC_GetString();
name = sc_String;
sc.GetString();
name = sc.String;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
defaults = new FLightDefaults(name.GetChars(), FlickerLight);
ScriptDepth++;
while (ScriptDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -489,50 +489,50 @@ void gl_ParseFlickerLight()
ScriptDepth--;
break;
case LIGHTTAG_COLOR:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255));
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
break;
case LIGHTTAG_OFFSET:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetOffset(FROM_MAP(floatTriple[0]), FROM_MAP(floatTriple[1]), FROM_MAP(floatTriple[2]));
break;
case LIGHTTAG_SIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_INTENSITY, intVal);
break;
case LIGHTTAG_SECSIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
break;
case LIGHTTAG_CHANCE:
floatVal = gl_ParseFloat();
floatVal = gl_ParseFloat(sc);
defaults->SetAngle((angle_t)(floatVal * ANGLE_MAX));
break;
case LIGHTTAG_SUBTRACTIVE:
defaults->SetSubtractive(gl_ParseInt() != 0);
defaults->SetSubtractive(gl_ParseInt(sc) != 0);
break;
case LIGHTTAG_HALO:
defaults->SetHalo(gl_ParseInt() != 0);
defaults->SetHalo(gl_ParseInt(sc) != 0);
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
gl_AddLightDefaults(defaults);
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
void gl_ParseFlickerLight2()
void gl_ParseFlickerLight2(FScanner &sc)
{
int type;
float floatVal, floatTriple[3];
@ -541,19 +541,19 @@ void gl_ParseFlickerLight2()
FLightDefaults *defaults;
// get name
SC_GetString();
name = sc_String;
sc.GetString();
name = sc.String;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
defaults = new FLightDefaults(name.GetChars(), RandomFlickerLight);
ScriptDepth++;
while (ScriptDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -564,38 +564,38 @@ void gl_ParseFlickerLight2()
ScriptDepth--;
break;
case LIGHTTAG_COLOR:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255));
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
break;
case LIGHTTAG_OFFSET:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetOffset(FROM_MAP(floatTriple[0]), FROM_MAP(floatTriple[1]), FROM_MAP(floatTriple[2]));
break;
case LIGHTTAG_SIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_INTENSITY, intVal);
break;
case LIGHTTAG_SECSIZE:
intVal = clamp<int>(gl_ParseInt(), 0, 255);
intVal = clamp<int>(gl_ParseInt(sc), 0, 255);
defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal);
break;
case LIGHTTAG_INTERVAL:
floatVal = gl_ParseFloat();
floatVal = gl_ParseFloat(sc);
defaults->SetAngle((angle_t)(floatVal * ANGLE_MAX));
break;
case LIGHTTAG_SUBTRACTIVE:
defaults->SetSubtractive(gl_ParseInt() != 0);
defaults->SetSubtractive(gl_ParseInt(sc) != 0);
break;
case LIGHTTAG_HALO:
defaults->SetHalo(gl_ParseInt() != 0);
defaults->SetHalo(gl_ParseInt(sc) != 0);
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
if (defaults->GetArg(LIGHT_SECONDARY_INTENSITY) < defaults->GetArg(LIGHT_INTENSITY))
@ -608,12 +608,12 @@ void gl_ParseFlickerLight2()
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
void gl_ParseSectorLight()
void gl_ParseSectorLight(FScanner &sc)
{
int type;
float floatVal;
@ -622,19 +622,19 @@ void gl_ParseSectorLight()
FLightDefaults *defaults;
// get name
SC_GetString();
name = sc_String;
sc.GetString();
name = sc.String;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
defaults = new FLightDefaults(name.GetChars(), SectorLight);
ScriptDepth++;
while (ScriptDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -645,37 +645,37 @@ void gl_ParseSectorLight()
ScriptDepth--;
break;
case LIGHTTAG_COLOR:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetArg(LIGHT_RED, clamp<int>((int)(floatTriple[0] * 255), 0, 255));
defaults->SetArg(LIGHT_GREEN, clamp<int>((int)(floatTriple[1] * 255), 0, 255));
defaults->SetArg(LIGHT_BLUE, clamp<int>((int)(floatTriple[2] * 255), 0, 255));
break;
case LIGHTTAG_OFFSET:
gl_ParseTriple(floatTriple);
gl_ParseTriple(sc, floatTriple);
defaults->SetOffset(FROM_MAP(floatTriple[0]), FROM_MAP(floatTriple[1]), FROM_MAP(floatTriple[2]));
break;
case LIGHTTAG_SCALE:
floatVal = gl_ParseFloat();
floatVal = gl_ParseFloat(sc);
defaults->SetArg(LIGHT_SCALE, (byte)(floatVal * 255));
break;
case LIGHTTAG_SUBTRACTIVE:
defaults->SetSubtractive(gl_ParseInt() != 0);
defaults->SetSubtractive(gl_ParseInt(sc) != 0);
break;
case LIGHTTAG_HALO:
defaults->SetHalo(gl_ParseInt() != 0);
defaults->SetHalo(gl_ParseInt(sc) != 0);
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
gl_AddLightDefaults(defaults);
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
@ -702,30 +702,30 @@ void gl_AddLightAssociation(FLightAssociation *assoc)
}
void gl_ParseFrame(FString name)
void gl_ParseFrame(FScanner &sc, FString name)
{
int type, startDepth;
FString frameName;
// get name
SC_GetString();
if (strlen(sc_String) > 8)
sc.GetString();
if (strlen(sc.String) > 8)
{
SC_ScriptError("Name longer than 8 characters: %s\n", sc_String);
sc.ScriptError("Name longer than 8 characters: %s\n", sc.String);
}
frameName = sc_String;
frameName = sc.String;
startDepth = ScriptDepth;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
ScriptDepth++;
while (ScriptDepth > startDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -736,42 +736,42 @@ void gl_ParseFrame(FString name)
ScriptDepth--;
break;
case LIGHTTAG_LIGHT:
gl_ParseString();
gl_AddLightAssociation(new FLightAssociation(name.GetChars(), frameName.GetChars(), sc_String));
gl_ParseString(sc);
gl_AddLightAssociation(new FLightAssociation(name.GetChars(), frameName.GetChars(), sc.String));
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
void gl_ParseObject()
void gl_ParseObject(FScanner &sc)
{
int type;
FString name;
// get name
SC_GetString();
name = sc_String;
sc.GetString();
name = sc.String;
// check for opening brace
SC_GetString();
if (SC_Compare("{"))
sc.GetString();
if (sc.Compare("{"))
{
ScriptDepth++;
while (ScriptDepth)
{
SC_GetString();
if ((type = SC_MatchString(LightTags)) != -1)
sc.GetString();
if ((type = sc.MatchString(LightTags)) != -1)
{
switch (type)
{
@ -782,19 +782,19 @@ void gl_ParseObject()
ScriptDepth--;
break;
case LIGHTTAG_FRAME:
gl_ParseFrame(name);
gl_ParseFrame(sc, name);
break;
}
}
else
{
SC_ScriptError("Unknown tag: %s\n", sc_String);
sc.ScriptError("Unknown tag: %s\n", sc.String);
}
}
}
else
{
SC_ScriptError("Expected '{'.\n");
sc.ScriptError("Expected '{'.\n");
}
}
@ -880,15 +880,15 @@ enum
// There is no functionality for this stuff!
//
//==========================================================================
bool gl_ParseShader()
bool gl_ParseShader(FScanner &sc)
{
int ShaderDepth = 0;
if (SC_GetString())
if (sc.GetString())
{
char *tmp;
tmp = strstr(sc_String, "{");
tmp = strstr(sc.String, "{");
while (tmp)
{
ShaderDepth++;
@ -896,7 +896,7 @@ bool gl_ParseShader()
tmp = strstr(tmp, "{");
}
tmp = strstr(sc_String, "}");
tmp = strstr(sc.String, "}");
while (tmp)
{
ShaderDepth--;
@ -1110,7 +1110,7 @@ void gl_RecreateAllAttachedLights()
// by LoadDynLightDefs, which wasn't simply integrated into ParseDefs
// because of the way the code needs to load two out of five lumps.
//==========================================================================
void gl_DoParseDefs(int workingLump)
void gl_DoParseDefs(FScanner &sc, int workingLump)
{
int recursion=0;
int lump, type;
@ -1118,84 +1118,80 @@ void gl_DoParseDefs(int workingLump)
// Get actor class name.
while (true)
{
SC_SavePos();
if (!SC_GetToken ())
sc.SavePos();
if (!sc.GetToken ())
{
if (recursion==0) return;
SC_Close();
SC_RestoreScriptState();
recursion--;
continue;
return;
}
if ((type = SC_MatchString(CoreKeywords)) == -1)
if ((type = sc.MatchString(CoreKeywords)) == -1)
{
SC_ScriptError("Error parsing defs. Unknown tag: %s.\n", sc_String);
sc.ScriptError("Error parsing defs. Unknown tag: %s.\n", sc.String);
break;
}
switch (type)
{
case TAG_INCLUDE:
{
SC_MustGetString();
// This is not using SC_Open because it can print a more useful error message when done here
lump = Wads.CheckNumForFullName(sc_String);
sc.MustGetString();
// This is not using sc.Open because it can print a more useful error message when done here
lump = Wads.CheckNumForFullName(sc.String);
// Try a normal WAD name lookup only if it's a proper name without path
// separator and not longer than 8 characters.
if (lump==-1 && strlen(sc_String) <= 8 && !strchr(sc_String, '/'))
lump = Wads.CheckNumForName(sc_String);
if (lump==-1 && strlen(sc.String) <= 8 && !strchr(sc.String, '/'))
lump = Wads.CheckNumForName(sc.String);
if (lump==-1)
SC_ScriptError("Lump '%s' not found", sc_String);
SC_SaveScriptState();
SC_OpenLumpNum(lump, sc_String);
recursion++;
sc.ScriptError("Lump '%s' not found", sc.String);
FScanner newscanner(lump, sc.String);
gl_DoParseDefs(newscanner, lump);
break;
}
case LIGHT_POINT:
gl_ParsePointLight();
gl_ParsePointLight(sc);
break;
case LIGHT_PULSE:
gl_ParsePulseLight();
gl_ParsePulseLight(sc);
break;
case LIGHT_FLICKER:
gl_ParseFlickerLight();
gl_ParseFlickerLight(sc);
break;
case LIGHT_FLICKER2:
gl_ParseFlickerLight2();
gl_ParseFlickerLight2(sc);
break;
case LIGHT_SECTOR:
gl_ParseSectorLight();
gl_ParseSectorLight(sc);
break;
case LIGHT_OBJECT:
gl_ParseObject();
gl_ParseObject(sc);
break;
case LIGHT_CLEAR:
gl_ReleaseLights();
break;
case TAG_SHADER:
gl_ParseShader();
gl_ParseShader(sc);
break;
case TAG_CLEARSHADERS:
break;
case TAG_SKYBOX:
gl_ParseSkybox();
gl_ParseSkybox(sc);
break;
case TAG_GLOW:
gl_InitGlow();
gl_InitGlow(sc);
break;
case TAG_BRIGHTMAP:
gl_ParseBrightmap(workingLump);
gl_ParseBrightmap(sc, workingLump);
break;
case TAG_DISABLE_FB:
{
SC_MustGetString();
const PClass *cls = PClass::FindClass(sc_String);
sc.MustGetString();
const PClass *cls = PClass::FindClass(sc.String);
if (cls) GetDefaultByType(cls)->renderflags |= RF_NEVERFULLBRIGHT;
}
break;
}
}
}
}
void gl_LoadDynLightDefs(char * defsLump)
{
@ -1204,9 +1200,8 @@ void gl_LoadDynLightDefs(char * defsLump)
lastLump = 0;
while ((workingLump = Wads.FindLump(defsLump, &lastLump)) != -1)
{
SC_OpenLumpNum(workingLump, defsLump);
gl_DoParseDefs(workingLump);
SC_Close();
FScanner sc(workingLump, defsLump);
gl_DoParseDefs(sc, workingLump);
}
}

View file

@ -253,14 +253,14 @@ void GLFlat::Draw(int pass)
{
case GLPASS_BASE:
gl_SetColor(lightlevel, extralight*gl_weaponlight, &Colormap,1.0f);
if (!foggy) gl_SetFog(lightlevel, Colormap.FadeColor, STYLE_Normal, Colormap.LightColor.a);
if (!foggy) gl_SetFog(lightlevel, Colormap.FadeColor, false, Colormap.LightColor.a);
DrawSubsectors(false);
break;
case GLPASS_BASE_MASKED:
case GLPASS_PLAIN: // Single-pass rendering
gl_SetColor(lightlevel, extralight*gl_weaponlight, &Colormap,1.0f);
if (!foggy || pass == GLPASS_PLAIN) gl_SetFog(lightlevel, Colormap.FadeColor, STYLE_Normal, Colormap.LightColor.a);
if (!foggy || pass == GLPASS_PLAIN) gl_SetFog(lightlevel, Colormap.FadeColor, false, Colormap.LightColor.a);
// fall through
case GLPASS_TEXTURE:
gltexture->Bind(Colormap.LightColor.a);
@ -270,15 +270,15 @@ void GLFlat::Draw(int pass)
break;
case GLPASS_FOG:
gl_SetFog(lightlevel, Colormap.FadeColor, STYLE_Normal, Colormap.LightColor.a);
gl_SetFog(lightlevel, Colormap.FadeColor, false, Colormap.LightColor.a);
DrawSubsectors(false);
break;
case GLPASS_LIGHT:
case GLPASS_LIGHT_ADDITIVE:
if (!foggy) gl_SetFog((255+lightlevel)>>1, Colormap.FadeColor, STYLE_Normal, Colormap.LightColor.a);
else gl_SetFog(lightlevel, Colormap.FadeColor, STYLE_Add, Colormap.LightColor.a);
if (!foggy) gl_SetFog((255+lightlevel)>>1, Colormap.FadeColor, false, Colormap.LightColor.a);
else gl_SetFog(lightlevel, Colormap.FadeColor, true, Colormap.LightColor.a);
if (sub)
{
@ -316,7 +316,7 @@ void GLFlat::Draw(int pass)
case GLPASS_TRANSLUCENT:
if (renderstyle==STYLE_Add) gl.BlendFunc(GL_SRC_ALPHA, GL_ONE);
gl_SetColor(lightlevel, extralight*gl_weaponlight, &Colormap, alpha);
gl_SetFog(lightlevel, Colormap.FadeColor, STYLE_Normal, Colormap.LightColor.a);
gl_SetFog(lightlevel, Colormap.FadeColor, false, Colormap.LightColor.a);
gl.AlphaFunc(GL_GEQUAL,0.5f*(alpha));
if (!gltexture) gl_EnableTexture(false);
@ -355,10 +355,10 @@ inline void GLFlat::PutFlat()
}
if ((gl.flags&RFL_NOSTENCIL) && !(renderflags&SSRF_RENDER3DPLANES))
{
renderstyle=STYLE_Normal;
renderstyle=STYLE_Translucent;
alpha=1.f;
}
if (renderstyle!=STYLE_Normal)
if (renderstyle!=STYLE_Translucent || alpha < 1.f - FLT_EPSILON)
{
int list = (renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER;
gl_drawinfo->drawlists[list].AddFlat (this);
@ -503,7 +503,7 @@ void GLFlat::ProcessSector(sector_t * frontsector, subsector_t * sub)
Colormap.CopyLightColor(*light->p_extra_colormap);
}
renderstyle = (alpha<1.0f-FLT_EPSILON) ? STYLE_Translucent : STYLE_Normal;
renderstyle = STYLE_Translucent;
if (alpha!=0.0f) Process(frontsector, false, false);
}
}
@ -541,7 +541,7 @@ void GLFlat::ProcessSector(sector_t * frontsector, subsector_t * sub)
if(!(sector->CeilingFlags&SECF_ABSLIGHTING)) lightlevel = *light->p_lightlevel;
Colormap.CopyLightColor(*light->p_extra_colormap);
}
renderstyle = (alpha<1.0f-FLT_EPSILON)? STYLE_Translucent : STYLE_Normal;
renderstyle = STYLE_Translucent;
if (alpha!=0.0f) Process(frontsector, true, false);
}
}
@ -599,8 +599,7 @@ void GLFlat::ProcessSector(sector_t * frontsector, subsector_t * sub)
Colormap.FadeColor=frontsector->ColorMap->Fade;
alpha=rover->alpha/255.0f;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add :
rover->alpha<255? STYLE_Translucent : STYLE_Normal;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent;
Process(rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG));
}
lastceilingheight=ff_top;
@ -622,8 +621,7 @@ void GLFlat::ProcessSector(sector_t * frontsector, subsector_t * sub)
Colormap.FadeColor=frontsector->ColorMap->Fade;
alpha=rover->alpha/255.0f;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add :
rover->alpha<255? STYLE_Translucent : STYLE_Normal;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent;
Process(rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG));
}
lastceilingheight=ff_bottom;
@ -665,8 +663,7 @@ void GLFlat::ProcessSector(sector_t * frontsector, subsector_t * sub)
}
alpha=rover->alpha/255.0f;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add :
rover->alpha<255? STYLE_Translucent : STYLE_Normal;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent;
Process(rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG));
}
lastfloorheight=ff_bottom;
@ -688,8 +685,7 @@ void GLFlat::ProcessSector(sector_t * frontsector, subsector_t * sub)
Colormap.FadeColor=frontsector->ColorMap->Fade;
alpha=rover->alpha/255.0f;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add :
rover->alpha<255? STYLE_Translucent : STYLE_Normal;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent;
Process(rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG));
}
lastfloorheight=ff_top;

View file

@ -452,8 +452,7 @@ void STACK_ARGS OpenGLFrameBuffer::DrawTextureV(FTexture *img, int x0, int y0, u
// just ignore for now...
if (parms.windowleft || parms.windowright != img->GetScaledWidth()) return;
if (parms.fillcolor > 0 || parms.style == STYLE_Shaded ||
parms.style == STYLE_TranslucentStencil || parms.style == STYLE_Stencil)
if (parms.fillcolor > 0 || (parms.style.Flags & STYLEF_ColorIsFixed))
{
r = RPART(parms.fillcolor)/255.0f;
g = GPART(parms.fillcolor)/255.0f;
@ -472,14 +471,8 @@ void STACK_ARGS OpenGLFrameBuffer::DrawTextureV(FTexture *img, int x0, int y0, u
int space = (GetTrueHeight()-GetHeight())/2;
gl.Scissor(parms.lclip, btm - parms.dclip + space, parms.rclip - parms.lclip, parms.dclip - parms.uclip);
if (parms.style == STYLE_TranslucentStencil || parms.style == STYLE_Stencil)
{
gl_SetTextureMode(TM_MASK);
}
else if (!parms.masked)
{
gl_SetTextureMode(TM_OPAQUE);
}
gl_SetRenderStyle(parms.style, !parms.masked, false);
gl.Color4f(r, g, b, FIXED2FLOAT(parms.alpha));
gl.Disable(GL_ALPHA_TEST);
@ -497,10 +490,9 @@ void STACK_ARGS OpenGLFrameBuffer::DrawTextureV(FTexture *img, int x0, int y0, u
gl.Scissor(0, 0, GetWidth(), GetHeight());
gl.Disable(GL_SCISSOR_TEST);
if (!parms.masked || parms.style == STYLE_TranslucentStencil || parms.style == STYLE_Stencil)
{
gl_SetTextureMode(TM_MODULATE);
}
gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.BlendEquation(GL_FUNC_ADD);
}
//==========================================================================

View file

@ -57,17 +57,27 @@ inline bool gl_isFullbright(PalEntry color, int lightlevel)
struct FColormap;
void gl_GetLightColor(int lightlevel, int rellight, const FColormap * cm, float * pred, float * pgreen, float * pblue, bool weapon=false);
void gl_SetColor(int light, int rellight, const FColormap * cm, float alpha, PalEntry ThingColor = 0xffffff, bool weapon=false);
void gl_SetColor(int light, int rellight, const FColormap * cm, float *red, float *green, float *blue, PalEntry ThingColor=0xffffff, bool weapon=false);
void gl_GetSpriteLight(fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, int desaturation, float * out);
void gl_SetSpriteLight(AActor * thing, int lightlevel, int rellight, FColormap * cm, float alpha, PalEntry ThingColor = 0xffffff, bool weapon=false);
void gl_GetSpriteLight(AActor * thing, int lightlevel, int rellight, FColormap * cm,
float *red, float *green, float *blue,
PalEntry ThingColor, bool weapon);
void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending);
void gl_SetSpriteLighting(FRenderStyle style, AActor *thing, int lightlevel, int rellight, FColormap *cm,
PalEntry ThingColor, bool fullbright, bool weapon);
struct particle_t;
void gl_SetSpriteLight(particle_t * thing, int lightlevel, int rellight, FColormap *cm, float alpha, PalEntry ThingColor = 0xffffff);
void gl_InitFog();
void gl_SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog);
float gl_GetFogDensity(int lightlevel, PalEntry fogcolor);
void gl_SetFog(int lightlevel, PalEntry pe, int renderstyle, int cm);
void gl_SetFog(int lightlevel, PalEntry pe, bool isadditive, int cm);
// textures + sprites

View file

@ -61,7 +61,7 @@ int MaxGlowingTexture; // TexMan.NumTextures can't be used because it may change
// Reads glow definitions from GLDEFS
//
//===========================================================================
void gl_InitGlow()
void gl_InitGlow(FScanner &sc)
{
if (GlowingTextures.Size()==0)
{
@ -72,17 +72,17 @@ void gl_InitGlow()
memset(&GlowingTextures[0], 0, sizeof(bool)*MaxGlowingTexture);
}
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
SC_MustGetString();
if (SC_Compare("FLATS"))
sc.MustGetString();
if (sc.Compare("FLATS"))
{
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
SC_MustGetString();
int flump=TexMan.CheckForTexture(sc_String, FTexture::TEX_Flat,FTextureManager::TEXMAN_TryAny);
sc.MustGetString();
int flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Flat,FTextureManager::TEXMAN_TryAny);
if (flump!=-1 && flump<MaxGlowingTexture)
{
GlowingTextures[flump]=true;
@ -90,13 +90,13 @@ void gl_InitGlow()
}
}
if (SC_Compare("WALLS"))
if (sc.Compare("WALLS"))
{
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
SC_MustGetString();
int flump=TexMan.CheckForTexture(sc_String, FTexture::TEX_Wall,FTextureManager::TEXMAN_TryAny);
sc.MustGetString();
int flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Wall,FTextureManager::TEXMAN_TryAny);
if (flump!=-1 && flump<MaxGlowingTexture)
{
GlowingTextures[flump]=true;

View file

@ -49,6 +49,8 @@
#include "gl/gl_portal.h"
EXTERN_CVAR (Float, transsouls)
CVAR (Bool, gl_lights_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
@ -245,6 +247,21 @@ void gl_GetLightColor(int lightlevel, int rellight, const FColormap * cm, float
else r=g=b=light/255.f;
}
//==========================================================================
//
// set current light color
//
//==========================================================================
void gl_SetColor(int light, int rellight, const FColormap * cm, float *red, float *green, float *blue, PalEntry ThingColor, bool weapon)
{
float r,g,b;
gl_GetLightColor(light, rellight, cm, &r, &g, &b, weapon);
*red = r * ThingColor.r/255.0f;
*green = g * ThingColor.g/255.0f;
*blue = b * ThingColor.b/255.0f;
}
//==========================================================================
//
// set current light color
@ -342,7 +359,7 @@ void gl_InitFog()
//
//==========================================================================
void gl_SetFog(int lightlevel, PalEntry fogcolor, int blendmode, int cm)
void gl_SetFog(int lightlevel, PalEntry fogcolor, bool isadditive, int cm)
{
float fogdensity;
@ -373,7 +390,7 @@ void gl_SetFog(int lightlevel, PalEntry fogcolor, int blendmode, int cm)
{
// For additive rendering using the regular fog color here would mean applying it twice
// so always use black
if (blendmode==STYLE_Add || blendmode==STYLE_Fuzzy)
if (isadditive)
{
fogcolor=0;
}
@ -555,6 +572,8 @@ void gl_GetSpriteLight(fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, in
}
}
static void gl_SetSpriteLight(fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec,
int lightlevel, int rellight, FColormap * cm, float alpha,
PalEntry ThingColor, bool weapon)
@ -568,7 +587,6 @@ static void gl_SetSpriteLight(fixed_t x, fixed_t y, fixed_t z, subsector_t * sub
r = clamp<float>(result[0]+r, 0, 1.0f) * ThingColor.r/255.f;
g = clamp<float>(result[1]+g, 0, 1.0f) * ThingColor.g/255.f;
b = clamp<float>(result[2]+b, 0, 1.0f) * ThingColor.b/255.f;
gl.Color4f(r, g, b, alpha);
}
@ -587,6 +605,107 @@ void gl_SetSpriteLight(particle_t * thing, int lightlevel, int rellight, FColorm
cm, alpha, ThingColor, false);
}
//==========================================================================
//
// Sets render state to draw the given render style
// includes: Texture mode, blend equation and blend mode
//
//==========================================================================
void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending)
{
static int blendstyles[] = { GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA };
static int renderops[] = { 0, GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, -1, -1, -1, -1};
int srcblend = blendstyles[style.SrcAlpha&3];
int dstblend = blendstyles[style.DestAlpha&3];
int blendequation = renderops[style.BlendOp&7];
int texturemode = drawopaque? TM_OPAQUE : TM_MODULATE;
if (style.Flags & STYLEF_ColorIsFixed)
{
texturemode = TM_MASK;
}
else if (style.Flags & STYLEF_InvertSource)
{
texturemode = drawopaque? TM_INVERTOPAQUE : TM_INVERT;
}
if (blendequation == -1)
{
srcblend = GL_DST_COLOR;
dstblend = GL_ONE_MINUS_SRC_ALPHA;
blendequation = GL_FUNC_ADD;
}
if (allowcolorblending && srcblend == GL_SRC_ALPHA && dstblend == GL_ONE && blendequation == GL_FUNC_ADD)
{
srcblend = GL_SRC_COLOR;
}
gl.BlendFunc(srcblend, dstblend);
gl.BlendEquation(blendequation);
gl_SetTextureMode(texturemode);
}
//==========================================================================
//
// Sets render state to draw the given render style
//
//==========================================================================
void gl_SetSpriteLighting(FRenderStyle style, AActor *thing, int lightlevel, int rellight, FColormap *cm,
PalEntry ThingColor, bool fullbright, bool weapon)
{
float alpha;
FColormap internal_cm;
if (style.Flags & STYLEF_RedIsAlpha)
{
cm->LightColor.a = CM_SHADE;
}
if (style.Flags & STYLEF_ColorIsFixed)
{
if (style.Flags & STYLEF_InvertSource)
{
ThingColor = PalEntry(thing->fillcolor).InverseColor();
}
else
{
ThingColor = thing->fillcolor;
}
gl_ModifyColor(ThingColor.r, ThingColor.g, ThingColor.b, cm->LightColor.a);
}
// This doesn't work like in the software renderer.
if (style.Flags & STYLEF_InvertSource)
{
internal_cm = *cm;
cm = &internal_cm;
int gray = (internal_cm.LightColor.r*77 + internal_cm.LightColor.r*143 + internal_cm.LightColor.r*36)>>8;
cm->LightColor.r = cm->LightColor.g = cm->LightColor.b = gray;
}
if (style.BlendOp == STYLEOP_Fuzz)
{
gl.Color4f(0.2f * ThingColor.r / 255.f, 0.2f * ThingColor.g / 255.f,
0.2f * ThingColor.b / 255.f, (alpha = 0.33f));
}
else
{
if (style.Flags & STYLEF_TransSoulsAlpha) alpha = transsouls;
else alpha = TO_MAP(thing->alpha);
if (gl_light_sprites && gl_lights && !fullbright)
{
gl_SetSpriteLight(thing, lightlevel, rellight, cm, alpha, ThingColor, weapon);
}
else
{
gl_SetColor(lightlevel, rellight, cm, alpha, ThingColor, weapon);
}
}
gl.AlphaFunc(GL_GEQUAL,alpha/2.f);
}
//==========================================================================
//
@ -602,3 +721,4 @@ CCMD(skyfog)
}

View file

@ -247,70 +247,70 @@ void gl_InitModels()
memset(&smf, 0, sizeof(smf));
while ((Lump = Wads.FindLump("MODELDEF", &lastLump)) != -1)
{
SC_OpenLumpNum(Lump, "MODELDEF");
while (SC_GetString())
FScanner sc(Lump, "MODELDEF");
while (sc.GetString())
{
if (SC_Compare("model"))
if (sc.Compare("model"))
{
SC_MustGetString();
sc.MustGetString();
memset(&smf, 0, sizeof(smf));
smf.xscale=smf.yscale=smf.zscale=1.f;
smf.type = PClass::FindClass(sc_String);
if (!smf.type) SC_ScriptError("MODELDEF: Unknown actor type '%s'\n", sc_String);
smf.type = PClass::FindClass(sc.String);
if (!smf.type) sc.ScriptError("MODELDEF: Unknown actor type '%s'\n", sc.String);
GetDefaultByType(smf.type)->hasmodel=true;
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
SC_MustGetString();
if (SC_Compare("path"))
sc.MustGetString();
if (sc.Compare("path"))
{
SC_MustGetString();
FixPathSeperator(sc_String);
path = sc_String;
sc.MustGetString();
FixPathSeperator(sc.String);
path = sc.String;
if (path[(int)path.Len()-1]!='/') path+='/';
}
else if (SC_Compare("model"))
else if (sc.Compare("model"))
{
SC_MustGetNumber();
index=sc_Number;
sc.MustGetNumber();
index=sc.Number;
if (index<0 || index>=MAX_MODELS_PER_FRAME)
{
SC_ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
}
SC_MustGetString();
FixPathSeperator(sc_String);
smf.models[index] = FindModel(path.GetChars(), sc_String);
sc.MustGetString();
FixPathSeperator(sc.String);
smf.models[index] = FindModel(path.GetChars(), sc.String);
if (!smf.models[index])
{
Printf("%s: model not found\n", sc_String);
Printf("%s: model not found\n", sc.String);
}
}
else if (SC_Compare("scale"))
else if (sc.Compare("scale"))
{
SC_MustGetFloat();
smf.xscale=sc_Float;
SC_MustGetFloat();
smf.yscale=sc_Float;
SC_MustGetFloat();
smf.zscale=sc_Float;
sc.MustGetFloat();
smf.xscale=sc.Float;
sc.MustGetFloat();
smf.yscale=sc.Float;
sc.MustGetFloat();
smf.zscale=sc.Float;
}
// [BB] Added zoffset reading.
else if (SC_Compare("zoffset"))
else if (sc.Compare("zoffset"))
{
SC_MustGetFloat();
smf.zoffset=sc_Float;
sc.MustGetFloat();
smf.zoffset=sc.Float;
}
// [BB] Added model flags reading.
else if (SC_Compare("ignoretranslation"))
else if (sc.Compare("ignoretranslation"))
{
smf.flags |= MDL_IGNORETRANSLATION;
}
else if (SC_Compare("pitchfrommomentum"))
else if (sc.Compare("pitchfrommomentum"))
{
smf.flags |= MDL_PITCHFROMMOMENTUM;
}
else if (SC_Compare("rotating"))
else if (sc.Compare("rotating"))
{
smf.flags |= MDL_ROTATING;
smf.xrotate = 0.;
@ -321,74 +321,74 @@ void gl_InitModels()
smf.rotationCenterZ = 0.;
smf.rotationSpeed = 1.;
}
else if (SC_Compare("rotation-speed"))
else if (sc.Compare("rotation-speed"))
{
SC_MustGetFloat();
smf.rotationSpeed = sc_Float;
sc.MustGetFloat();
smf.rotationSpeed = sc.Float;
}
else if (SC_Compare("rotation-vector"))
else if (sc.Compare("rotation-vector"))
{
SC_MustGetFloat();
smf.xrotate = sc_Float;
SC_MustGetFloat();
smf.yrotate = sc_Float;
SC_MustGetFloat();
smf.zrotate = sc_Float;
sc.MustGetFloat();
smf.xrotate = sc.Float;
sc.MustGetFloat();
smf.yrotate = sc.Float;
sc.MustGetFloat();
smf.zrotate = sc.Float;
}
else if (SC_Compare("rotation-center"))
else if (sc.Compare("rotation-center"))
{
SC_MustGetFloat();
smf.rotationCenterX = sc_Float;
SC_MustGetFloat();
smf.rotationCenterY = sc_Float;
SC_MustGetFloat();
smf.rotationCenterZ = sc_Float;
sc.MustGetFloat();
smf.rotationCenterX = sc.Float;
sc.MustGetFloat();
smf.rotationCenterY = sc.Float;
sc.MustGetFloat();
smf.rotationCenterZ = sc.Float;
}
else if (SC_Compare("interpolatedoubledframes"))
else if (sc.Compare("interpolatedoubledframes"))
{
smf.flags |= MDL_INTERPOLATEDOUBLEDFRAMES;
}
else if (SC_Compare("nointerpolation"))
else if (sc.Compare("nointerpolation"))
{
smf.flags |= MDL_NOINTERPOLATION;
}
else if (SC_Compare("skin"))
else if (sc.Compare("skin"))
{
SC_MustGetNumber();
index=sc_Number;
sc.MustGetNumber();
index=sc.Number;
if (index<0 || index>=MAX_MODELS_PER_FRAME)
{
SC_ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
}
SC_MustGetString();
FixPathSeperator(sc_String);
if (SC_Compare(""))
sc.MustGetString();
FixPathSeperator(sc.String);
if (sc.Compare(""))
{
smf.skins[index]=NULL;
}
else
{
smf.skins[index]=LoadSkin(path.GetChars(), sc_String);
smf.skins[index]=LoadSkin(path.GetChars(), sc.String);
if (smf.skins[index] == NULL)
{
Printf("Skin '%s' not found in '%s'\n",
sc_String, smf.type->TypeName.GetChars());
sc.String, smf.type->TypeName.GetChars());
}
}
}
else if (SC_Compare("frameindex") || SC_Compare("frame"))
else if (sc.Compare("frameindex") || sc.Compare("frame"))
{
bool isframe=!!SC_Compare("frame");
bool isframe=!!sc.Compare("frame");
SC_MustGetString();
sc.MustGetString();
smf.sprite = -1;
for (i = 0; i < (int)sprites.Size (); ++i)
{
if (strncmp (sprites[i].name, sc_String, 4) == 0)
if (strncmp (sprites[i].name, sc.String, 4) == 0)
{
if (sprites[i].numframes==0)
{
//SC_ScriptError("Sprite %s has no frames", sc_String);
//sc.ScriptError("Sprite %s has no frames", sc.String);
}
smf.sprite = i;
break;
@ -396,32 +396,32 @@ void gl_InitModels()
}
if (smf.sprite==-1)
{
SC_ScriptError("Unknown sprite %s in model definition for %s", sc_String, smf.type->TypeName.GetChars());
sc.ScriptError("Unknown sprite %s in model definition for %s", sc.String, smf.type->TypeName.GetChars());
}
SC_MustGetString();
FString framechars = sc_String;
sc.MustGetString();
FString framechars = sc.String;
SC_MustGetNumber();
index=sc_Number;
sc.MustGetNumber();
index=sc.Number;
if (index<0 || index>=MAX_MODELS_PER_FRAME)
{
SC_ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars());
}
if (isframe)
{
SC_MustGetString();
sc.MustGetString();
if (smf.models[index]!=NULL)
{
smf.modelframes[index] = smf.models[index]->FindFrame(sc_String);
if (smf.modelframes[index]==-1) SC_ScriptError("Unknown frame '%s' in %s", sc_String, smf.type->TypeName.GetChars());
smf.modelframes[index] = smf.models[index]->FindFrame(sc.String);
if (smf.modelframes[index]==-1) sc.ScriptError("Unknown frame '%s' in %s", sc.String, smf.type->TypeName.GetChars());
}
else smf.modelframes[index] = -1;
}
else
{
SC_MustGetNumber();
smf.modelframes[index] = sc_Number;
sc.MustGetNumber();
smf.modelframes[index] = sc.Number;
}
for(i=0; framechars[i]>0; i++)
@ -431,7 +431,7 @@ void gl_InitModels()
if (c<0 || c>=29)
{
SC_ScriptError("Invalid frame character %c found", c+'A');
sc.ScriptError("Invalid frame character %c found", c+'A');
}
if (map[c]) continue;
smf.frame=c;
@ -501,14 +501,9 @@ void gl_RenderModel(GLSprite * spr, int cm)
gl.MatrixMode(GL_MODELVIEW);
gl.PushMatrix();
gl.DepthFunc(GL_LEQUAL);
// [BB] In case the model should be rendered translucent, do back face culling.
// This solves a few of the problems caused by the lack of depth sorting.
// TO-DO: Implement proper depth sorting.
if ( spr->actor->RenderStyle!=STYLE_Normal )
{
gl.Enable(GL_CULL_FACE);
glFrontFace(GL_CW);
}
// Model space => World space
gl.Translatef(spr->x, spr->z, spr->y );
@ -617,6 +612,5 @@ void gl_RenderModel(GLSprite * spr, int cm)
gl.MatrixMode(GL_MODELVIEW);
gl.PopMatrix();
gl.DepthFunc(GL_LESS);
if ( spr->actor->RenderStyle!=STYLE_Normal )
gl.Disable(GL_CULL_FACE);
}

View file

@ -55,6 +55,14 @@ int GetCeilingLight (const sector_t *sec);
struct GLDrawList;
class ADynamicLight;
enum HWRenderStyle
{
STYLEHW_Normal, // default
STYLEHW_Solid, // drawn solid (needs special treatment for sprites)
STYLEHW_NoAlphaTest, // disable alpha test
};
//==========================================================================
//
// One sector plane, still in fixed point
@ -137,11 +145,12 @@ public:
FColormap Colormap;
ERenderStyle RenderStyle;
fixed_t viewdistance;
byte lightlevel;
byte type;
byte RenderStyle;
byte flags;
short rellight;
@ -266,6 +275,7 @@ public:
FGLTexture *gltexture;
FColormap Colormap; // light and fog
ERenderStyle renderstyle;
float alpha;
GLSectorPlane plane;
@ -274,7 +284,6 @@ public:
bool foggy;
bool ceiling;
byte renderflags;
byte renderstyle;
void DrawSubsector(subsector_t * sub);
void DrawSubsectorLights(subsector_t * sub, int pass);
@ -304,12 +313,13 @@ public:
friend struct GLDrawList;
friend void Mod_RenderModel(GLSprite * spr, model_t * mdl, int framenumber);
unsigned char RenderStyle;
byte lightlevel;
byte foglevel;
byte hw_styleflags;
PalEntry ThingColor; // thing's own color
FColormap Colormap;
FSpriteModelFrame * modelframe;
FRenderStyle RenderStyle;
int translation;
int index;

View file

@ -116,28 +116,28 @@ public:
//
//-----------------------------------------------------------------------------
void gl_ParseSkybox()
void gl_ParseSkybox(FScanner &sc)
{
int facecount=0;
SC_MustGetString();
sc.MustGetString();
FSkyBox * sb = new FSkyBox;
uppercopy(sb->Name, sc_String);
uppercopy(sb->Name, sc.String);
sb->Name[8]=0;
SC_MustGetStringName("{");
while (!SC_CheckString("}"))
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
SC_MustGetString();
sc.MustGetString();
if (facecount<6)
{
sb->faces[facecount] = TexMan[TexMan.GetTexture(sc_String, FTexture::TEX_Wall, FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_Overridable)];
sb->faces[facecount] = TexMan[TexMan.GetTexture(sc.String, FTexture::TEX_Wall, FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_Overridable)];
}
facecount++;
}
if (facecount != 3 && facecount != 6)
{
SC_ScriptError("%s: Skybox definition requires either 3 or 6 faces", sb->Name);
sc.ScriptError("%s: Skybox definition requires either 3 or 6 faces", sb->Name);
}
sb->SetSize();
TexMan.AddTexture(sb);

View file

@ -71,7 +71,7 @@ int glpart=-2;
//==========================================================================
void GLSprite::Draw(int pass)
{
bool alphatestdisabled = false;
bool additivefog = false;
if (pass!=GLPASS_PLAIN && pass!=GLPASS_TRANSLUCENT) return;
@ -79,7 +79,7 @@ void GLSprite::Draw(int pass)
{
// The translucent pass requires special setup for the various modes.
if (!gl_sprite_blend && RenderStyle != STYLE_Normal && actor && !(actor->momx|actor->momy))
if (!gl_sprite_blend && hw_styleflags != STYLEHW_Solid && actor && !(actor->momx|actor->momy))
{
// Draw translucent non-moving sprites with a slightly altered z-offset to avoid z-fighting
// when in the same position as a regular sprite.
@ -89,16 +89,25 @@ void GLSprite::Draw(int pass)
gl.PolygonOffset(-1.0f, -64.0f);
}
if (!gl_isBlack(Colormap.FadeColor)) gl_EnableBrightmap(false);
switch(RenderStyle)
// Brightmaps will only be used when doing regular drawing ops and having no fog
if (!gl_isBlack(Colormap.FadeColor) || RenderStyle.BlendOp != STYLEOP_Add)
{
case STYLE_Fuzzy:
gl_EnableBrightmap(false);
}
gl_SetRenderStyle(RenderStyle, false,
// The rest of the needed checks are done inside gl_SetRenderStyle
trans > 1.f - FLT_EPSILON && gl_usecolorblending && actor && (actor->renderflags & RF_FULLBRIGHT));
if (hw_styleflags == STYLEHW_NoAlphaTest)
{
gl.Disable(GL_ALPHA_TEST);
}
if (RenderStyle.BlendOp == STYLEOP_Fuzz)
{
float fuzzalpha=0.44f;
float minalpha=0.1f;
gl.BlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
gl_EnableBrightmap(false);
// fog + fuzz don't work well without some fiddling with the alpha value!
if (!gl_isBlack(Colormap.FadeColor))
@ -118,55 +127,49 @@ void GLSprite::Draw(int pass)
gl.AlphaFunc(GL_GEQUAL,minalpha);
gl.Color4f(0.2f,0.2f,0.2f,fuzzalpha);
break;
additivefog = true;
}
case STYLE_Add:
if (trans<1.0-FLT_EPSILON || !gl_usecolorblending || !(actor->renderflags & RF_FULLBRIGHT))
else if (RenderStyle.BlendOp == STYLEOP_Add && RenderStyle.DestAlpha == STYLEALPHA_One)
{
gl.BlendFunc(GL_SRC_ALPHA,GL_ONE);
gl.AlphaFunc(GL_GEQUAL,0.5f*trans);
break;
}
gl.BlendFunc(GL_SRC_COLOR, GL_ONE);
break;
case STYLE_Normal:
trans=1.0f;
case STYLE_Translucent:
gl.AlphaFunc(GL_GEQUAL,0.5f*trans);
break;
additivefog = true;
}
}
if (pass == GLPASS_TRANSLUCENT && ((gltexture && gltexture->GetTransparent()) || RenderStyle == STYLE_Transparent))
if (RenderStyle.BlendOp!=STYLEOP_Fuzz)
{
gl.Disable(GL_ALPHA_TEST);
alphatestdisabled = true;
if (actor)
{
gl_SetSpriteLighting(RenderStyle, actor, lightlevel, extralight*gl_weaponlight, &Colormap, ThingColor,
!!(actor->renderflags & RF_FULLBRIGHT), false);
}
if (RenderStyle!=STYLE_Fuzzy)
else if (particle)
{
bool res=false;
if (gl_lights && !gl_fixedcolormap)
{
if (actor && gl_light_sprites && !(actor->renderflags & RF_FULLBRIGHT))
{
gl_SetSpriteLight(actor, lightlevel, extralight*gl_weaponlight, &Colormap, trans, ThingColor);
res=true;
}
else if (particle && gl_light_particles)
if (gl_light_particles)
{
gl_SetSpriteLight(particle, lightlevel, extralight*gl_weaponlight, &Colormap, trans, ThingColor);
res=true;
}
else
{
gl_SetColor(lightlevel, extralight*gl_weaponlight, &Colormap, trans, ThingColor);
}
}
if (!res) gl_SetColor(lightlevel, extralight*gl_weaponlight, &Colormap, trans, ThingColor);
else return;
}
if (gl_isBlack(Colormap.FadeColor)) foglevel=lightlevel;
gl_SetFog(foglevel, Colormap.FadeColor, RenderStyle, Colormap.LightColor.a);
if (RenderStyle.Flags & STYLEF_FadeToBlack)
{
Colormap.FadeColor=0;
additivefog = true;
}
if (RenderStyle.Flags & STYLEF_InvertOverlay)
{
Colormap.FadeColor = Colormap.FadeColor.InverseColor();
additivefog=false;
}
gl_SetFog(foglevel, Colormap.FadeColor, additivefog, Colormap.LightColor.a);
if (gltexture) gltexture->BindPatch(Colormap.LightColor.a,translation);
else if (!modelframe) gl_EnableTexture(false);
@ -225,19 +228,17 @@ void GLSprite::Draw(int pass)
if (pass==GLPASS_TRANSLUCENT)
{
gl_EnableBrightmap(true);
// For translucent objects restore the default blending mode here!
if (RenderStyle == STYLE_Add || RenderStyle == STYLE_Fuzzy)
{
gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
gl.BlendEquation(GL_FUNC_ADD);
gl_SetTextureMode(TM_MODULATE);
// [BB] Restore the alpha test after drawing a smooth particle.
if (alphatestdisabled)
if (hw_styleflags == STYLEHW_NoAlphaTest)
{
gl.Enable(GL_ALPHA_TEST);
}
if (!gl_sprite_blend && RenderStyle != STYLE_Normal && actor && !(actor->momx|actor->momy))
if (!gl_sprite_blend && hw_styleflags != STYLEHW_Solid && actor && !(actor->momx|actor->momy))
{
gl.Disable(GL_POLYGON_OFFSET_FILL);
gl.PolygonOffset(0, 0);
@ -346,8 +347,8 @@ void GLSprite::Process(AActor* thing,sector_t * sector)
if (thing==viewactor) return;
// invisible things
if (thing->renderflags&RF_INVISIBLE || thing->RenderStyle==STYLE_None) return;
if (thing->sprite==0) return;
if (thing->renderflags & RF_INVISIBLE || !thing->RenderStyle.IsVisible(thing->alpha)) return;
// [RH] Interpolate the sprite's position to make it look smooth
fixed_t thingx = thing->PrevX + FixedMul (r_TicFrac, thing->x - thing->PrevX);
@ -355,7 +356,7 @@ void GLSprite::Process(AActor* thing,sector_t * sector)
fixed_t thingz = thing->PrevZ + FixedMul (r_TicFrac, thing->z - thing->PrevZ);
// too close to the camera. This doesn't look good if it is a sprite.
if (P_AproxDistance(thingx-viewx, thingy-viewy)<2*FRACUNIT)
if (P_AproxDistance(thingx-viewx, thingy-viewy)<2*FRACUNIT && abs(thingz - viewz) < 30*FRACUNIT)
{
if (!gl_FindModelFrame(RUNTIME_TYPE(thing), thing->sprite, thing->frame /*, thing->state*/))
{
@ -546,47 +547,53 @@ void GLSprite::Process(AActor* thing,sector_t * sector)
extern TArray<PalEntry> BloodTranslationColors;
ThingColor = BloodTranslationColors[GetTranslationIndex(translation)];
gl_ModifyColor(ThingColor.r, ThingColor.g, ThingColor.b, Colormap.LightColor.a);
ThingColor.a=0;
translation = TRANSLATION(TRANSLATION_Standard, 8);
}
else ThingColor=0xffffff;
RenderStyle=thing->RenderStyle;
trans=TO_MAP(thing->alpha);
RenderStyle = thing->RenderStyle;
RenderStyle.CheckFuzz();
trans = TO_MAP(thing->alpha);
hw_styleflags = STYLEHW_Normal;
if (RenderStyle == STYLE_OptFuzzy)
if (RenderStyle.Flags & STYLEF_TransSoulsAlpha)
{
RenderStyle = STYLE_Fuzzy; // This option is useless in OpenGL The fuzz effect looks much better here!
}
else if (RenderStyle == STYLE_SoulTrans)
{
RenderStyle = STYLE_Translucent;
trans = transsouls;
}
else if (RenderStyle == STYLE_Normal)
else if (RenderStyle.Flags & STYLEF_Alpha1)
{
trans = 1.f;
}
if (trans >= 1.f-FLT_EPSILON && (
(RenderStyle.SrcAlpha == STYLEALPHA_One && RenderStyle.DestAlpha == STYLEALPHA_Zero) ||
(RenderStyle.SrcAlpha == STYLEALPHA_Src && RenderStyle.DestAlpha == STYLEALPHA_InvSrc)
))
{
// This is a non-translucent sprite (i.e. STYLE_Normal or equivalent)
trans=1.f;
if (gltexture && gltexture->GetTransparent())
{
RenderStyle = STYLE_Transparent;
hw_styleflags = STYLEHW_NoAlphaTest;
}
else hw_styleflags = STYLEHW_Solid;
}
if (enhancedvision && gl_enhanced_lightamp)
{
if (RenderStyle==STYLE_Fuzzy)
if (RenderStyle.BlendOp == STYLEOP_Fuzz)
{
// enhanced vision makes them more visible!
trans=0.5f;
RenderStyle=STYLE_Translucent;
RenderStyle = STYLE_Translucent;
}
else if (thing->flags&MF_STEALTH)
else if (thing->flags & MF_STEALTH)
{
// enhanced vision overcomes stealth!
if (trans<0.5f) trans=0.5f;
if (trans < 0.5f) trans = 0.5f;
}
}
@ -598,11 +605,6 @@ void GLSprite::Process(AActor* thing,sector_t * sector)
index = gl_spriteindex++;
particle=NULL;
if (RenderStyle==STYLE_Translucent && trans>=1.0f-FLT_EPSILON)
{
RenderStyle=STYLE_Normal;
}
const bool drawWithXYBillboard = ( !(actor->renderflags & RF_FORCEYBILLBOARD)
&& players[consoleplayer].camera
&& (gl_billboard_mode == 2 || actor->renderflags & RF_FORCEXYBILLBOARD ) );
@ -610,16 +612,16 @@ void GLSprite::Process(AActor* thing,sector_t * sector)
if (thing->Sector->e->lightlist.Size()==0 || gl_fixedcolormap || fullbright || (drawWithXYBillboard && !modelframe))
{
PutSprite(RenderStyle!=STYLE_Normal);
PutSprite(hw_styleflags != STYLEHW_Solid);
}
else if (modelframe)
{
// FIXME: Get the appropriate light color here!
PutSprite(RenderStyle!=STYLE_Normal);
PutSprite(hw_styleflags != STYLEHW_Solid);
}
else
{
SplitSprite(thing->Sector,RenderStyle!=STYLE_Normal);
SplitSprite(thing->Sector, hw_styleflags != STYLEHW_Solid);
}
rendered_sprites++;
}
@ -644,6 +646,7 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s
if (particle->trans==0) return;
lightlevel = sector->lightlevel;
if (gl_fixedcolormap)
{
Colormap.GetFixedColormap();
@ -669,9 +672,9 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s
}
trans=particle->trans/255.0f;
RenderStyle = STYLE_Add;
ThingColor = GPalette.BaseColors[particle->color];
gl_ModifyColor(ThingColor.r, ThingColor.g, ThingColor.b, Colormap.LightColor.a);
ThingColor.a=0;
modelframe=NULL;
@ -731,15 +734,14 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s
this->particle=particle;
// [BB] Smooth particles have to be rendered without the alpha test.
if (gl_particles_style == 2)
RenderStyle=STYLE_Transparent;
if (gl_particles_style == 2) hw_styleflags = STYLEHW_NoAlphaTest;
else
{
if (trans>=1.0f-FLT_EPSILON) RenderStyle=STYLE_Normal;
else RenderStyle=STYLE_Translucent;
if (trans>=1.0f-FLT_EPSILON) hw_styleflags = STYLEHW_Solid;
else hw_styleflags = STYLEHW_Normal;
}
PutSprite(RenderStyle==STYLE_Translucent || RenderStyle==STYLE_Transparent);
PutSprite(hw_styleflags != STYLEHW_Solid);
rendered_sprites++;
}

View file

@ -251,6 +251,17 @@ void iCopyColors(unsigned char * pout, const unsigned char * pin, int cm, int co
}
break;
case CM_SHADE:
// Alpha shade uses the red channel for true color pics
for(i=0;i<count;i++)
{
pout[0] = pout[1] = pout[2] = 255;
pout[3] = T::R(pin);
pout+=4;
pin+=step;
}
break;
default:
if (cm<=CM_DESAT31)
{
@ -1311,7 +1322,10 @@ const WorldTextureInfo * FGLTexture::Bind(int texunit, int cm, int clampmode, in
if (gl.flags & RFL_GLSL)
{
if (createWarped && gl_warp_shader && tex->bWarped!=0)
{
Clean(true);
GetWorldTextureInfo();
}
if ((gl_warp_shader && tex->bWarped!=0) ||
(usebright) ||
@ -1344,7 +1358,8 @@ const WorldTextureInfo * FGLTexture::Bind(int texunit, int cm, int clampmode, in
}
// Bind it to the system.
if (!gltexture->Bind(texunit, cm, translation))
// clamping in x-direction may cause problems when rendering segs
if (!gltexture->Bind(texunit, cm, translation, gl_render_precise? clampmode&GLT_CLAMPY : clampmode))
{
int w,h;
@ -1360,8 +1375,6 @@ const WorldTextureInfo * FGLTexture::Bind(int texunit, int cm, int clampmode, in
}
delete buffer;
}
// clamping in x-direction may cause problems when rendering segs
gltexture->SetTextureClamp(gl_render_precise? clampmode&GLT_CLAMPY : clampmode);
if (tex->bHasCanvas) static_cast<FCanvasTexture*>(tex)->NeedUpdate();
return (WorldTextureInfo*)this;
@ -1412,7 +1425,10 @@ const PatchTextureInfo * FGLTexture::BindPatch(int texunit, int cm, int translat
if (gl.flags & RFL_GLSL)
{
if (createWarped && gl_warp_shader && tex->bWarped!=0)
{
Clean(true);
GetPatchTextureInfo();
}
if ((gl_warp_shader && tex->bWarped!=0) ||
(usebright) ||
@ -1446,7 +1462,7 @@ const PatchTextureInfo * FGLTexture::BindPatch(int texunit, int cm, int translat
// Bind it to the system. For multitexturing this
// should be the only thing that needs adjusting
if (!glpatch->Bind(texunit, cm, translation))
if (!glpatch->Bind(texunit, cm, translation, -1))
{
int w, h;
@ -1540,7 +1556,7 @@ FGLTexture * FGLTexture::ValidateTexture(int no, bool translate)
//
//==========================================================================
void gl_ParseBrightmap(int deflump)
void gl_ParseBrightmap(FScanner &sc, int deflump)
{
int type = FTexture::TEX_Any;
bool disable_fullbright=false;
@ -1549,57 +1565,57 @@ void gl_ParseBrightmap(int deflump)
int maplump = -1;
FString maplumpname;
SC_MustGetString();
if (SC_Compare("texture")) type = FTexture::TEX_Wall;
else if (SC_Compare("flat")) type = FTexture::TEX_Flat;
else if (SC_Compare("sprite")) type = FTexture::TEX_Sprite;
else SC_UnGet();
sc.MustGetString();
if (sc.Compare("texture")) type = FTexture::TEX_Wall;
else if (sc.Compare("flat")) type = FTexture::TEX_Flat;
else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite;
else sc.UnGet();
SC_MustGetString();
int no = TexMan.CheckForTexture(sc_String, type);
sc.MustGetString();
int no = TexMan.CheckForTexture(sc.String, type);
FTexture *tex = TexMan[no];
SC_MustGetToken('{');
while (!SC_CheckToken('}'))
sc.MustGetToken('{');
while (!sc.CheckToken('}'))
{
SC_MustGetString();
if (SC_Compare("disablefullbright"))
sc.MustGetString();
if (sc.Compare("disablefullbright"))
{
// This can also be used without a brightness map to disable
// fullbright in rotations that only use brightness maps on
// other angles.
disable_fullbright = true;
}
else if (SC_Compare("thiswad"))
else if (sc.Compare("thiswad"))
{
// only affects textures defined in the WAD containing the definition file.
thiswad = true;
}
else if (SC_Compare ("iwad"))
else if (sc.Compare ("iwad"))
{
// only affects textures defined in the IWAD.
iwad = true;
}
else if (SC_Compare ("map"))
else if (sc.Compare ("map"))
{
SC_MustGetString();
sc.MustGetString();
if (maplump >= 0)
{
Printf("Multiple brightmap definitions in texture %s\n", tex? tex->Name : "(null)");
}
maplump = Wads.CheckNumForFullName(sc_String);
maplump = Wads.CheckNumForFullName(sc.String);
// Try a normal WAD name lookup only if it's a proper name without path separator and
// not longer than 8 characters.
if (maplump==-1 && strlen(sc_String) <= 8 && !strchr(sc_String, '/'))
maplump = Wads.CheckNumForName(sc_String);
if (maplump==-1 && strlen(sc.String) <= 8 && !strchr(sc.String, '/'))
maplump = Wads.CheckNumForName(sc.String);
if (maplump==-1)
Printf("Brightmap '%s' not found in texture '%s'\n", sc_String, tex? tex->Name : "(null)");
Printf("Brightmap '%s' not found in texture '%s'\n", sc.String, tex? tex->Name : "(null)");
maplumpname = sc_String;
maplumpname = sc.String;
}
}
if (!tex)

View file

@ -879,18 +879,9 @@ void GLWall::DoMidTexture(seg_t * seg, bool drawfogboundary,
switch (seg->sidedef->Flags& WALLF_ADDTRANS)//TRANSBITS)
{
case 0:
if (seg->linedef->alpha<255)
{
RenderStyle=STYLE_Translucent;
alpha=(float)seg->linedef->alpha/255.0f;
translucent=true;
}
else if (seg->linedef->alpha==255)
{
RenderStyle=STYLE_Normal;
alpha=1.0f;
translucent=false;
}
translucent = seg->linedef->alpha<255 || (gltexture && gltexture->GetTransparent());
break;
case WALLF_ADDTRANS:
@ -899,11 +890,6 @@ void GLWall::DoMidTexture(seg_t * seg, bool drawfogboundary,
translucent=true;
break;
}
if (gltexture && gltexture->GetTransparent())
{
if (RenderStyle == STYLE_Normal) RenderStyle = STYLE_Translucent;
translucent = true;
}
//
//
@ -978,7 +964,6 @@ void GLWall::DoMidTexture(seg_t * seg, bool drawfogboundary,
else SplitWall(realfront, translucent);
}
alpha=1.0f;
RenderStyle=STYLE_Normal;
}
// restore some values that have been altered in this function!
glseg=glsave;

View file

@ -40,6 +40,7 @@
#include "p_local.h"
#include "p_lnspec.h"
#include "a_sharedglobal.h"
#include "r_blend.h"
#include "gl/gl_struct.h"
#include "gl/gl_renderstruct.h"
#include "gl/gl_portal.h"
@ -229,7 +230,7 @@ void GLWall::RenderMirrorSurface()
gl.BlendFunc(GL_SRC_ALPHA,GL_ONE);
gl.AlphaFunc(GL_GREATER,0);
gl.DepthFunc(GL_LEQUAL);
gl_SetFog(lightlevel, Colormap.FadeColor, STYLE_Add, Colormap.LightColor.a);
gl_SetFog(lightlevel, Colormap.FadeColor, true, Colormap.LightColor.a);
FGLTexture * pat=FGLTexture::ValidateTexture(lump);
pat->BindPatch(Colormap.LightColor.a, 0);
@ -270,11 +271,15 @@ void GLWall::RenderTranslucentWall()
{
bool transparent = gltexture? gltexture->GetTransparent() : false;
// currently the only modes possible are solid, additive or translucent
// and until that changes I won't fix this code for the new blending modes!
bool isadditive = RenderStyle == STYLE_Add;
if (!transparent) gl.AlphaFunc(GL_GEQUAL,0.5f*fabs(alpha));
else gl.Disable(GL_ALPHA_TEST);
if (RenderStyle==STYLE_Add) gl.BlendFunc(GL_SRC_ALPHA,GL_ONE);
if (isadditive) gl.BlendFunc(GL_SRC_ALPHA,GL_ONE);
if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, Colormap.FadeColor, RenderStyle, Colormap.LightColor.a);
if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, Colormap.FadeColor, isadditive, Colormap.LightColor.a);
else gl_SetFog(255, 0, STYLE_Normal, CM_DEFAULT);
if (gltexture)
@ -293,7 +298,7 @@ void GLWall::RenderTranslucentWall()
RenderWall(1,NULL);
// restore default settings
if (RenderStyle==STYLE_Add) gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (isadditive) gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (!transparent) gl.Enable(GL_ALPHA_TEST);
if (!gltexture)

View file

@ -144,7 +144,9 @@ void gl_DrawPlayerSprites(sector_t * viewsector)
player_t * player=playermo->player;
if(!player || playermo->renderflags&RF_INVISIBLE || !r_drawplayersprites ||
viewactor!=playermo || playermo->RenderStyle==STYLE_None) return;
viewactor!=playermo || playermo->RenderStyle.BlendOp == STYLEOP_None) return;
P_BobWeapon (player, &player->psprites[ps_weapon], &ofsx, &ofsy);
// check for fullbright
if (player->fixedcolormap==0)
@ -210,46 +212,21 @@ void gl_DrawPlayerSprites(sector_t * viewsector)
}
}
PalEntry ThingColor = playermo->fillcolor;
vissprite_t vis;
vis.RenderStyle=playermo->RenderStyle;
vis.alpha=playermo->alpha;
if (playermo->Inventory) playermo->Inventory->AlterWeaponSprite(&vis);
// Set light and blend mode
switch(vis.RenderStyle)
{
case STYLE_OptFuzzy:
case STYLE_Fuzzy:
gl.BlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
gl.AlphaFunc(GL_GEQUAL,0.1f);
gl.Color4f(0.2f,0.2f,0.2f,0.33f);
break;
/*
case STYLE_Subtract:
gl.BlendEquation(GL_FUNC_REVERSE_SUBTRACT);
*/
case STYLE_Add:
case STYLE_Translucent:
if (vis.RenderStyle == STYLE_Add) gl.BlendFunc(GL_SRC_ALPHA,GL_ONE);
gl.AlphaFunc(GL_GEQUAL, 0.5f * TO_MAP(abs(vis.alpha)));
gl_SetColor(lightlevel, 0, &cm, TO_MAP(vis.alpha), (PalEntry)0xffffff, true);
break;
case STYLE_Normal:
// Set the render parameters
vis.RenderStyle.CheckFuzz();
gl_SetRenderStyle(vis.RenderStyle, false, false);
if (gl_light_sprites && gl_lights && !fullbright)
{
gl_SetSpriteLight(playermo, lightlevel, 0, &cm, 1.0, (PalEntry)0xffffff, true);
}
else
{
gl_SetColor(lightlevel, 0, &cm, 1.0f, (PalEntry)0xffffff, true);
}
break;
// set the lighting parameters (only calls glColor and glAlphaFunc)
gl_SetSpriteLighting(vis.RenderStyle, playermo, lightlevel, 0, &cm, 0xffffff, fullbright, true);
}
P_BobWeapon (player, &player->psprites[ps_weapon], &ofsx, &ofsy);
// Weapons are not drawn with fog so we can skip that step here
// now draw the different layers of the weapon
gl_EnableBrightmap(true);
@ -257,19 +234,11 @@ void gl_DrawPlayerSprites(sector_t * viewsector)
if (psp->state) DrawPSprite (player,psp,psp->sx+ofsx, psp->sy+ofsy, cm.LightColor.a);
gl_EnableBrightmap(false);
// Restore default settings
switch(vis.RenderStyle)
{
case STYLE_Stencil:
case STYLE_TranslucentStencil:
case STYLE_Fuzzy:
case STYLE_Add:
case STYLE_Translucent:
gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.AlphaFunc(GL_GEQUAL,0.5f);
break;
}
gl.BlendEquation(GL_FUNC_ADD);
gl.Color3f(1.0f,1.0f,1.0f);
gl_SetTextureMode(TM_MODULATE);
// The Targeter's sprites are always drawn normally!
for (; i<NUMPSPRITES; i++,psp++)

View file

@ -73,6 +73,9 @@ CUSTOM_CVAR(Int, gl_texture_format, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINI
FGLTexture::FlushAll();
}
CVAR(Bool, gl_clamping_bug, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//===========================================================================
//
// Static texture data
@ -336,7 +339,7 @@ unsigned * GLTexture::GetTexID(int cm, int translation)
// the lastbound variable will have to be changed
//
//===========================================================================
unsigned int GLTexture::Bind(int texunit, int cm,int translation)
unsigned int GLTexture::Bind(int texunit, int cm,int translation, int clampmode)
{
unsigned int * pTexID=GetTexID(cm, translation);
@ -346,6 +349,7 @@ unsigned int GLTexture::Bind(int texunit, int cm,int translation)
lastbound[texunit]=*pTexID;
if (texunit != 0) gl.ActiveTexture(GL_TEXTURE0+texunit);
gl.BindTexture(GL_TEXTURE_2D, *pTexID);
if (clampmode != -1) SetTextureClamp(clampmode);
if (texunit != 0) gl.ActiveTexture(GL_TEXTURE0);
return *pTexID;
}
@ -383,11 +387,11 @@ unsigned int GLTexture::CreateTexture(unsigned char * buffer, int w, int h, bool
//===========================================================================
void GLTexture::SetTextureClamp(int newclampmode)
{
if ((clampmode&GLT_CLAMPX) != (newclampmode&GLT_CLAMPX))
if (gl_clamping_bug || (clampmode&GLT_CLAMPX) != (newclampmode&GLT_CLAMPX))
{
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, newclampmode&GLT_CLAMPX? GL_CLAMP_TO_EDGE : GL_REPEAT);
}
if ((clampmode&GLT_CLAMPY) != (newclampmode&GLT_CLAMPY))
if (gl_clamping_bug || (clampmode&GLT_CLAMPY) != (newclampmode&GLT_CLAMPY))
{
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, newclampmode&GLT_CLAMPY? GL_CLAMP_TO_EDGE : GL_REPEAT);
}

View file

@ -69,12 +69,13 @@ private:
void LoadImage(unsigned char * buffer,int w, int h, unsigned int & glTexID,int wrapparam, bool alphatexture, int texunit);
unsigned * GetTexID(int cm, int translation);
void SetTextureClamp(int clampmode);
public:
GLTexture(int w, int h, bool mip, bool wrap);
~GLTexture();
unsigned int Bind(int texunit, int cm, int translation=0);
unsigned int Bind(int texunit, int cm, int translation=0, int clampmode = -1);
unsigned int CreateTexture(unsigned char * buffer, int w, int h,bool wrap, int texunit, int cm, int translation=0);
void Resize(int _width, int _height) ;
@ -94,7 +95,6 @@ public:
float FixToTexU(int v) { return (float)v/(float)FRACUNIT/(float)texwidth; }
float FixToTexV(int v) { return (float)v/(float)FRACUNIT/(float)texheight; }
void SetTextureClamp(int clampmode);
};

View file

@ -356,6 +356,9 @@ static void APIENTRY LoadExtensions()
if (!gl->BlendEquation) gl->BlendEquation = glBlendEquationDummy;
if (CheckExtension("GL_ARB_texture_non_power_of_two")) gl->flags|=RFL_NPOT_TEXTURE;
if (CheckExtension("GL_NV_texture_env_combine4")) gl->flags|=RFL_TEX_ENV_COMBINE4_NV;
if (CheckExtension("GL_ATI_texture_env_combine3")) gl->flags|=RFL_TEX_ENV_COMBINE4_NV;
if (CheckExtension("GL_ARB_texture_non_power_of_two")) gl->flags|=RFL_NPOT_TEXTURE;
#ifndef unix
PFNWGLSWAPINTERVALEXTPROC vs = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
@ -883,6 +886,68 @@ static void APIENTRY SetTextureMode(int type)
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
}
else if (type == TM_INVERT)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
}
else if (type == TM_INVERTOPAQUE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
}
else if (type == TM_COLOROVERLAY)
{
// Bah! Why can't ATI and NVidia create a common extension for this? :(
if (gl->flags & RFL_TEX_ENV_COMBINE4_NV)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
}
else if (gl->flags & RFL_TEX_ENV_COMBINE3_ATI)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE_ADD_ATI);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
}
else
{
// not supported
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
}
else if (type == TM_BRIGHTMAP || type == TM_BRIGHTMAP_TEXTURED)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

View file

@ -9,15 +9,25 @@ enum RenderFlags
RFL_FRAGMENT_PROGRAM=4,
RFL_GLSL=8,
RFL_OCCLUSION_QUERY=16,
RFL_TEX_ENV_COMBINE4_NV=32,
RFL_TEX_ENV_COMBINE3_ATI=64,
};
enum TexMode
{
TM_MODULATE=0,
TM_MASK=1,
TM_OPAQUE=2,
TM_BRIGHTMAP=3,
TM_BRIGHTMAP_TEXTURED=4,
TMF_MASKBIT = 1,
TMF_OPAQUEBIT = 2,
TMF_INVERTBIT = 4,
TM_MODULATE = 0,
TM_MASK = TMF_MASKBIT,
TM_OPAQUE = TMF_OPAQUEBIT,
TM_INVERT = TMF_INVERTBIT,
//TM_INVERTMASK = TMF_MASKBIT | TMF_INVERTBIT
TM_INVERTOPAQUE = TMF_INVERTBIT | TMF_OPAQUEBIT,
TM_COLOROVERLAY=8,
TM_BRIGHTMAP=16,
TM_BRIGHTMAP_TEXTURED=24,
};
struct RenderContext

View file

@ -42,8 +42,9 @@
extern FStringTable GStrings;
// QuitDOOM messages
#define NUM_QUITMESSAGES 15
// QuitGame messages
#define NUM_QUITDOOMMESSAGES 15
#define NUM_QUITSTRIFEMESSAGES 8
extern const char *endmsg[];

View file

@ -180,7 +180,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
}
if (teamplay && deathmatch)
gamestate == GS_INTERMISSION ? y = SCREENHEIGHT / 3.5 : y = SCREENHEIGHT / 16;
gamestate == GS_INTERMISSION ? y = SCREENHEIGHT * 7 / 2 : y = SCREENHEIGHT / 16;
else
gamestate == GS_INTERMISSION ? y = SCREENHEIGHT / 4 : y = SCREENHEIGHT / 16;
@ -216,7 +216,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
sprintf (score, "%d", teams[i].score);
screen->SetFont (BigFont);
screen->DrawText (teams[i].GetTextColor (), scorexwidth, gamestate == GS_INTERMISSION ? y / 1.25 : y / 2, score,
screen->DrawText (teams[i].GetTextColor (), scorexwidth, gamestate == GS_INTERMISSION ? y * 4 / 5 : y / 2, score,
DTA_CleanNoMove, true, TAG_DONE);
scorexwidth += SCREENWIDTH / 8;
@ -238,7 +238,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
DTA_CleanNoMove, true, TAG_DONE);
x = (SCREENWIDTH >> 1) - (((maxwidth + 32 + 32 + 16) * CleanXfac) >> 1);
gamestate == GS_INTERMISSION ? y = SCREENHEIGHT / 3.5 : y = SCREENHEIGHT / 10;
gamestate == GS_INTERMISSION ? y = SCREENHEIGHT * 2 / 7 : y = SCREENHEIGHT / 10;
if (teamplay && deathmatch)
y += SCREENWIDTH / 32;
@ -337,7 +337,7 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int x, int y, int h
if (player->mo->ScoreIcon > 0)
{
screen->DrawTexture (TexMan[player->mo->ScoreIcon], SCREENWIDTH / 2.25, y,
screen->DrawTexture (TexMan[player->mo->ScoreIcon], SCREENWIDTH * 4 / 9, y,
DTA_CleanNoMove, true, TAG_DONE);
}
}

View file

@ -215,7 +215,7 @@ static void ApplyActorDefault (int defnum, const char *datastr, int dataint)
case ADEF_Flags4Clear: actor->flags4 &= ~dataint; break;
case ADEF_Flags5Clear: actor->flags5 &= ~dataint; break;
case ADEF_Alpha: actor->alpha = dataint; break;
case ADEF_RenderStyle: actor->RenderStyle = dataint; break;
case ADEF_RenderStyle: actor->RenderStyle = ERenderStyle(dataint); break;
case ADEF_RenderFlags: actor->renderflags = dataint; break;
case ADEF_Translation: actor->Translation = dataint; break;
case ADEF_MinMissileChance: actor->MinMissileChance = dataint; break;

View file

@ -126,7 +126,7 @@ static void M_Options (int choice);
static void M_EndGame (int choice);
static void M_ReadThis (int choice);
static void M_ReadThisMore (int choice);
static void M_QuitDOOM (int choice);
static void M_QuitGame (int choice);
static void M_GameFiles (int choice);
static void M_ClearSaveStuff ();
@ -170,7 +170,6 @@ static void M_StartMessage (const char *string, void(*routine)(int), bool input)
void M_PlayerSetup ();
static void M_PlayerSetupTicker ();
static void M_PlayerSetupDrawer ();
static void M_RenderPlayerBackdrop ();
static void M_EditPlayerName (int choice);
static void M_ChangePlayerTeam (int choice);
static void M_PlayerNameChanged (FSaveGameNode *dummy);
@ -275,7 +274,7 @@ static oldmenuitem_t MainMenu[]=
{1,0,'s',"M_SAVEG",M_SaveGame, CR_UNTRANSLATED},
{1,0,'o',"M_OPTION",M_Options, CR_UNTRANSLATED}, // [RH] Moved
{1,0,'r',"M_RDTHIS",M_ReadThis, CR_UNTRANSLATED}, // Another hickup with Special edition.
{1,0,'q',"M_QUITG",M_QuitDOOM, CR_UNTRANSLATED}
{1,0,'q',"M_QUITG",M_QuitGame, CR_UNTRANSLATED}
};
static oldmenu_t MainDef =
@ -296,7 +295,7 @@ static oldmenuitem_t HereticMainMenu[] =
{1,1,'o',"MNU_OPTIONS",M_Options, CR_UNTRANSLATED},
{1,1,'f',"MNU_GAMEFILES",M_GameFiles, CR_UNTRANSLATED},
{1,1,'i',"MNU_INFO",M_ReadThis, CR_UNTRANSLATED},
{1,1,'q',"MNU_QUITGAME",M_QuitDOOM, CR_UNTRANSLATED}
{1,1,'q',"MNU_QUITGAME",M_QuitGame, CR_UNTRANSLATED}
};
static oldmenu_t HereticMainDef =
@ -625,7 +624,7 @@ CCMD (menu_quit)
{ // F10
//M_StartControlPanel (true);
S_Sound (CHAN_VOICE, "menu/activate", 1, ATTN_NONE);
M_QuitDOOM(0);
M_QuitGame(0);
}
CCMD (menu_game)
@ -1466,10 +1465,9 @@ void M_DrawReadThis ()
{
tex = TexMan[gameinfo.info.infoPage[InfoType-1]];
// Did the mapper choose a custom help page via MAPINFO?
if((level.f1 != NULL) && (strcmp(level.f1, "") != 0)) {
if(TexMan.CheckForTexture(level.f1,0,0) == -1)
TexMan.AddPatch(level.f1); // Needs to be marked as a patch.
tex = TexMan[level.f1];
if((level.f1 != NULL) && (strlen(level.f1) > 0))
{
tex = TexMan.FindTexture(level.f1);
}
if (InfoType > 1)
@ -1942,7 +1940,7 @@ void M_FinishReadThis (int choice)
}
//
// M_QuitDOOM
// M_QuitGame
//
void M_QuitResponse(int ch)
@ -1960,16 +1958,16 @@ void M_QuitResponse(int ch)
ST_Endoom();
}
void M_QuitDOOM (int choice)
void M_QuitGame (int choice)
{
// We pick index 0 which is language sensitive,
// or one at random, between 1 and maximum number.
if (gameinfo.gametype == GAME_Doom)
if (gameinfo.gametype & (GAME_Doom|GAME_Strife))
{
int quitmsg = gametic % NUM_QUITMESSAGES;
if (quitmsg != 0)
int quitmsg = gametic % (gameinfo.gametype == GAME_Doom ? NUM_QUITDOOMMESSAGES : NUM_QUITSTRIFEMESSAGES - 1);
if (quitmsg != 0 || gameinfo.gametype == GAME_Strife)
{
EndString.Format("QUITMSG%d", quitmsg);
EndString.Format("QUITMSG%d", quitmsg + (gameinfo.gametype == GAME_Doom ? 0 : NUM_QUITDOOMMESSAGES + 1));
EndString.Format("%s\n\n%s", GStrings(EndString), GStrings("DOSY"));
}
else
@ -2119,7 +2117,7 @@ static void M_PlayerSetupDrawer ()
DTA_DestWidth, 72 * CleanXfac,
DTA_DestHeight, 80 * CleanYfac,
DTA_Translation, &FireRemap,
DTA_Masked, true,
DTA_Masked, false,
TAG_DONE);
}

View file

@ -3361,12 +3361,12 @@ void InitCrosshairsList()
while ((lump = Wads.FindLump("XHAIRS", &lastlump)) != -1)
{
SC_OpenLumpNum(lump, "XHAIRS");
while (SC_GetNumber())
FScanner sc(lump, "XHAIRS");
while (sc.GetNumber())
{
value.value = float(sc_Number);
SC_MustGetString();
value.name = sc_String;
value.value = float(sc.Number);
sc.MustGetString();
value.name = sc.String;
if (value.value != 0)
{ // Check if it already exists. If not, add it.
unsigned int i;
@ -3388,7 +3388,6 @@ void InitCrosshairsList()
}
}
}
SC_Close();
}
VideoItems[CROSSHAIR_INDEX].b.numvalues = float(Crosshairs.Size());
VideoItems[CROSSHAIR_INDEX].e.valuestrings = &Crosshairs[0];

2388
src/modplug/fastmix.cpp Normal file

File diff suppressed because it is too large Load diff

134
src/modplug/it_defs.h Normal file
View file

@ -0,0 +1,134 @@
#ifndef _ITDEFS_H_
#define _ITDEFS_H_
#pragma pack(1)
typedef struct tagITFILEHEADER
{
DWORD id; // 0x4D504D49
CHAR songname[26];
WORD reserved1; // 0x1004
WORD ordnum;
WORD insnum;
WORD smpnum;
WORD patnum;
WORD cwtv;
WORD cmwt;
WORD flags;
WORD special;
BYTE globalvol;
BYTE mv;
BYTE speed;
BYTE tempo;
BYTE sep;
BYTE zero;
WORD msglength;
DWORD msgoffset;
DWORD reserved2;
BYTE chnpan[64];
BYTE chnvol[64];
} ITFILEHEADER;
typedef struct tagITENVELOPE
{
BYTE flags;
BYTE num;
BYTE lpb;
BYTE lpe;
BYTE slb;
BYTE sle;
BYTE data[25*3];
BYTE reserved;
} ITENVELOPE;
// Old Impulse Instrument Format (cmwt < 0x200)
typedef struct tagITOLDINSTRUMENT
{
DWORD id; // IMPI = 0x49504D49
CHAR filename[12]; // DOS file name
BYTE zero;
BYTE flags;
BYTE vls;
BYTE vle;
BYTE sls;
BYTE sle;
WORD reserved1;
WORD fadeout;
BYTE nna;
BYTE dnc;
WORD trkvers;
BYTE nos;
BYTE reserved2;
CHAR name[26];
WORD reserved3[3];
BYTE keyboard[240];
BYTE volenv[200];
BYTE nodes[50];
} ITOLDINSTRUMENT;
// Impulse Instrument Format
typedef struct tagITINSTRUMENT
{
DWORD id;
CHAR filename[12];
BYTE zero;
BYTE nna;
BYTE dct;
BYTE dca;
WORD fadeout;
signed char pps;
BYTE ppc;
BYTE gbv;
BYTE dfp;
BYTE rv;
BYTE rp;
WORD trkvers;
BYTE nos;
BYTE reserved1;
CHAR name[26];
BYTE ifc;
BYTE ifr;
BYTE mch;
BYTE mpr;
WORD mbank;
BYTE keyboard[240];
ITENVELOPE volenv;
ITENVELOPE panenv;
ITENVELOPE pitchenv;
BYTE dummy[4]; // was 7, but IT v2.17 saves 554 bytes
} ITINSTRUMENT;
// IT Sample Format
typedef struct ITSAMPLESTRUCT
{
DWORD id; // 0x53504D49
CHAR filename[12];
BYTE zero;
BYTE gvl;
BYTE flags;
BYTE vol;
CHAR name[26];
BYTE cvt;
BYTE dfp;
DWORD length;
DWORD loopbegin;
DWORD loopend;
DWORD C5Speed;
DWORD susloopbegin;
DWORD susloopend;
DWORD samplepointer;
BYTE vis;
BYTE vid;
BYTE vir;
BYTE vit;
} ITSAMPLESTRUCT;
#pragma pack()
extern BYTE autovibit2xm[8];
extern BYTE autovibxm2it[8];
#endif

186
src/modplug/load_669.cpp Normal file
View file

@ -0,0 +1,186 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
////////////////////////////////////////////////////////////
// 669 Composer / UNIS 669 module loader
////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
typedef struct tagFILEHEADER669
{
WORD sig; // 'if' or 'JN'
signed char songmessage[108]; // Song Message
BYTE samples; // number of samples (1-64)
BYTE patterns; // number of patterns (1-128)
BYTE restartpos;
BYTE orders[128];
BYTE tempolist[128];
BYTE breaks[128];
} FILEHEADER669;
typedef struct tagSAMPLE669
{
BYTE filename[13];
BYTE length[4]; // when will somebody think about DWORD align ???
BYTE loopstart[4];
BYTE loopend[4];
} SAMPLE669;
BOOL CSoundFile::Read669(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
BOOL b669Ext;
const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream;
const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1);
DWORD dwMemPos = 0;
if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return FALSE;
if ((bswapLE16(pfh->sig) != 0x6669) && (bswapLE16(pfh->sig) != 0x4E4A)) return FALSE;
b669Ext = (bswapLE16(pfh->sig) == 0x4E4A) ? TRUE : FALSE;
if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128)
|| (!pfh->patterns) || (pfh->patterns > 128)) return FALSE;
DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600;
if (dontfuckwithme > dwMemLength) return FALSE;
for (UINT ichk=0; ichk<pfh->samples; ichk++)
{
DWORD len = bswapLE32(*((DWORD *)(&psmp[ichk].length)));
dontfuckwithme += len;
}
if (dontfuckwithme > dwMemLength) return FALSE;
// That should be enough checking: this must be a 669 module.
m_nType = MOD_TYPE_669;
m_dwSongFlags |= SONG_LINEARSLIDES;
m_nMinPeriod = 28 << 2;
m_nMaxPeriod = 1712 << 3;
m_nDefaultTempo = 125;
m_nDefaultSpeed = 6;
m_nChannels = 8;
memcpy(m_szNames[0], pfh->songmessage, 16);
m_nSamples = pfh->samples;
for (UINT nins=1; nins<=m_nSamples; nins++, psmp++)
{
DWORD len = bswapLE32(*((DWORD *)(&psmp->length)));
DWORD loopstart = bswapLE32(*((DWORD *)(&psmp->loopstart)));
DWORD loopend = bswapLE32(*((DWORD *)(&psmp->loopend)));
if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
if ((loopend > len) && (!loopstart)) loopend = 0;
if (loopend > len) loopend = len;
if (loopstart + 4 >= loopend) loopstart = loopend = 0;
Ins[nins].nLength = len;
Ins[nins].nLoopStart = loopstart;
Ins[nins].nLoopEnd = loopend;
if (loopend) Ins[nins].uFlags |= CHN_LOOP;
memcpy(m_szNames[nins], psmp->filename, 13);
Ins[nins].nVolume = 256;
Ins[nins].nGlobalVol = 64;
Ins[nins].nPan = 128;
}
// Song Message
m_lpszSongComments = new char[109];
memcpy(m_lpszSongComments, pfh->songmessage, 108);
m_lpszSongComments[108] = 0;
// Reading Orders
memcpy(Order, pfh->orders, 128);
m_nRestartPos = pfh->restartpos;
if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0;
// Reading Pattern Break Locations
for (UINT npan=0; npan<8; npan++)
{
ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0;
ChnSettings[npan].nVolume = 64;
}
// Reading Patterns
dwMemPos = 0x1F1 + pfh->samples * 25;
for (UINT npat=0; npat<pfh->patterns; npat++)
{
Patterns[npat] = AllocatePattern(64, m_nChannels);
if (!Patterns[npat]) break;
PatternSize[npat] = 64;
MODCOMMAND *m = Patterns[npat];
const BYTE *p = lpStream + dwMemPos;
for (UINT row=0; row<64; row++)
{
MODCOMMAND *mspeed = m;
if ((row == pfh->breaks[npat]) && (row != 63))
{
for (UINT i=0; i<8; i++)
{
m[i].command = CMD_PATTERNBREAK;
m[i].param = 0;
}
}
for (UINT n=0; n<8; n++, m++, p+=3)
{
UINT note = p[0] >> 2;
UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
UINT vol = p[1] & 0x0F;
if (p[0] < 0xFE)
{
m->note = note + 37;
m->instr = instr + 1;
}
if (p[0] <= 0xFE)
{
m->volcmd = VOLCMD_VOLUME;
m->vol = (vol << 2) + 2;
}
if (p[2] != 0xFF)
{
UINT command = p[2] >> 4;
UINT param = p[2] & 0x0F;
switch(command)
{
case 0x00: command = CMD_PORTAMENTOUP; break;
case 0x01: command = CMD_PORTAMENTODOWN; break;
case 0x02: command = CMD_TONEPORTAMENTO; break;
case 0x03: command = CMD_MODCMDEX; param |= 0x50; break;
case 0x04: command = CMD_VIBRATO; param |= 0x40; break;
case 0x05: if (param) command = CMD_SPEED; else command = 0; param += 2; break;
case 0x06: if (param == 0) { command = CMD_PANNINGSLIDE; param = 0xFE; } else
if (param == 1) { command = CMD_PANNINGSLIDE; param = 0xEF; } else
command = 0;
break;
default: command = 0;
}
if (command)
{
if (command == CMD_SPEED) mspeed = NULL;
m->command = command;
m->param = param;
}
}
}
if ((!row) && (mspeed))
{
for (UINT i=0; i<8; i++) if (!mspeed[i].command)
{
mspeed[i].command = CMD_SPEED;
mspeed[i].param = pfh->tempolist[npat] + 2;
break;
}
}
}
dwMemPos += 0x600;
}
// Reading Samples
for (UINT n=1; n<=m_nSamples; n++)
{
UINT len = Ins[n].nLength;
if (dwMemPos >= dwMemLength) break;
if (len > 4) ReadSample(&Ins[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
dwMemPos += len;
}
return TRUE;
}

5114
src/modplug/load_abc.cpp Normal file

File diff suppressed because it is too large Load diff

417
src/modplug/load_amf.cpp Normal file
View file

@ -0,0 +1,417 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
///////////////////////////////////////////////////
//
// AMF module loader
//
// There is 2 types of AMF files:
// - ASYLUM Music Format
// - Advanced Music Format(DSM)
//
///////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#define AMFLOG
//#pragma warning(disable:4244)
#pragma pack(1)
typedef struct _AMFFILEHEADER
{
UCHAR szAMF[3];
UCHAR version;
CHAR title[32];
UCHAR numsamples;
UCHAR numorders;
USHORT numtracks;
UCHAR numchannels;
} AMFFILEHEADER;
typedef struct _AMFSAMPLE
{
UCHAR type;
CHAR samplename[32];
CHAR filename[13];
ULONG offset;
ULONG length;
USHORT c2spd;
UCHAR volume;
} AMFSAMPLE;
#pragma pack()
#ifdef AMFLOG
extern void Log(LPCSTR, ...);
#endif
VOID AMF_Unpack(MODCOMMAND *pPat, const BYTE *pTrack, UINT nRows, UINT nChannels)
//-------------------------------------------------------------------------------
{
UINT lastinstr = 0;
UINT nTrkSize = bswapLE16(*(USHORT *)pTrack);
nTrkSize += (UINT)pTrack[2] <<16;
pTrack += 3;
while (nTrkSize--)
{
UINT row = pTrack[0];
UINT cmd = pTrack[1];
UINT arg = pTrack[2];
if (row >= nRows) break;
MODCOMMAND *m = pPat + row * nChannels;
if (cmd < 0x7F) // note+vol
{
m->note = cmd+1;
if (!m->instr) m->instr = lastinstr;
m->volcmd = VOLCMD_VOLUME;
m->vol = arg;
} else
if (cmd == 0x7F) // duplicate row
{
signed char rdelta = (signed char)arg;
int rowsrc = (int)row + (int)rdelta;
if ((rowsrc >= 0) && (rowsrc < (int)nRows)) memcpy(m, &pPat[rowsrc*nChannels],sizeof(pPat[rowsrc*nChannels]));
} else
if (cmd == 0x80) // instrument
{
m->instr = arg+1;
lastinstr = m->instr;
} else
if (cmd == 0x83) // volume
{
m->volcmd = VOLCMD_VOLUME;
m->vol = arg;
} else
// effect
{
UINT command = cmd & 0x7F;
UINT param = arg;
switch(command)
{
// 0x01: Set Speed
case 0x01: command = CMD_SPEED; break;
// 0x02: Volume Slide
// 0x0A: Tone Porta + Vol Slide
// 0x0B: Vibrato + Vol Slide
case 0x02: command = CMD_VOLUMESLIDE;
case 0x0A: if (command == 0x0A) command = CMD_TONEPORTAVOL;
case 0x0B: if (command == 0x0B) command = CMD_VIBRATOVOL;
if (param & 0x80) param = (-(signed char)param)&0x0F;
else param = (param&0x0F)<<4;
break;
// 0x04: Porta Up/Down
case 0x04: if (param & 0x80) { command = CMD_PORTAMENTOUP; param = -(signed char)param; }
else { command = CMD_PORTAMENTODOWN; } break;
// 0x06: Tone Portamento
case 0x06: command = CMD_TONEPORTAMENTO; break;
// 0x07: Tremor
case 0x07: command = CMD_TREMOR; break;
// 0x08: Arpeggio
case 0x08: command = CMD_ARPEGGIO; break;
// 0x09: Vibrato
case 0x09: command = CMD_VIBRATO; break;
// 0x0C: Pattern Break
case 0x0C: command = CMD_PATTERNBREAK; break;
// 0x0D: Position Jump
case 0x0D: command = CMD_POSITIONJUMP; break;
// 0x0F: Retrig
case 0x0F: command = CMD_RETRIG; break;
// 0x10: Offset
case 0x10: command = CMD_OFFSET; break;
// 0x11: Fine Volume Slide
case 0x11: if (param) { command = CMD_VOLUMESLIDE;
if (param & 0x80) param = 0xF0|((-(signed char)param)&0x0F);
else param = 0x0F|((param&0x0F)<<4);
} else command = 0; break;
// 0x12: Fine Portamento
// 0x16: Extra Fine Portamento
case 0x12:
case 0x16: if (param) { int mask = (command == 0x16) ? 0xE0 : 0xF0;
command = (param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN;
if (param & 0x80) param = mask|((-(signed char)param)&0x0F);
else param |= mask;
} else command = 0; break;
// 0x13: Note Delay
case 0x13: command = CMD_S3MCMDEX; param = 0xD0|(param & 0x0F); break;
// 0x14: Note Cut
case 0x14: command = CMD_S3MCMDEX; param = 0xC0|(param & 0x0F); break;
// 0x15: Set Tempo
case 0x15: command = CMD_TEMPO; break;
// 0x17: Panning
case 0x17: param = (param+64)&0x7F;
if (m->command) { if (!m->volcmd) { m->volcmd = VOLCMD_PANNING; m->vol = param/2; } command = 0; }
else { command = CMD_PANNING8; }
// Unknown effects
default: command = param = 0;
}
if (command)
{
m->command = command;
m->param = param;
}
}
pTrack += 3;
}
}
BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
AMFFILEHEADER *pfh = (AMFFILEHEADER *)lpStream;
DWORD dwMemPos;
if ((!lpStream) || (dwMemLength < 2048)) return FALSE;
if ((!strncmp((LPCTSTR)lpStream, "ASYLUM Music Format V1.0", 25)) && (dwMemLength > 4096))
{
UINT numorders, numpats, numsamples;
dwMemPos = 32;
numpats = lpStream[dwMemPos+3];
numorders = lpStream[dwMemPos+4];
numsamples = 64;
dwMemPos += 6;
if ((!numpats) || (numpats > MAX_PATTERNS) || (!numorders)
|| (numpats*64*32 + 294 + 37*64 >= dwMemLength)) return FALSE;
m_nType = MOD_TYPE_AMF0;
m_nChannels = 8;
m_nInstruments = 0;
m_nSamples = 31;
m_nDefaultTempo = 125;
m_nDefaultSpeed = 6;
for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++)
{
Order[iOrd] = (iOrd < numorders) ? lpStream[dwMemPos+iOrd] : 0xFF;
}
dwMemPos = 294; // ???
for (UINT iSmp=0; iSmp<numsamples; iSmp++)
{
MODINSTRUMENT *psmp = &Ins[iSmp+1];
memcpy(m_szNames[iSmp+1], lpStream+dwMemPos, 22);
psmp->nFineTune = MOD2XMFineTune(lpStream[dwMemPos+22]);
psmp->nVolume = lpStream[dwMemPos+23];
psmp->nGlobalVol = 64;
if (psmp->nVolume > 0x40) psmp->nVolume = 0x40;
psmp->nVolume <<= 2;
psmp->nLength = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+25)));
psmp->nLoopStart = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+29)));
psmp->nLoopEnd = psmp->nLoopStart + bswapLE32(*((LPDWORD)(lpStream+dwMemPos+33)));
if ((psmp->nLoopEnd > psmp->nLoopStart) && (psmp->nLoopEnd <= psmp->nLength))
{
psmp->uFlags = CHN_LOOP;
} else
{
psmp->nLoopStart = psmp->nLoopEnd = 0;
}
if ((psmp->nLength) && (iSmp>31)) m_nSamples = iSmp+1;
dwMemPos += 37;
}
for (UINT iPat=0; iPat<numpats; iPat++)
{
MODCOMMAND *p = AllocatePattern(64, m_nChannels);
if (!p) break;
Patterns[iPat] = p;
PatternSize[iPat] = 64;
const UCHAR *pin = lpStream + dwMemPos;
for (UINT i=0; i<8*64; i++)
{
p->note = 0;
if (pin[0])
{
p->note = pin[0] + 13;
}
p->instr = pin[1];
p->command = pin[2];
p->param = pin[3];
if (p->command > 0x0F)
{
#ifdef AMFLOG
Log("0x%02X.0x%02X ?", p->command, p->param);
#endif
p->command = 0;
}
ConvertModCommand(p);
pin += 4;
p++;
}
dwMemPos += 64*32;
}
// Read samples
for (UINT iData=0; iData<m_nSamples; iData++)
{
MODINSTRUMENT *psmp = &Ins[iData+1];
if (psmp->nLength)
{
dwMemPos += ReadSample(psmp, RS_PCM8S, (LPCSTR)(lpStream+dwMemPos), dwMemLength);
}
}
return TRUE;
}
////////////////////////////
// DSM/AMF
USHORT *ptracks[MAX_PATTERNS];
DWORD sampleseekpos[MAX_SAMPLES];
if ((pfh->szAMF[0] != 'A') || (pfh->szAMF[1] != 'M') || (pfh->szAMF[2] != 'F')
|| (pfh->version < 10) || (pfh->version > 14) || (!bswapLE16(pfh->numtracks))
|| (!pfh->numorders) || (pfh->numorders > MAX_PATTERNS)
|| (!pfh->numsamples) || (pfh->numsamples > MAX_SAMPLES)
|| (pfh->numchannels < 4) || (pfh->numchannels > 32))
return FALSE;
memcpy(m_szNames[0], pfh->title, 32);
dwMemPos = sizeof(AMFFILEHEADER);
m_nType = MOD_TYPE_AMF;
m_nChannels = pfh->numchannels;
m_nSamples = pfh->numsamples;
m_nInstruments = 0;
// Setup Channel Pan Positions
if (pfh->version >= 11)
{
signed char *panpos = (signed char *)(lpStream + dwMemPos);
UINT nchannels = (pfh->version >= 13) ? 32 : 16;
for (UINT i=0; i<nchannels; i++)
{
int pan = (panpos[i] + 64) * 2;
if (pan < 0) pan = 0;
if (pan > 256) { pan = 128; ChnSettings[i].dwFlags |= CHN_SURROUND; }
ChnSettings[i].nPan = pan;
}
dwMemPos += nchannels;
} else
{
for (UINT i=0; i<16; i++)
{
ChnSettings[i].nPan = (lpStream[dwMemPos+i] & 1) ? 0x30 : 0xD0;
}
dwMemPos += 16;
}
// Get Tempo/Speed
m_nDefaultTempo = 125;
m_nDefaultSpeed = 6;
if (pfh->version >= 13)
{
if (lpStream[dwMemPos] >= 32) m_nDefaultTempo = lpStream[dwMemPos];
if (lpStream[dwMemPos+1] <= 32) m_nDefaultSpeed = lpStream[dwMemPos+1];
dwMemPos += 2;
}
// Setup sequence list
for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++)
{
Order[iOrd] = 0xFF;
if (iOrd < pfh->numorders)
{
Order[iOrd] = iOrd;
PatternSize[iOrd] = 64;
if (pfh->version >= 14)
{
PatternSize[iOrd] = bswapLE16(*(USHORT *)(lpStream+dwMemPos));
dwMemPos += 2;
}
ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos);
dwMemPos += m_nChannels * sizeof(USHORT);
}
}
if (dwMemPos + m_nSamples * (sizeof(AMFSAMPLE)+8) > dwMemLength) return TRUE;
// Read Samples
UINT maxsampleseekpos = 0;
for (UINT iIns=0; iIns<m_nSamples; iIns++)
{
MODINSTRUMENT *pins = &Ins[iIns+1];
AMFSAMPLE *psh = (AMFSAMPLE *)(lpStream + dwMemPos);
dwMemPos += sizeof(AMFSAMPLE);
memcpy(m_szNames[iIns+1], psh->samplename, 32);
memcpy(pins->name, psh->filename, 13);
pins->nLength = bswapLE32(psh->length);
pins->nC4Speed = bswapLE16(psh->c2spd);
pins->nGlobalVol = 64;
pins->nVolume = psh->volume * 4;
if (pfh->version >= 11)
{
pins->nLoopStart = bswapLE32(*(DWORD *)(lpStream+dwMemPos));
pins->nLoopEnd = bswapLE32(*(DWORD *)(lpStream+dwMemPos+4));
dwMemPos += 8;
} else
{
pins->nLoopStart = bswapLE16(*(WORD *)(lpStream+dwMemPos));
pins->nLoopEnd = pins->nLength;
dwMemPos += 2;
}
sampleseekpos[iIns] = 0;
if ((psh->type) && (bswapLE32(psh->offset) < dwMemLength-1))
{
sampleseekpos[iIns] = bswapLE32(psh->offset);
if (bswapLE32(psh->offset) > maxsampleseekpos)
maxsampleseekpos = bswapLE32(psh->offset);
if ((pins->nLoopEnd > pins->nLoopStart + 2)
&& (pins->nLoopEnd <= pins->nLength)) pins->uFlags |= CHN_LOOP;
}
}
// Read Track Mapping Table
USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos);
UINT realtrackcnt = 0;
dwMemPos += pfh->numtracks * sizeof(USHORT);
for (UINT iTrkMap=0; iTrkMap<pfh->numtracks; iTrkMap++)
{
if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap];
}
// Store tracks positions
BYTE **pTrackData = new BYTE *[realtrackcnt];
memset(pTrackData, 0, sizeof(pTrackData));
for (UINT iTrack=0; iTrack<realtrackcnt; iTrack++) if (dwMemPos + 3 <= dwMemLength)
{
UINT nTrkSize = bswapLE16(*(USHORT *)(lpStream+dwMemPos));
nTrkSize += (UINT)lpStream[dwMemPos+2] << 16;
if (dwMemPos + nTrkSize * 3 + 3 <= dwMemLength)
{
pTrackData[iTrack] = (BYTE *)(lpStream + dwMemPos);
}
dwMemPos += nTrkSize * 3 + 3;
}
// Create the patterns from the list of tracks
for (UINT iPat=0; iPat<pfh->numorders; iPat++)
{
MODCOMMAND *p = AllocatePattern(PatternSize[iPat], m_nChannels);
if (!p) break;
Patterns[iPat] = p;
for (UINT iChn=0; iChn<m_nChannels; iChn++)
{
UINT nTrack = bswapLE16(ptracks[iPat][iChn]);
if ((nTrack) && (nTrack <= pfh->numtracks))
{
UINT realtrk = bswapLE16(pTrackMap[nTrack-1]);
if (realtrk)
{
realtrk--;
if ((realtrk < realtrackcnt) && (pTrackData[realtrk]))
{
AMF_Unpack(p+iChn, pTrackData[realtrk], PatternSize[iPat], m_nChannels);
}
}
}
}
}
delete pTrackData;
// Read Sample Data
for (UINT iSeek=1; iSeek<=maxsampleseekpos; iSeek++)
{
if (dwMemPos >= dwMemLength) break;
for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (iSeek == sampleseekpos[iSmp])
{
MODINSTRUMENT *pins = &Ins[iSmp+1];
dwMemPos += ReadSample(pins, RS_PCM8U, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
break;
}
}
return TRUE;
}

628
src/modplug/load_ams.cpp Normal file
View file

@ -0,0 +1,628 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
//////////////////////////////////////////////
// AMS module loader //
//////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
#pragma pack(1)
typedef struct AMSFILEHEADER
{
char szHeader[7]; // "Extreme" // changed from CHAR
BYTE verlo, verhi; // 0x??,0x01
BYTE chncfg;
BYTE samples;
WORD patterns;
WORD orders;
BYTE vmidi;
WORD extra;
} AMSFILEHEADER;
typedef struct AMSSAMPLEHEADER
{
DWORD length;
DWORD loopstart;
DWORD loopend;
BYTE finetune_and_pan;
WORD samplerate; // C-2 = 8363
BYTE volume; // 0-127
BYTE infobyte;
} AMSSAMPLEHEADER;
#pragma pack()
BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
BYTE pkinf[MAX_SAMPLES];
AMSFILEHEADER *pfh = (AMSFILEHEADER *)lpStream;
DWORD dwMemPos;
UINT tmp, tmp2;
if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
if ((pfh->verhi != 0x01) || (strncmp(pfh->szHeader, "Extreme", 7))
|| (!pfh->patterns) || (!pfh->orders) || (!pfh->samples) || (pfh->samples > MAX_SAMPLES)
|| (pfh->patterns > MAX_PATTERNS) || (pfh->orders > MAX_ORDERS))
{
return ReadAMS2(lpStream, dwMemLength);
}
dwMemPos = sizeof(AMSFILEHEADER) + pfh->extra;
if (dwMemPos + pfh->samples * sizeof(AMSSAMPLEHEADER) + 256 >= dwMemLength) return FALSE;
m_nType = MOD_TYPE_AMS;
m_nInstruments = 0;
m_nChannels = (pfh->chncfg & 0x1F) + 1;
m_nSamples = pfh->samples;
for (UINT nSmp=1; nSmp<=m_nSamples; nSmp++, dwMemPos += sizeof(AMSSAMPLEHEADER))
{
AMSSAMPLEHEADER *psh = (AMSSAMPLEHEADER *)(lpStream + dwMemPos);
MODINSTRUMENT *pins = &Ins[nSmp];
pins->nLength = psh->length;
pins->nLoopStart = psh->loopstart;
pins->nLoopEnd = psh->loopend;
pins->nGlobalVol = 64;
pins->nVolume = psh->volume << 1;
pins->nC4Speed = psh->samplerate;
pins->nPan = (psh->finetune_and_pan & 0xF0);
if (pins->nPan < 0x80) pins->nPan += 0x10;
pins->nFineTune = MOD2XMFineTune(psh->finetune_and_pan & 0x0F);
pins->uFlags = (psh->infobyte & 0x80) ? CHN_16BIT : 0;
if ((pins->nLoopEnd <= pins->nLength) && (pins->nLoopStart+4 <= pins->nLoopEnd)) pins->uFlags |= CHN_LOOP;
pkinf[nSmp] = psh->infobyte;
}
// Read Song Name
tmp = lpStream[dwMemPos++];
if (dwMemPos + tmp + 1 >= dwMemLength) return TRUE;
tmp2 = (tmp < 32) ? tmp : 31;
if (tmp2) memcpy(m_szNames[0], lpStream+dwMemPos, tmp2);
m_szNames[0][tmp2] = 0;
dwMemPos += tmp;
// Read sample names
for (UINT sNam=1; sNam<=m_nSamples; sNam++)
{
if (dwMemPos + 32 >= dwMemLength) return TRUE;
tmp = lpStream[dwMemPos++];
tmp2 = (tmp < 32) ? tmp : 31;
if (tmp2) memcpy(m_szNames[sNam], lpStream+dwMemPos, tmp2);
dwMemPos += tmp;
}
// Skip Channel names
for (UINT cNam=0; cNam<m_nChannels; cNam++)
{
if (dwMemPos + 32 >= dwMemLength) return TRUE;
tmp = lpStream[dwMemPos++];
dwMemPos += tmp;
}
// Read Pattern Names
m_lpszPatternNames = new char[pfh->patterns * 32]; // changed from CHAR
if (!m_lpszPatternNames) return TRUE;
m_nPatternNames = pfh->patterns;
memset(m_lpszPatternNames, 0, m_nPatternNames * 32);
for (UINT pNam=0; pNam < m_nPatternNames; pNam++)
{
if (dwMemPos + 32 >= dwMemLength) return TRUE;
tmp = lpStream[dwMemPos++];
tmp2 = (tmp < 32) ? tmp : 31;
if (tmp2) memcpy(m_lpszPatternNames+pNam*32, lpStream+dwMemPos, tmp2);
dwMemPos += tmp;
}
// Read Song Comments
tmp = *((WORD *)(lpStream+dwMemPos));
dwMemPos += 2;
if (dwMemPos + tmp >= dwMemLength) return TRUE;
if (tmp)
{
m_lpszSongComments = new char[tmp+1]; // changed from CHAR
if (!m_lpszSongComments) return TRUE;
memset(m_lpszSongComments, 0, tmp+1);
memcpy(m_lpszSongComments, lpStream + dwMemPos, tmp);
dwMemPos += tmp;
}
// Read Order List
for (UINT iOrd=0; iOrd<pfh->orders; iOrd++, dwMemPos += 2)
{
UINT n = *((WORD *)(lpStream+dwMemPos));
Order[iOrd] = (BYTE)n;
}
// Read Patterns
for (UINT iPat=0; iPat<pfh->patterns; iPat++)
{
if (dwMemPos + 4 >= dwMemLength) return TRUE;
UINT len = *((DWORD *)(lpStream + dwMemPos));
dwMemPos += 4;
if ((len >= dwMemLength) || (dwMemPos + len > dwMemLength)) return TRUE;
PatternSize[iPat] = 64;
MODCOMMAND *m = AllocatePattern(PatternSize[iPat], m_nChannels);
if (!m) return TRUE;
Patterns[iPat] = m;
const BYTE *p = lpStream + dwMemPos;
UINT row = 0, i = 0;
while ((row < PatternSize[iPat]) && (i+2 < len))
{
BYTE b0 = p[i++];
BYTE b1 = p[i++];
BYTE b2 = 0;
UINT ch = b0 & 0x3F;
// Note+Instr
if (!(b0 & 0x40))
{
b2 = p[i++];
if (ch < m_nChannels)
{
if (b1 & 0x7F) m[ch].note = (b1 & 0x7F) + 25;
m[ch].instr = b2;
}
if (b1 & 0x80)
{
b0 |= 0x40;
b1 = p[i++];
}
}
// Effect
if (b0 & 0x40)
{
anothercommand:
if (b1 & 0x40)
{
if (ch < m_nChannels)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = b1 & 0x3F;
}
} else
{
b2 = p[i++];
if (ch < m_nChannels)
{
UINT cmd = b1 & 0x3F;
if (cmd == 0x0C)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = b2 >> 1;
} else
if (cmd == 0x0E)
{
if (!m[ch].command)
{
UINT command = CMD_S3MCMDEX;
UINT param = b2;
switch(param & 0xF0)
{
case 0x00: if (param & 0x08) { param &= 0x07; param |= 0x90; } else {command=param=0;} break;
case 0x10: command = CMD_PORTAMENTOUP; param |= 0xF0; break;
case 0x20: command = CMD_PORTAMENTODOWN; param |= 0xF0; break;
case 0x30: param = (param & 0x0F) | 0x10; break;
case 0x40: param = (param & 0x0F) | 0x30; break;
case 0x50: param = (param & 0x0F) | 0x20; break;
case 0x60: param = (param & 0x0F) | 0xB0; break;
case 0x70: param = (param & 0x0F) | 0x40; break;
case 0x90: command = CMD_RETRIG; param &= 0x0F; break;
case 0xA0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command=param=0; break;
case 0xB0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command=param=0; break;
}
m[ch].command = command;
m[ch].param = param;
}
} else
{
m[ch].command = cmd;
m[ch].param = b2;
ConvertModCommand(&m[ch]);
}
}
}
if (b1 & 0x80)
{
b1 = p[i++];
if (i <= len) goto anothercommand;
}
}
if (b0 & 0x80)
{
row++;
m += m_nChannels;
}
}
dwMemPos += len;
}
// Read Samples
for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength)
{
if (dwMemPos >= dwMemLength - 9) return TRUE;
UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8;
dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////
// AMS 2.2 loader
#pragma pack(1)
typedef struct AMS2FILEHEADER
{
DWORD dwHdr1; // AMShdr
WORD wHdr2;
BYTE b1A; // 0x1A
BYTE titlelen; // 30-bytes max
CHAR szTitle[30]; // [titlelen]
} AMS2FILEHEADER;
typedef struct AMS2SONGHEADER
{
WORD version;
BYTE instruments;
WORD patterns;
WORD orders;
WORD bpm;
BYTE speed;
BYTE channels;
BYTE commands;
BYTE rows;
WORD flags;
} AMS2SONGHEADER;
typedef struct AMS2INSTRUMENT
{
BYTE samples;
BYTE notemap[120];
} AMS2INSTRUMENT;
typedef struct AMS2ENVELOPE
{
BYTE speed;
BYTE sustain;
BYTE loopbegin;
BYTE loopend;
BYTE points;
BYTE info[3];
} AMS2ENVELOPE;
typedef struct AMS2SAMPLE
{
DWORD length;
DWORD loopstart;
DWORD loopend;
WORD frequency;
BYTE finetune;
WORD c4speed;
CHAR transpose;
BYTE volume;
BYTE flags;
} AMS2SAMPLE;
#pragma pack()
BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength)
//------------------------------------------------------------
{
AMS2FILEHEADER *pfh = (AMS2FILEHEADER *)lpStream;
AMS2SONGHEADER *psh;
DWORD dwMemPos;
BYTE smpmap[16];
BYTE packedsamples[MAX_SAMPLES];
if ((pfh->dwHdr1 != 0x68534D41) || (pfh->wHdr2 != 0x7264)
|| (pfh->b1A != 0x1A) || (pfh->titlelen > 30)) return FALSE;
dwMemPos = pfh->titlelen + 8;
psh = (AMS2SONGHEADER *)(lpStream + dwMemPos);
if (((psh->version & 0xFF00) != 0x0200) || (!psh->instruments)
|| (psh->instruments > MAX_INSTRUMENTS) || (!psh->patterns) || (!psh->orders)) return FALSE;
dwMemPos += sizeof(AMS2SONGHEADER);
if (pfh->titlelen)
{
memcpy(m_szNames, pfh->szTitle, pfh->titlelen);
m_szNames[0][pfh->titlelen] = 0;
}
m_nType = MOD_TYPE_AMS;
m_nChannels = 32;
m_nDefaultTempo = psh->bpm >> 8;
m_nDefaultSpeed = psh->speed;
m_nInstruments = psh->instruments;
m_nSamples = 0;
if (psh->flags & 0x40) m_dwSongFlags |= SONG_LINEARSLIDES;
for (UINT nIns=1; nIns<=m_nInstruments; nIns++)
{
UINT insnamelen = lpStream[dwMemPos];
CHAR *pinsname = (CHAR *)(lpStream+dwMemPos+1);
dwMemPos += insnamelen + 1;
AMS2INSTRUMENT *pins = (AMS2INSTRUMENT *)(lpStream + dwMemPos);
dwMemPos += sizeof(AMS2INSTRUMENT);
if (dwMemPos + 1024 >= dwMemLength) return TRUE;
AMS2ENVELOPE *volenv, *panenv, *pitchenv;
volenv = (AMS2ENVELOPE *)(lpStream+dwMemPos);
dwMemPos += 5 + volenv->points*3;
panenv = (AMS2ENVELOPE *)(lpStream+dwMemPos);
dwMemPos += 5 + panenv->points*3;
pitchenv = (AMS2ENVELOPE *)(lpStream+dwMemPos);
dwMemPos += 5 + pitchenv->points*3;
INSTRUMENTHEADER *penv = new INSTRUMENTHEADER;
if (!penv) return TRUE;
memset(smpmap, 0, sizeof(smpmap));
memset(penv, 0, sizeof(INSTRUMENTHEADER));
for (UINT ismpmap=0; ismpmap<pins->samples; ismpmap++)
{
if ((ismpmap >= 16) || (m_nSamples+1 >= MAX_SAMPLES)) break;
m_nSamples++;
smpmap[ismpmap] = m_nSamples;
}
penv->nGlobalVol = 64;
penv->nPan = 128;
penv->nPPC = 60;
Headers[nIns] = penv;
if (insnamelen)
{
if (insnamelen > 31) insnamelen = 31;
memcpy(penv->name, pinsname, insnamelen);
penv->name[insnamelen] = 0;
}
for (UINT inotemap=0; inotemap<120; inotemap++)
{
penv->NoteMap[inotemap] = inotemap+1;
penv->Keyboard[inotemap] = smpmap[pins->notemap[inotemap] & 0x0F];
}
// Volume Envelope
{
UINT pos = 0;
penv->nVolEnv = (volenv->points > 16) ? 16 : volenv->points;
penv->nVolSustainBegin = penv->nVolSustainEnd = volenv->sustain;
penv->nVolLoopStart = volenv->loopbegin;
penv->nVolLoopEnd = volenv->loopend;
for (UINT i=0; i<penv->nVolEnv; i++)
{
penv->VolEnv[i] = (BYTE)((volenv->info[i*3+2] & 0x7F) >> 1);
pos += volenv->info[i*3] + ((volenv->info[i*3+1] & 1) << 8);
penv->VolPoints[i] = (WORD)pos;
}
}
penv->nFadeOut = (((lpStream[dwMemPos+2] & 0x0F) << 8) | (lpStream[dwMemPos+1])) << 3;
UINT envflags = lpStream[dwMemPos+3];
if (envflags & 0x01) penv->dwFlags |= ENV_VOLLOOP;
if (envflags & 0x02) penv->dwFlags |= ENV_VOLSUSTAIN;
if (envflags & 0x04) penv->dwFlags |= ENV_VOLUME;
dwMemPos += 5;
// Read Samples
for (UINT ismp=0; ismp<pins->samples; ismp++)
{
MODINSTRUMENT *psmp = ((ismp < 16) && (smpmap[ismp])) ? &Ins[smpmap[ismp]] : NULL;
UINT smpnamelen = lpStream[dwMemPos];
if ((psmp) && (smpnamelen) && (smpnamelen <= 22))
{
memcpy(m_szNames[smpmap[ismp]], lpStream+dwMemPos+1, smpnamelen);
}
dwMemPos += smpnamelen + 1;
if (psmp)
{
AMS2SAMPLE *pams = (AMS2SAMPLE *)(lpStream+dwMemPos);
psmp->nGlobalVol = 64;
psmp->nPan = 128;
psmp->nLength = pams->length;
psmp->nLoopStart = pams->loopstart;
psmp->nLoopEnd = pams->loopend;
psmp->nC4Speed = pams->c4speed;
psmp->RelativeTone = pams->transpose;
psmp->nVolume = pams->volume / 2;
packedsamples[smpmap[ismp]] = pams->flags;
if (pams->flags & 0x04) psmp->uFlags |= CHN_16BIT;
if (pams->flags & 0x08) psmp->uFlags |= CHN_LOOP;
if (pams->flags & 0x10) psmp->uFlags |= CHN_PINGPONGLOOP;
}
dwMemPos += sizeof(AMS2SAMPLE);
}
}
if (dwMemPos + 256 >= dwMemLength) return TRUE;
// Comments
{
UINT composernamelen = lpStream[dwMemPos];
if (composernamelen)
{
m_lpszSongComments = new char[composernamelen+1]; // changed from CHAR
if (m_lpszSongComments)
{
memcpy(m_lpszSongComments, lpStream+dwMemPos+1, composernamelen);
m_lpszSongComments[composernamelen] = 0;
}
}
dwMemPos += composernamelen + 1;
// channel names
for (UINT i=0; i<32; i++)
{
UINT chnnamlen = lpStream[dwMemPos];
if ((chnnamlen) && (chnnamlen < MAX_CHANNELNAME))
{
memcpy(ChnSettings[i].szName, lpStream+dwMemPos+1, chnnamlen);
}
dwMemPos += chnnamlen + 1;
if (dwMemPos + chnnamlen + 256 >= dwMemLength) return TRUE;
}
// packed comments (ignored)
UINT songtextlen = *((LPDWORD)(lpStream+dwMemPos));
dwMemPos += songtextlen;
if (dwMemPos + 256 >= dwMemLength) return TRUE;
}
// Order List
{
for (UINT i=0; i<MAX_ORDERS; i++)
{
Order[i] = 0xFF;
if (dwMemPos + 2 >= dwMemLength) return TRUE;
if (i < psh->orders)
{
Order[i] = lpStream[dwMemPos];
dwMemPos += 2;
}
}
}
// Pattern Data
for (UINT ipat=0; ipat<psh->patterns; ipat++)
{
if (dwMemPos+8 >= dwMemLength) return TRUE;
UINT packedlen = *((LPDWORD)(lpStream+dwMemPos));
UINT numrows = 1 + (UINT)(lpStream[dwMemPos+4]);
//UINT patchn = 1 + (UINT)(lpStream[dwMemPos+5] & 0x1F);
//UINT patcmds = 1 + (UINT)(lpStream[dwMemPos+5] >> 5);
UINT patnamlen = lpStream[dwMemPos+6];
dwMemPos += 4;
if ((ipat < MAX_PATTERNS) && (packedlen < dwMemLength-dwMemPos) && (numrows >= 8))
{
if ((patnamlen) && (patnamlen < MAX_PATTERNNAME))
{
char s[MAX_PATTERNNAME]; // changed from CHAR
memcpy(s, lpStream+dwMemPos+3, patnamlen);
s[patnamlen] = 0;
SetPatternName(ipat, s);
}
PatternSize[ipat] = numrows;
Patterns[ipat] = AllocatePattern(numrows, m_nChannels);
if (!Patterns[ipat]) return TRUE;
// Unpack Pattern Data
LPCBYTE psrc = lpStream + dwMemPos;
UINT pos = 3 + patnamlen;
UINT row = 0;
while ((pos < packedlen) && (row < numrows))
{
MODCOMMAND *m = Patterns[ipat] + row * m_nChannels;
UINT byte1 = psrc[pos++];
UINT ch = byte1 & 0x1F;
// Read Note + Instr
if (!(byte1 & 0x40))
{
UINT byte2 = psrc[pos++];
UINT note = byte2 & 0x7F;
if (note) m[ch].note = (note > 1) ? (note-1) : 0xFF;
m[ch].instr = psrc[pos++];
// Read Effect
while (byte2 & 0x80)
{
byte2 = psrc[pos++];
if (byte2 & 0x40)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = byte2 & 0x3F;
} else
{
UINT command = byte2 & 0x3F;
UINT param = psrc[pos++];
if (command == 0x0C)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = param / 2;
} else
if (command < 0x10)
{
m[ch].command = command;
m[ch].param = param;
ConvertModCommand(&m[ch]);
} else
{
// TODO: AMS effects
}
}
}
}
if (byte1 & 0x80) row++;
}
}
dwMemPos += packedlen;
}
// Read Samples
for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength)
{
if (dwMemPos >= dwMemLength - 9) return TRUE;
UINT flags;
if (packedsamples[iSmp] & 0x03)
{
flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8;
} else
{
flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S;
}
dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////
// AMS Sample unpacking
void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char packcharacter)
{
UINT tmplen = dmax;
signed char *amstmp = new signed char[tmplen];
if (!amstmp) return;
// Unpack Loop
{
signed char *p = amstmp;
UINT i=0, j=0;
while ((i < inputlen) && (j < tmplen))
{
signed char ch = psrc[i++];
if (ch == packcharacter)
{
BYTE ch2 = psrc[i++];
if (ch2)
{
ch = psrc[i++];
while (ch2--)
{
p[j++] = ch;
if (j >= tmplen) break;
}
} else p[j++] = packcharacter;
} else p[j++] = ch;
}
}
// Bit Unpack Loop
{
signed char *p = amstmp;
UINT bitcount = 0x80, dh;
UINT k=0;
for (UINT i=0; i<dmax; i++)
{
BYTE al = *p++;
dh = 0;
for (UINT count=0; count<8; count++)
{
UINT bl = al & bitcount;
bl = ((bl|(bl<<8)) >> ((dh+8-count) & 7)) & 0xFF;
bitcount = ((bitcount|(bitcount<<8)) >> 1) & 0xFF;
pdest[k++] |= bl;
if (k >= dmax)
{
k = 0;
dh++;
}
}
bitcount = ((bitcount|(bitcount<<8)) >> dh) & 0xFF;
}
}
// Delta Unpack
{
signed char old = 0;
for (UINT i=0; i<dmax; i++)
{
int pos = ((LPBYTE)pdest)[i];
if ((pos != 128) && (pos & 0x80)) pos = -(pos & 0x7F);
old -= (signed char)pos;
pdest[i] = old;
}
}
delete amstmp;
}

368
src/modplug/load_dbm.cpp Normal file
View file

@ -0,0 +1,368 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
///////////////////////////////////////////////////////////////
//
// DigiBooster Pro Module Loader (*.dbm)
//
// Note: this loader doesn't handle multiple songs
//
///////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
#define DBM_FILE_MAGIC 0x304d4244
#define DBM_ID_NAME 0x454d414e
#define DBM_NAMELEN 0x2c000000
#define DBM_ID_INFO 0x4f464e49
#define DBM_INFOLEN 0x0a000000
#define DBM_ID_SONG 0x474e4f53
#define DBM_ID_INST 0x54534e49
#define DBM_ID_VENV 0x564e4556
#define DBM_ID_PATT 0x54544150
#define DBM_ID_SMPL 0x4c504d53
#pragma pack(1)
typedef struct DBMFILEHEADER
{
DWORD dbm_id; // "DBM0" = 0x304d4244
WORD trkver; // Tracker version: 02.15
WORD reserved;
DWORD name_id; // "NAME" = 0x454d414e
DWORD name_len; // name length: always 44
CHAR songname[44];
DWORD info_id; // "INFO" = 0x4f464e49
DWORD info_len; // 0x0a000000
WORD instruments;
WORD samples;
WORD songs;
WORD patterns;
WORD channels;
DWORD song_id; // "SONG" = 0x474e4f53
DWORD song_len;
CHAR songname2[44];
WORD orders;
// WORD orderlist[0]; // orderlist[orders] in words
} DBMFILEHEADER;
typedef struct DBMINSTRUMENT
{
CHAR name[30];
WORD sampleno;
WORD volume;
DWORD finetune;
DWORD loopstart;
DWORD looplen;
WORD panning;
WORD flags;
} DBMINSTRUMENT;
typedef struct DBMENVELOPE
{
WORD instrument;
BYTE flags;
BYTE numpoints;
BYTE sustain1;
BYTE loopbegin;
BYTE loopend;
BYTE sustain2;
WORD volenv[2*32];
} DBMENVELOPE;
typedef struct DBMPATTERN
{
WORD rows;
DWORD packedsize;
BYTE patterndata[2]; // [packedsize]
} DBMPATTERN;
typedef struct DBMSAMPLE
{
DWORD flags;
DWORD samplesize;
BYTE sampledata[2]; // [samplesize]
} DBMSAMPLE;
#pragma pack()
BOOL CSoundFile::ReadDBM(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
DBMFILEHEADER *pfh = (DBMFILEHEADER *)lpStream;
DWORD dwMemPos;
UINT nOrders, nSamples, nInstruments, nPatterns;
if ((!lpStream) || (dwMemLength <= sizeof(DBMFILEHEADER)) || (!pfh->channels)
|| (pfh->dbm_id != DBM_FILE_MAGIC) || (!pfh->songs) || (pfh->song_id != DBM_ID_SONG)
|| (pfh->name_id != DBM_ID_NAME) || (pfh->name_len != DBM_NAMELEN)
|| (pfh->info_id != DBM_ID_INFO) || (pfh->info_len != DBM_INFOLEN)) return FALSE;
dwMemPos = sizeof(DBMFILEHEADER);
nOrders = bswapBE16(pfh->orders);
if (dwMemPos + 2 * nOrders + 8*3 >= dwMemLength) return FALSE;
nInstruments = bswapBE16(pfh->instruments);
nSamples = bswapBE16(pfh->samples);
nPatterns = bswapBE16(pfh->patterns);
m_nType = MOD_TYPE_DBM;
m_nChannels = bswapBE16(pfh->channels);
if (m_nChannels < 4) m_nChannels = 4;
if (m_nChannels > 64) m_nChannels = 64;
memcpy(m_szNames[0], (pfh->songname[0]) ? pfh->songname : pfh->songname2, 32);
m_szNames[0][31] = 0;
for (UINT iOrd=0; iOrd < nOrders; iOrd++)
{
Order[iOrd] = lpStream[dwMemPos+iOrd*2+1];
if (iOrd >= MAX_ORDERS-2) break;
}
dwMemPos += 2*nOrders;
while (dwMemPos + 10 < dwMemLength)
{
DWORD chunk_id = ((LPDWORD)(lpStream+dwMemPos))[0];
DWORD chunk_size = bswapBE32(((LPDWORD)(lpStream+dwMemPos))[1]);
DWORD chunk_pos;
dwMemPos += 8;
chunk_pos = dwMemPos;
if ((dwMemPos + chunk_size > dwMemLength) || (chunk_size > dwMemLength)) break;
dwMemPos += chunk_size;
// Instruments
if (chunk_id == DBM_ID_INST)
{
if (nInstruments >= MAX_INSTRUMENTS) nInstruments = MAX_INSTRUMENTS-1;
for (UINT iIns=0; iIns<nInstruments; iIns++)
{
MODINSTRUMENT *psmp;
INSTRUMENTHEADER *penv;
DBMINSTRUMENT *pih;
UINT nsmp;
if (chunk_pos + sizeof(DBMINSTRUMENT) > dwMemPos) break;
if ((penv = new INSTRUMENTHEADER) == NULL) break;
pih = (DBMINSTRUMENT *)(lpStream+chunk_pos);
nsmp = bswapBE16(pih->sampleno);
psmp = ((nsmp) && (nsmp < MAX_SAMPLES)) ? &Ins[nsmp] : NULL;
memset(penv, 0, sizeof(INSTRUMENTHEADER));
memcpy(penv->name, pih->name, 30);
if (psmp)
{
memcpy(m_szNames[nsmp], pih->name, 30);
m_szNames[nsmp][30] = 0;
}
Headers[iIns+1] = penv;
penv->nFadeOut = 1024; // ???
penv->nGlobalVol = 64;
penv->nPan = bswapBE16(pih->panning);
if ((penv->nPan) && (penv->nPan < 256))
penv->dwFlags = ENV_SETPANNING;
else
penv->nPan = 128;
penv->nPPC = 5*12;
for (UINT i=0; i<120; i++)
{
penv->Keyboard[i] = nsmp;
penv->NoteMap[i] = i+1;
}
// Sample Info
if (psmp)
{
DWORD sflags = bswapBE16(pih->flags);
psmp->nVolume = bswapBE16(pih->volume) * 4;
if ((!psmp->nVolume) || (psmp->nVolume > 256)) psmp->nVolume = 256;
psmp->nGlobalVol = 64;
psmp->nC4Speed = bswapBE32(pih->finetune);
int f2t = FrequencyToTranspose(psmp->nC4Speed);
psmp->RelativeTone = f2t >> 7;
psmp->nFineTune = f2t & 0x7F;
if ((pih->looplen) && (sflags & 3))
{
psmp->nLoopStart = bswapBE32(pih->loopstart);
psmp->nLoopEnd = psmp->nLoopStart + bswapBE32(pih->looplen);
psmp->uFlags |= CHN_LOOP;
psmp->uFlags &= ~CHN_PINGPONGLOOP;
if (sflags & 2) psmp->uFlags |= CHN_PINGPONGLOOP;
}
}
chunk_pos += sizeof(DBMINSTRUMENT);
m_nInstruments = iIns+1;
}
} else
// Volume Envelopes
if (chunk_id == DBM_ID_VENV)
{
UINT nEnvelopes = lpStream[chunk_pos+1];
chunk_pos += 2;
for (UINT iEnv=0; iEnv<nEnvelopes; iEnv++)
{
DBMENVELOPE *peh;
UINT nins;
if (chunk_pos + sizeof(DBMENVELOPE) > dwMemPos) break;
peh = (DBMENVELOPE *)(lpStream+chunk_pos);
nins = bswapBE16(peh->instrument);
if ((nins) && (nins < MAX_INSTRUMENTS) && (Headers[nins]) && (peh->numpoints))
{
INSTRUMENTHEADER *penv = Headers[nins];
if (peh->flags & 1) penv->dwFlags |= ENV_VOLUME;
if (peh->flags & 2) penv->dwFlags |= ENV_VOLSUSTAIN;
if (peh->flags & 4) penv->dwFlags |= ENV_VOLLOOP;
penv->nVolEnv = peh->numpoints + 1;
if (penv->nVolEnv > MAX_ENVPOINTS) penv->nVolEnv = MAX_ENVPOINTS;
penv->nVolLoopStart = peh->loopbegin;
penv->nVolLoopEnd = peh->loopend;
penv->nVolSustainBegin = penv->nVolSustainEnd = peh->sustain1;
for (UINT i=0; i<penv->nVolEnv; i++)
{
penv->VolPoints[i] = bswapBE16(peh->volenv[i*2]);
penv->VolEnv[i] = (BYTE)bswapBE16(peh->volenv[i*2+1]);
}
}
chunk_pos += sizeof(DBMENVELOPE);
}
} else
// Packed Pattern Data
if (chunk_id == DBM_ID_PATT)
{
if (nPatterns > MAX_PATTERNS) nPatterns = MAX_PATTERNS;
for (UINT iPat=0; iPat<nPatterns; iPat++)
{
DBMPATTERN *pph;
DWORD pksize;
UINT nRows;
if (chunk_pos + sizeof(DBMPATTERN) > dwMemPos) break;
pph = (DBMPATTERN *)(lpStream+chunk_pos);
pksize = bswapBE32(pph->packedsize);
if ((chunk_pos + pksize + 6 > dwMemPos) || (pksize > dwMemPos)) break;
nRows = bswapBE16(pph->rows);
if ((nRows >= 4) && (nRows <= 256))
{
MODCOMMAND *m = AllocatePattern(nRows, m_nChannels);
if (m)
{
LPBYTE pkdata = (LPBYTE)&pph->patterndata;
UINT row = 0;
UINT i = 0;
PatternSize[iPat] = nRows;
Patterns[iPat] = m;
while ((i+3<pksize) && (row < nRows))
{
UINT ch = pkdata[i++];
if (ch)
{
BYTE b = pkdata[i++];
ch--;
if (ch < m_nChannels)
{
if (b & 0x01)
{
UINT note = pkdata[i++];
if (note == 0x1F) note = 0xFF; else
if ((note) && (note < 0xFE))
{
note = ((note >> 4)*12) + (note & 0x0F) + 13;
}
m[ch].note = note;
}
if (b & 0x02) m[ch].instr = pkdata[i++];
if (b & 0x3C)
{
UINT cmd1 = 0xFF, param1 = 0, cmd2 = 0xFF, param2 = 0;
if (b & 0x04) cmd1 = (UINT)pkdata[i++];
if (b & 0x08) param1 = pkdata[i++];
if (b & 0x10) cmd2 = (UINT)pkdata[i++];
if (b & 0x20) param2 = pkdata[i++];
if (cmd1 == 0x0C)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = param1;
cmd1 = 0xFF;
} else
if (cmd2 == 0x0C)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = param2;
cmd2 = 0xFF;
}
if ((cmd1 > 0x13) || ((cmd1 >= 0x10) && (cmd2 < 0x10)))
{
cmd1 = cmd2;
param1 = param2;
cmd2 = 0xFF;
}
if (cmd1 <= 0x13)
{
m[ch].command = cmd1;
m[ch].param = param1;
ConvertModCommand(&m[ch]);
}
}
} else
{
if (b & 0x01) i++;
if (b & 0x02) i++;
if (b & 0x04) i++;
if (b & 0x08) i++;
if (b & 0x10) i++;
if (b & 0x20) i++;
}
} else
{
row++;
m += m_nChannels;
}
}
}
}
chunk_pos += 6 + pksize;
}
} else
// Reading Sample Data
if (chunk_id == DBM_ID_SMPL)
{
if (nSamples >= MAX_SAMPLES) nSamples = MAX_SAMPLES-1;
m_nSamples = nSamples;
for (UINT iSmp=1; iSmp<=nSamples; iSmp++)
{
MODINSTRUMENT *pins;
DBMSAMPLE *psh;
DWORD samplesize;
DWORD sampleflags;
if (chunk_pos + sizeof(DBMSAMPLE) >= dwMemPos) break;
psh = (DBMSAMPLE *)(lpStream+chunk_pos);
chunk_pos += 8;
samplesize = bswapBE32(psh->samplesize);
sampleflags = bswapBE32(psh->flags);
pins = &Ins[iSmp];
pins->nLength = samplesize;
if (sampleflags & 2)
{
pins->uFlags |= CHN_16BIT;
samplesize <<= 1;
}
if ((chunk_pos+samplesize > dwMemPos) || (samplesize > dwMemLength)) break;
if (sampleflags & 3)
{
ReadSample(pins, (pins->uFlags & CHN_16BIT) ? RS_PCM16M : RS_PCM8S,
(LPSTR)(psh->sampledata), samplesize);
}
chunk_pos += samplesize;
}
}
}
return TRUE;
}

606
src/modplug/load_dmf.cpp Normal file
View file

@ -0,0 +1,606 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
///////////////////////////////////////////////////////
// DMF DELUSION DIGITAL MUSIC FILEFORMAT (X-Tracker) //
///////////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#define DMFLOG
//#pragma warning(disable:4244)
#pragma pack(1)
typedef struct DMFHEADER
{
DWORD id; // "DDMF" = 0x464d4444
BYTE version; // 4
CHAR trackername[8]; // "XTRACKER"
CHAR songname[30];
CHAR composer[20];
BYTE date[3];
} DMFHEADER;
typedef struct DMFINFO
{
DWORD id; // "INFO"
DWORD infosize;
} DMFINFO;
typedef struct DMFSEQU
{
DWORD id; // "SEQU"
DWORD seqsize;
WORD loopstart;
WORD loopend;
WORD sequ[2];
} DMFSEQU;
typedef struct DMFPATT
{
DWORD id; // "PATT"
DWORD patsize;
WORD numpat; // 1-1024
BYTE tracks;
BYTE firstpatinfo;
} DMFPATT;
typedef struct DMFTRACK
{
BYTE tracks;
BYTE beat; // [hi|lo] -> hi=ticks per beat, lo=beats per measure
WORD ticks; // max 512
DWORD jmpsize;
} DMFTRACK;
typedef struct DMFSMPI
{
DWORD id;
DWORD size;
BYTE samples;
} DMFSMPI;
typedef struct DMFSAMPLE
{
DWORD len;
DWORD loopstart;
DWORD loopend;
WORD c3speed;
BYTE volume;
BYTE flags;
} DMFSAMPLE;
#pragma pack()
#ifdef DMFLOG
extern void Log(LPCSTR s, ...);
#endif
BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
DMFHEADER *pfh = (DMFHEADER *)lpStream;
DMFINFO *psi;
DMFSEQU *sequ;
DWORD dwMemPos;
BYTE infobyte[32];
BYTE smplflags[MAX_SAMPLES];
if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
if ((pfh->id != 0x464d4444) || (!pfh->version) || (pfh->version & 0xF0)) return FALSE;
dwMemPos = 66;
memcpy(m_szNames[0], pfh->songname, 30);
m_szNames[0][30] = 0;
m_nType = MOD_TYPE_DMF;
m_nChannels = 0;
#ifdef DMFLOG
Log("DMF version %d: \"%s\": %d bytes (0x%04X)\n", pfh->version, m_szNames[0], dwMemLength, dwMemLength);
#endif
while (dwMemPos + 7 < dwMemLength)
{
DWORD id = *((LPDWORD)(lpStream+dwMemPos));
switch(id)
{
// "INFO"
case 0x4f464e49:
// "CMSG"
case 0x47534d43:
psi = (DMFINFO *)(lpStream+dwMemPos);
if (id == 0x47534d43) dwMemPos++;
if ((psi->infosize > dwMemLength) || (psi->infosize + dwMemPos + 8 > dwMemLength)) goto dmfexit;
if ((psi->infosize >= 8) && (!m_lpszSongComments))
{
m_lpszSongComments = new char[psi->infosize]; // changed from CHAR
if (m_lpszSongComments)
{
for (UINT i=0; i<psi->infosize-1; i++)
{
CHAR c = lpStream[dwMemPos+8+i];
if ((i % 40) == 39)
m_lpszSongComments[i] = 0x0d;
else
m_lpszSongComments[i] = (c < ' ') ? ' ' : c;
}
m_lpszSongComments[psi->infosize-1] = 0;
}
}
dwMemPos += psi->infosize + 8 - 1;
break;
// "SEQU"
case 0x55514553:
sequ = (DMFSEQU *)(lpStream+dwMemPos);
if ((sequ->seqsize >= dwMemLength) || (dwMemPos + sequ->seqsize + 12 > dwMemLength)) goto dmfexit;
{
UINT nseq = sequ->seqsize >> 1;
if (nseq >= MAX_ORDERS-1) nseq = MAX_ORDERS-1;
if (sequ->loopstart < nseq) m_nRestartPos = sequ->loopstart;
for (UINT i=0; i<nseq; i++) Order[i] = (BYTE)sequ->sequ[i];
}
dwMemPos += sequ->seqsize + 8;
break;
// "PATT"
case 0x54544150:
if (!m_nChannels)
{
DMFPATT *patt = (DMFPATT *)(lpStream+dwMemPos);
UINT numpat;
DWORD dwPos = dwMemPos + 11;
if ((patt->patsize >= dwMemLength) || (dwMemPos + patt->patsize + 8 > dwMemLength)) goto dmfexit;
numpat = patt->numpat;
if (numpat > MAX_PATTERNS) numpat = MAX_PATTERNS;
m_nChannels = patt->tracks;
if (m_nChannels < patt->firstpatinfo) m_nChannels = patt->firstpatinfo;
if (m_nChannels > 32) m_nChannels = 32;
if (m_nChannels < 4) m_nChannels = 4;
for (UINT npat=0; npat<numpat; npat++)
{
DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos);
#ifdef DMFLOG
Log("Pattern #%d: %d tracks, %d rows\n", npat, pt->tracks, pt->ticks);
#endif
UINT tracks = pt->tracks;
if (tracks > 32) tracks = 32;
UINT ticks = pt->ticks;
if (ticks > 256) ticks = 256;
if (ticks < 16) ticks = 16;
dwPos += 8;
if ((pt->jmpsize >= dwMemLength) || (dwPos + pt->jmpsize + 4 >= dwMemLength)) break;
PatternSize[npat] = (WORD)ticks;
MODCOMMAND *m = AllocatePattern(PatternSize[npat], m_nChannels);
if (!m) goto dmfexit;
Patterns[npat] = m;
DWORD d = dwPos;
dwPos += pt->jmpsize;
UINT ttype = 1;
UINT tempo = 125;
UINT glbinfobyte = 0;
UINT pbeat = (pt->beat & 0xf0) ? pt->beat>>4 : 8;
BOOL tempochange = (pt->beat & 0xf0) ? TRUE : FALSE;
memset(infobyte, 0, sizeof(infobyte));
for (UINT row=0; row<ticks; row++)
{
MODCOMMAND *p = &m[row*m_nChannels];
// Parse track global effects
if (!glbinfobyte)
{
BYTE info = lpStream[d++];
BYTE infoval = 0;
if ((info & 0x80) && (d < dwPos)) glbinfobyte = lpStream[d++];
info &= 0x7f;
if ((info) && (d < dwPos)) infoval = lpStream[d++];
switch(info)
{
case 1: ttype = 0; tempo = infoval; tempochange = TRUE; break;
case 2: ttype = 1; tempo = infoval; tempochange = TRUE; break;
case 3: pbeat = infoval>>4; tempochange = ttype; break;
#ifdef DMFLOG
default: if (info) Log("GLB: %02X.%02X\n", info, infoval);
#endif
}
} else
{
glbinfobyte--;
}
// Parse channels
for (UINT i=0; i<tracks; i++) if (!infobyte[i])
{
MODCOMMAND cmd = {0,0,0,0,0,0};
BYTE info = lpStream[d++];
if (info & 0x80) infobyte[i] = lpStream[d++];
// Instrument
if (info & 0x40)
{
cmd.instr = lpStream[d++];
}
// Note
if (info & 0x20)
{
cmd.note = lpStream[d++];
if ((cmd.note) && (cmd.note < 0xfe)) cmd.note &= 0x7f;
if ((cmd.note) && (cmd.note < 128)) cmd.note += 24;
}
// Volume
if (info & 0x10)
{
cmd.volcmd = VOLCMD_VOLUME;
cmd.vol = (lpStream[d++]+3)>>2;
}
// Effect 1
if (info & 0x08)
{
BYTE efx = lpStream[d++];
BYTE eval = lpStream[d++];
switch(efx)
{
// 1: Key Off
case 1: if (!cmd.note) cmd.note = 0xFE; break;
// 2: Set Loop
// 4: Sample Delay
case 4: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break;
// 5: Retrig
case 5: if (eval&0xe0) { cmd.command = CMD_RETRIG; cmd.param = (eval>>5); } break;
// 6: Offset
case 6: cmd.command = CMD_OFFSET; cmd.param = eval; break;
#ifdef DMFLOG
default: Log("FX1: %02X.%02X\n", efx, eval);
#endif
}
}
// Effect 2
if (info & 0x04)
{
BYTE efx = lpStream[d++];
BYTE eval = lpStream[d++];
switch(efx)
{
// 1: Finetune
case 1: if (eval&0xf0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>4)|0x20; } break;
// 2: Note Delay
case 2: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break;
// 3: Arpeggio
case 3: if (eval) { cmd.command = CMD_ARPEGGIO; cmd.param = eval; } break;
// 4: Portamento Up
case 4: cmd.command = CMD_PORTAMENTOUP; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break;
// 5: Portamento Down
case 5: cmd.command = CMD_PORTAMENTODOWN; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break;
// 6: Tone Portamento
case 6: cmd.command = CMD_TONEPORTAMENTO; cmd.param = eval; break;
// 8: Vibrato
case 8: cmd.command = CMD_VIBRATO; cmd.param = eval; break;
// 12: Note cut
case 12: if (eval & 0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xc0; }
else if (!cmd.note) { cmd.note = 0xfe; } break;
#ifdef DMFLOG
default: Log("FX2: %02X.%02X\n", efx, eval);
#endif
}
}
// Effect 3
if (info & 0x02)
{
BYTE efx = lpStream[d++];
BYTE eval = lpStream[d++];
switch(efx)
{
// 1: Vol Slide Up
case 1: if (eval == 0xff) break;
eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f;
cmd.command = CMD_VOLUMESLIDE; cmd.param = eval<<4; break;
// 2: Vol Slide Down
case 2: if (eval == 0xff) break;
eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f;
cmd.command = CMD_VOLUMESLIDE; cmd.param = eval; break;
// 7: Set Pan
case 7: if (!cmd.volcmd) { cmd.volcmd = VOLCMD_PANNING; cmd.vol = (eval+3)>>2; }
else { cmd.command = CMD_PANNING8; cmd.param = eval; } break;
// 8: Pan Slide Left
case 8: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f;
cmd.command = CMD_PANNINGSLIDE; cmd.param = eval<<4; break;
// 9: Pan Slide Right
case 9: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f;
cmd.command = CMD_PANNINGSLIDE; cmd.param = eval; break;
#ifdef DMFLOG
default: Log("FX3: %02X.%02X\n", efx, eval);
#endif
}
}
// Store effect
if (i < m_nChannels) p[i] = cmd;
if (d > dwPos)
{
#ifdef DMFLOG
Log("Unexpected EOP: row=%d\n", row);
#endif
break;
}
} else
{
infobyte[i]--;
}
// Find free channel for tempo change
if (tempochange)
{
tempochange = FALSE;
UINT speed=6, modtempo=tempo;
UINT rpm = ((ttype) && (pbeat)) ? tempo*pbeat : (tempo+1)*15;
for (speed=30; speed>1; speed--)
{
modtempo = rpm*speed/24;
if (modtempo <= 200) break;
if ((speed < 6) && (modtempo < 256)) break;
}
#ifdef DMFLOG
Log("Tempo change: ttype=%d pbeat=%d tempo=%3d -> speed=%d tempo=%d\n",
ttype, pbeat, tempo, speed, modtempo);
#endif
for (UINT ich=0; ich<m_nChannels; ich++) if (!p[ich].command)
{
if (speed)
{
p[ich].command = CMD_SPEED;
p[ich].param = (BYTE)speed;
speed = 0;
} else
if ((modtempo >= 32) && (modtempo < 256))
{
p[ich].command = CMD_TEMPO;
p[ich].param = (BYTE)modtempo;
modtempo = 0;
} else
{
break;
}
}
}
if (d >= dwPos) break;
}
#ifdef DMFLOG
Log(" %d/%d bytes remaining\n", dwPos-d, pt->jmpsize);
#endif
if (dwPos + 8 >= dwMemLength) break;
}
dwMemPos += patt->patsize + 8;
}
break;
// "SMPI": Sample Info
case 0x49504d53:
{
DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos);
if (pds->size <= dwMemLength - dwMemPos)
{
DWORD dwPos = dwMemPos + 9;
m_nSamples = pds->samples;
if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1;
for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++)
{
UINT namelen = lpStream[dwPos];
smplflags[iSmp] = 0;
if (dwPos+namelen+1+sizeof(DMFSAMPLE) > dwMemPos+pds->size+8) break;
if (namelen)
{
UINT rlen = (namelen < 32) ? namelen : 31;
memcpy(m_szNames[iSmp], lpStream+dwPos+1, rlen);
m_szNames[iSmp][rlen] = 0;
}
dwPos += namelen + 1;
DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos);
MODINSTRUMENT *psmp = &Ins[iSmp];
psmp->nLength = psh->len;
psmp->nLoopStart = psh->loopstart;
psmp->nLoopEnd = psh->loopend;
psmp->nC4Speed = psh->c3speed;
psmp->nGlobalVol = 64;
psmp->nVolume = (psh->volume) ? ((WORD)psh->volume)+1 : (WORD)256;
psmp->uFlags = (psh->flags & 2) ? CHN_16BIT : 0;
if (psmp->uFlags & CHN_16BIT) psmp->nLength >>= 1;
if (psh->flags & 1) psmp->uFlags |= CHN_LOOP;
smplflags[iSmp] = psh->flags;
dwPos += (pfh->version < 8) ? 22 : 30;
#ifdef DMFLOG
Log("SMPI %d/%d: len=%d flags=0x%02X\n", iSmp, m_nSamples, psmp->nLength, psh->flags);
#endif
}
}
dwMemPos += pds->size + 8;
}
break;
// "SMPD": Sample Data
case 0x44504d53:
{
DWORD dwPos = dwMemPos + 8;
UINT ismpd = 0;
for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++)
{
ismpd++;
DWORD pksize;
if (dwPos + 4 >= dwMemLength)
{
#ifdef DMFLOG
Log("Unexpected EOF at sample %d/%d! (pos=%d)\n", iSmp, m_nSamples, dwPos);
#endif
break;
}
pksize = *((LPDWORD)(lpStream+dwPos));
#ifdef DMFLOG
Log("sample %d: pos=0x%X pksize=%d ", iSmp, dwPos, pksize);
Log("len=%d flags=0x%X [%08X]\n", Ins[iSmp].nLength, smplflags[ismpd], *((LPDWORD)(lpStream+dwPos+4)));
#endif
dwPos += 4;
if (pksize > dwMemLength - dwPos)
{
#ifdef DMFLOG
Log("WARNING: pksize=%d, but only %d bytes left\n", pksize, dwMemLength-dwPos);
#endif
pksize = dwMemLength - dwPos;
}
if ((pksize) && (iSmp <= m_nSamples))
{
UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S;
if (smplflags[ismpd] & 4) flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_DMF16 : RS_DMF8;
ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwPos), pksize);
}
dwPos += pksize;
}
dwMemPos = dwPos;
}
break;
// "ENDE": end of file
case 0x45444e45:
goto dmfexit;
// Unrecognized id, or "ENDE" field
default:
dwMemPos += 4;
break;
}
}
dmfexit:
if (!m_nChannels)
{
if (!m_nSamples)
{
m_nType = MOD_TYPE_NONE;
return FALSE;
}
m_nChannels = 4;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////
// DMF Compression
#pragma pack(1)
typedef struct DMF_HNODE
{
short int left, right;
BYTE value;
} DMF_HNODE;
typedef struct DMF_HTREE
{
LPBYTE ibuf, ibufmax;
DWORD bitbuf;
UINT bitnum;
UINT lastnode, nodecount;
DMF_HNODE nodes[256];
} DMF_HTREE;
#pragma pack()
// DMF Huffman ReadBits
BYTE DMFReadBits(DMF_HTREE *tree, UINT nbits)
//-------------------------------------------
{
BYTE x = 0, bitv = 1;
while (nbits--)
{
if (tree->bitnum)
{
tree->bitnum--;
} else
{
tree->bitbuf = (tree->ibuf < tree->ibufmax) ? *(tree->ibuf++) : 0;
tree->bitnum = 7;
}
if (tree->bitbuf & 1) x |= bitv;
bitv <<= 1;
tree->bitbuf >>= 1;
}
return x;
}
//
// tree: [8-bit value][12-bit index][12-bit index] = 32-bit
//
void DMFNewNode(DMF_HTREE *tree)
//------------------------------
{
BYTE isleft, isright;
UINT actnode;
actnode = tree->nodecount;
if (actnode > 255) return;
tree->nodes[actnode].value = DMFReadBits(tree, 7);
isleft = DMFReadBits(tree, 1);
isright = DMFReadBits(tree, 1);
actnode = tree->lastnode;
if (actnode > 255) return;
tree->nodecount++;
tree->lastnode = tree->nodecount;
if (isleft)
{
tree->nodes[actnode].left = tree->lastnode;
DMFNewNode(tree);
} else
{
tree->nodes[actnode].left = -1;
}
tree->lastnode = tree->nodecount;
if (isright)
{
tree->nodes[actnode].right = tree->lastnode;
DMFNewNode(tree);
} else
{
tree->nodes[actnode].right = -1;
}
}
int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen)
//----------------------------------------------------------------------
{
DMF_HTREE tree;
UINT actnode;
BYTE value, sign, delta = 0;
memset(&tree, 0, sizeof(tree));
tree.ibuf = ibuf;
tree.ibufmax = ibufmax;
DMFNewNode(&tree);
value = 0;
for (UINT i=0; i<maxlen; i++)
{
actnode = 0;
sign = DMFReadBits(&tree, 1);
do
{
if (DMFReadBits(&tree, 1))
actnode = tree.nodes[actnode].right;
else
actnode = tree.nodes[actnode].left;
if (actnode > 255) break;
delta = tree.nodes[actnode].value;
if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum)) break;
} while ((tree.nodes[actnode].left >= 0) && (tree.nodes[actnode].right >= 0));
if (sign) delta ^= 0xFF;
value += delta;
psample[i] = (i) ? value : 0;
}
#ifdef DMFLOG
// Log("DMFUnpack: %d remaining bytes\n", tree.ibufmax-tree.ibuf);
#endif
return int(tree.ibuf - ibuf);
}

236
src/modplug/load_dsm.cpp Normal file
View file

@ -0,0 +1,236 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
//////////////////////////////////////////////
// DSIK Internal Format (DSM) module loader //
//////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
#pragma pack(1)
#define DSMID_RIFF 0x46464952 // "RIFF"
#define DSMID_DSMF 0x464d5344 // "DSMF"
#define DSMID_SONG 0x474e4f53 // "SONG"
#define DSMID_INST 0x54534e49 // "INST"
#define DSMID_PATT 0x54544150 // "PATT"
typedef struct DSMNOTE
{
BYTE note,ins,vol,cmd,inf;
} DSMNOTE;
typedef struct DSMINST
{
DWORD id_INST;
DWORD inst_len;
CHAR filename[13];
BYTE flags;
BYTE flags2;
BYTE volume;
DWORD length;
DWORD loopstart;
DWORD loopend;
DWORD reserved1;
WORD c2spd;
WORD reserved2;
CHAR samplename[28];
} DSMINST;
typedef struct DSMFILEHEADER
{
DWORD id_RIFF; // "RIFF"
DWORD riff_len;
DWORD id_DSMF; // "DSMF"
DWORD id_SONG; // "SONG"
DWORD song_len;
} DSMFILEHEADER;
typedef struct DSMSONG
{
CHAR songname[28];
WORD reserved1;
WORD flags;
DWORD reserved2;
WORD numord;
WORD numsmp;
WORD numpat;
WORD numtrk;
BYTE globalvol;
BYTE mastervol;
BYTE speed;
BYTE bpm;
BYTE panpos[16];
BYTE orders[128];
} DSMSONG;
typedef struct DSMPATT
{
DWORD id_PATT;
DWORD patt_len;
BYTE dummy1;
BYTE dummy2;
} DSMPATT;
#pragma pack()
BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
DSMFILEHEADER *pfh = (DSMFILEHEADER *)lpStream;
DSMSONG *psong;
DWORD dwMemPos;
UINT nPat, nSmp;
if ((!lpStream) || (dwMemLength < 1024) || (pfh->id_RIFF != DSMID_RIFF)
|| (pfh->riff_len + 8 > dwMemLength) || (pfh->riff_len < 1024)
|| (pfh->id_DSMF != DSMID_DSMF) || (pfh->id_SONG != DSMID_SONG)
|| (pfh->song_len > dwMemLength)) return FALSE;
psong = (DSMSONG *)(lpStream + sizeof(DSMFILEHEADER));
dwMemPos = sizeof(DSMFILEHEADER) + pfh->song_len;
m_nType = MOD_TYPE_DSM;
m_nChannels = psong->numtrk;
if (m_nChannels < 4) m_nChannels = 4;
if (m_nChannels > 16) m_nChannels = 16;
m_nSamples = psong->numsmp;
if (m_nSamples > MAX_SAMPLES) m_nSamples = MAX_SAMPLES;
m_nDefaultSpeed = psong->speed;
m_nDefaultTempo = psong->bpm;
m_nDefaultGlobalVolume = psong->globalvol << 2;
if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256;
m_nSongPreAmp = psong->mastervol & 0x7F;
for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++)
{
Order[iOrd] = (BYTE)((iOrd < psong->numord) ? psong->orders[iOrd] : 0xFF);
}
for (UINT iPan=0; iPan<16; iPan++)
{
ChnSettings[iPan].nPan = 0x80;
if (psong->panpos[iPan] <= 0x80)
{
ChnSettings[iPan].nPan = psong->panpos[iPan] << 1;
}
}
memcpy(m_szNames[0], psong->songname, 28);
nPat = 0;
nSmp = 1;
while (dwMemPos < dwMemLength - 8)
{
DSMPATT *ppatt = (DSMPATT *)(lpStream + dwMemPos);
DSMINST *pins = (DSMINST *)(lpStream+dwMemPos);
// Reading Patterns
if (ppatt->id_PATT == DSMID_PATT)
{
dwMemPos += 8;
if (dwMemPos + ppatt->patt_len >= dwMemLength) break;
DWORD dwPos = dwMemPos;
dwMemPos += ppatt->patt_len;
MODCOMMAND *m = AllocatePattern(64, m_nChannels);
if (!m) break;
PatternSize[nPat] = 64;
Patterns[nPat] = m;
UINT row = 0;
while ((row < 64) && (dwPos + 2 <= dwMemPos))
{
UINT flag = lpStream[dwPos++];
if (flag)
{
UINT ch = (flag & 0x0F) % m_nChannels;
if (flag & 0x80)
{
UINT note = lpStream[dwPos++];
if (note)
{
if (note <= 12*9) note += 12;
m[ch].note = (BYTE)note;
}
}
if (flag & 0x40)
{
m[ch].instr = lpStream[dwPos++];
}
if (flag & 0x20)
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = lpStream[dwPos++];
}
if (flag & 0x10)
{
UINT command = lpStream[dwPos++];
UINT param = lpStream[dwPos++];
switch(command)
{
// 4-bit Panning
case 0x08:
switch(param & 0xF0)
{
case 0x00: param <<= 4; break;
case 0x10: command = 0x0A; param = (param & 0x0F) << 4; break;
case 0x20: command = 0x0E; param = (param & 0x0F) | 0xA0; break;
case 0x30: command = 0x0E; param = (param & 0x0F) | 0x10; break;
case 0x40: command = 0x0E; param = (param & 0x0F) | 0x20; break;
default: command = 0;
}
break;
// Portamentos
case 0x11:
case 0x12:
command &= 0x0F;
break;
// 3D Sound (?)
case 0x13:
command = 'X' - 55;
param = 0x91;
break;
default:
// Volume + Offset (?)
command = ((command & 0xF0) == 0x20) ? 0x09 : 0;
}
m[ch].command = (BYTE)command;
m[ch].param = (BYTE)param;
if (command) ConvertModCommand(&m[ch]);
}
} else
{
m += m_nChannels;
row++;
}
}
nPat++;
} else
// Reading Samples
if ((nSmp <= m_nSamples) && (pins->id_INST == DSMID_INST))
{
if (dwMemPos + pins->inst_len >= dwMemLength - 8) break;
DWORD dwPos = dwMemPos + sizeof(DSMINST);
dwMemPos += 8 + pins->inst_len;
memcpy(m_szNames[nSmp], pins->samplename, 28);
MODINSTRUMENT *psmp = &Ins[nSmp];
memcpy(psmp->name, pins->filename, 13);
psmp->nGlobalVol = 64;
psmp->nC4Speed = pins->c2spd;
psmp->uFlags = (WORD)((pins->flags & 1) ? CHN_LOOP : 0);
psmp->nLength = pins->length;
psmp->nLoopStart = pins->loopstart;
psmp->nLoopEnd = pins->loopend;
psmp->nVolume = (WORD)(pins->volume << 2);
if (psmp->nVolume > 256) psmp->nVolume = 256;
UINT smptype = (pins->flags & 2) ? RS_PCM8S : RS_PCM8U;
ReadSample(psmp, smptype, (LPCSTR)(lpStream+dwPos), dwMemLength - dwPos);
nSmp++;
} else
{
break;
}
}
return TRUE;
}

270
src/modplug/load_far.cpp Normal file
View file

@ -0,0 +1,270 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
////////////////////////////////////////
// Farandole (FAR) module loader //
////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
#define FARFILEMAGIC 0xFE524146 // "FAR"
#pragma pack(1)
typedef struct FARHEADER1
{
DWORD id; // file magic FAR=
CHAR songname[40]; // songname
CHAR magic2[3]; // 13,10,26
WORD headerlen; // remaining length of header in bytes
BYTE version; // 0xD1
BYTE onoff[16];
BYTE edit1[9];
BYTE speed;
BYTE panning[16];
BYTE edit2[4];
WORD stlen;
} FARHEADER1;
typedef struct FARHEADER2
{
BYTE orders[256];
BYTE numpat;
BYTE snglen;
BYTE loopto;
WORD patsiz[256];
} FARHEADER2;
typedef struct FARSAMPLE
{
CHAR samplename[32];
DWORD length;
BYTE finetune;
BYTE volume;
DWORD reppos;
DWORD repend;
BYTE type;
BYTE loop;
} FARSAMPLE;
#pragma pack()
BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
FARHEADER1 *pmh1 = (FARHEADER1 *)lpStream;
FARHEADER2 *pmh2;
DWORD dwMemPos = sizeof(FARHEADER1);
UINT headerlen;
BYTE samplemap[8];
if ((!lpStream) || (dwMemLength < 1024) || (bswapLE32(pmh1->id) != FARFILEMAGIC)
|| (pmh1->magic2[0] != 13) || (pmh1->magic2[1] != 10) || (pmh1->magic2[2] != 26)) return FALSE;
headerlen = bswapLE16(pmh1->headerlen);
pmh1->stlen = bswapLE16( pmh1->stlen ); /* inplace byteswap -- Toad */
if ((headerlen >= dwMemLength) || (dwMemPos + pmh1->stlen + sizeof(FARHEADER2) >= dwMemLength)) return FALSE;
// Globals
m_nType = MOD_TYPE_FAR;
m_nChannels = 16;
m_nInstruments = 0;
m_nSamples = 0;
m_nSongPreAmp = 0x20;
m_nDefaultSpeed = pmh1->speed;
m_nDefaultTempo = 80;
m_nDefaultGlobalVolume = 256;
memcpy(m_szNames[0], pmh1->songname, 32);
// Channel Setting
for (UINT nchpan=0; nchpan<16; nchpan++)
{
ChnSettings[nchpan].dwFlags = 0;
ChnSettings[nchpan].nPan = ((pmh1->panning[nchpan] & 0x0F) << 4) + 8;
ChnSettings[nchpan].nVolume = 64;
}
// Reading comment
if (pmh1->stlen)
{
UINT szLen = pmh1->stlen;
if (szLen > dwMemLength - dwMemPos) szLen = dwMemLength - dwMemPos;
if ((m_lpszSongComments = new char[szLen + 1]) != NULL)
{
memcpy(m_lpszSongComments, lpStream+dwMemPos, szLen);
m_lpszSongComments[szLen] = 0;
}
dwMemPos += pmh1->stlen;
}
// Reading orders
pmh2 = (FARHEADER2 *)(lpStream + dwMemPos);
dwMemPos += sizeof(FARHEADER2);
if (dwMemPos >= dwMemLength) return TRUE;
for (UINT iorder=0; iorder<MAX_ORDERS; iorder++)
{
Order[iorder] = (iorder <= pmh2->snglen) ? pmh2->orders[iorder] : 0xFF;
}
m_nRestartPos = pmh2->loopto;
// Reading Patterns
dwMemPos += headerlen - (869 + pmh1->stlen);
if (dwMemPos >= dwMemLength) return TRUE;
// byteswap pattern data -- Toad
UINT psfix = 0 ;
while( psfix++ < 256 )
{
pmh2->patsiz[psfix] = bswapLE16( pmh2->patsiz[psfix] ) ;
}
// end byteswap of pattern data
WORD *patsiz = (WORD *)pmh2->patsiz;
for (UINT ipat=0; ipat<256; ipat++) if (patsiz[ipat])
{
UINT patlen = patsiz[ipat];
if ((ipat >= MAX_PATTERNS) || (patsiz[ipat] < 2))
{
dwMemPos += patlen;
continue;
}
if (dwMemPos + patlen >= dwMemLength) return TRUE;
UINT rows = (patlen - 2) >> 6;
if (!rows)
{
dwMemPos += patlen;
continue;
}
if (rows > 256) rows = 256;
if (rows < 16) rows = 16;
PatternSize[ipat] = rows;
if ((Patterns[ipat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE;
MODCOMMAND *m = Patterns[ipat];
UINT patbrk = lpStream[dwMemPos];
const BYTE *p = lpStream + dwMemPos + 2;
UINT max = rows*16*4;
if (max > patlen-2) max = patlen-2;
for (UINT len=0; len<max; len += 4, m++)
{
BYTE note = p[len];
BYTE ins = p[len+1];
BYTE vol = p[len+2];
BYTE eff = p[len+3];
if (note)
{
m->instr = ins + 1;
m->note = note + 36;
}
if (vol & 0x0F)
{
m->volcmd = VOLCMD_VOLUME;
m->vol = (vol & 0x0F) << 2;
if (m->vol <= 4) m->vol = 0;
}
switch(eff & 0xF0)
{
// 1.x: Portamento Up
case 0x10:
m->command = CMD_PORTAMENTOUP;
m->param = eff & 0x0F;
break;
// 2.x: Portamento Down
case 0x20:
m->command = CMD_PORTAMENTODOWN;
m->param = eff & 0x0F;
break;
// 3.x: Tone-Portamento
case 0x30:
m->command = CMD_TONEPORTAMENTO;
m->param = (eff & 0x0F) << 2;
break;
// 4.x: Retrigger
case 0x40:
m->command = CMD_RETRIG;
m->param = 6 / (1+(eff&0x0F)) + 1;
break;
// 5.x: Set Vibrato Depth
case 0x50:
m->command = CMD_VIBRATO;
m->param = (eff & 0x0F);
break;
// 6.x: Set Vibrato Speed
case 0x60:
m->command = CMD_VIBRATO;
m->param = (eff & 0x0F) << 4;
break;
// 7.x: Vol Slide Up
case 0x70:
m->command = CMD_VOLUMESLIDE;
m->param = (eff & 0x0F) << 4;
break;
// 8.x: Vol Slide Down
case 0x80:
m->command = CMD_VOLUMESLIDE;
m->param = (eff & 0x0F);
break;
// A.x: Port to vol
case 0xA0:
m->volcmd = VOLCMD_VOLUME;
m->vol = ((eff & 0x0F) << 2) + 4;
break;
// B.x: Set Balance
case 0xB0:
m->command = CMD_PANNING8;
m->param = (eff & 0x0F) << 4;
break;
// F.x: Set Speed
case 0xF0:
m->command = CMD_SPEED;
m->param = eff & 0x0F;
break;
default:
if ((patbrk) && (patbrk+1 == (len >> 6)) && (patbrk+1 != rows-1))
{
m->command = CMD_PATTERNBREAK;
patbrk = 0;
}
}
}
dwMemPos += patlen;
}
// Reading samples
if (dwMemPos + 8 >= dwMemLength) return TRUE;
memcpy(samplemap, lpStream+dwMemPos, 8);
dwMemPos += 8;
MODINSTRUMENT *pins = &Ins[1];
for (UINT ismp=0; ismp<64; ismp++, pins++) if (samplemap[ismp >> 3] & (1 << (ismp & 7)))
{
if (dwMemPos + sizeof(FARSAMPLE) > dwMemLength) return TRUE;
FARSAMPLE *pfs = (FARSAMPLE *)(lpStream + dwMemPos);
dwMemPos += sizeof(FARSAMPLE);
m_nSamples = ismp + 1;
memcpy(m_szNames[ismp+1], pfs->samplename, 32);
pfs->length = bswapLE32( pfs->length ) ; /* endian fix - Toad */
pins->nLength = pfs->length ;
pins->nLoopStart = bswapLE32(pfs->reppos) ;
pins->nLoopEnd = bswapLE32(pfs->repend) ;
pins->nFineTune = 0;
pins->nC4Speed = 8363*2;
pins->nGlobalVol = 64;
pins->nVolume = pfs->volume << 4;
pins->uFlags = 0;
if ((pins->nLength > 3) && (dwMemPos + 4 < dwMemLength))
{
if (pfs->type & 1)
{
pins->uFlags |= CHN_16BIT;
pins->nLength >>= 1;
pins->nLoopStart >>= 1;
pins->nLoopEnd >>= 1;
}
if ((pfs->loop & 8) && (pins->nLoopEnd > 4)) pins->uFlags |= CHN_LOOP;
ReadSample(pins, (pins->uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S,
(LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
}
dwMemPos += pfs->length;
}
return TRUE;
}

1511
src/modplug/load_it.cpp Normal file

File diff suppressed because it is too large Load diff

15
src/modplug/load_j2b.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
///////////////////////////////////////////////////
//
// J2B module loader
//
///////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"

503
src/modplug/load_mdl.cpp Normal file
View file

@ -0,0 +1,503 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
//////////////////////////////////////////////
// DigiTracker (MDL) module loader //
//////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
typedef struct MDLSONGHEADER
{
DWORD id; // "DMDL" = 0x4C444D44
BYTE version;
} MDLSONGHEADER;
typedef struct MDLINFOBLOCK
{
CHAR songname[32];
CHAR composer[20];
WORD norders;
WORD repeatpos;
BYTE globalvol;
BYTE speed;
BYTE tempo;
BYTE channelinfo[32];
BYTE seq[256];
} MDLINFOBLOCK;
typedef struct MDLPATTERNDATA
{
BYTE channels;
BYTE lastrow; // nrows = lastrow+1
CHAR name[16];
WORD data[1];
} MDLPATTERNDATA;
void ConvertMDLCommand(MODCOMMAND *m, UINT eff, UINT data)
//--------------------------------------------------------
{
UINT command = 0, param = data;
switch(eff)
{
case 0x01: command = CMD_PORTAMENTOUP; break;
case 0x02: command = CMD_PORTAMENTODOWN; break;
case 0x03: command = CMD_TONEPORTAMENTO; break;
case 0x04: command = CMD_VIBRATO; break;
case 0x05: command = CMD_ARPEGGIO; break;
case 0x07: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break;
case 0x08: command = CMD_PANNING8; param <<= 1; break;
case 0x0B: command = CMD_POSITIONJUMP; break;
case 0x0C: command = CMD_GLOBALVOLUME; break;
case 0x0D: command = CMD_PATTERNBREAK; param = (data & 0x0F) + (data>>4)*10; break;
case 0x0E:
command = CMD_S3MCMDEX;
switch(data & 0xF0)
{
case 0x00: command = 0; break; // What is E0x in MDL (there is a bunch) ?
case 0x10: if (param & 0x0F) { param |= 0xF0; command = CMD_PANNINGSLIDE; } else command = 0; break;
case 0x20: if (param & 0x0F) { param = (param << 4) | 0x0F; command = CMD_PANNINGSLIDE; } else command = 0; break;
case 0x30: param = (data & 0x0F) | 0x10; break; // glissando
case 0x40: param = (data & 0x0F) | 0x30; break; // vibrato waveform
case 0x60: param = (data & 0x0F) | 0xB0; break;
case 0x70: param = (data & 0x0F) | 0x40; break; // tremolo waveform
case 0x90: command = CMD_RETRIG; param &= 0x0F; break;
case 0xA0: param = (data & 0x0F) << 4; command = CMD_GLOBALVOLSLIDE; break;
case 0xB0: param = data & 0x0F; command = CMD_GLOBALVOLSLIDE; break;
case 0xF0: param = ((data >> 8) & 0x0F) | 0xA0; break;
}
break;
case 0x0F: command = CMD_SPEED; break;
case 0x10: if ((param & 0xF0) != 0xE0) { command = CMD_VOLUMESLIDE; if ((param & 0xF0) == 0xF0) param = ((param << 4) | 0x0F); else param >>= 2; } break;
case 0x20: if ((param & 0xF0) != 0xE0) { command = CMD_VOLUMESLIDE; if ((param & 0xF0) != 0xF0) param >>= 2; } break;
case 0x30: command = CMD_RETRIG; break;
case 0x40: command = CMD_TREMOLO; break;
case 0x50: command = CMD_TREMOR; break;
case 0xEF: if (param > 0xFF) param = 0xFF; command = CMD_OFFSET; break;
}
if (command)
{
m->command = command;
m->param = param;
}
}
void UnpackMDLTrack(MODCOMMAND *pat, UINT nChannels, UINT nRows, UINT nTrack, const BYTE *lpTracks)
//-------------------------------------------------------------------------------------------------
{
MODCOMMAND cmd, *m = pat;
UINT len = *((WORD *)lpTracks);
UINT pos = 0, row = 0, i;
lpTracks += 2;
for (UINT ntrk=1; ntrk<nTrack; ntrk++)
{
lpTracks += len;
len = *((WORD *)lpTracks);
lpTracks += 2;
}
cmd.note = cmd.instr = 0;
cmd.volcmd = cmd.vol = 0;
cmd.command = cmd.param = 0;
while ((row < nRows) && (pos < len))
{
UINT xx;
BYTE b = lpTracks[pos++];
xx = b >> 2;
switch(b & 0x03)
{
case 0x01:
for (i=0; i<=xx; i++)
{
if (row) *m = *(m-nChannels);
m += nChannels;
row++;
if (row >= nRows) break;
}
break;
case 0x02:
if (xx < row) *m = pat[nChannels*xx];
m += nChannels;
row++;
break;
case 0x03:
{
cmd.note = (xx & 0x01) ? lpTracks[pos++] : 0;
cmd.instr = (xx & 0x02) ? lpTracks[pos++] : 0;
cmd.volcmd = cmd.vol = 0;
cmd.command = cmd.param = 0;
if ((cmd.note < 120-12) && (cmd.note)) cmd.note += 12;
UINT volume = (xx & 0x04) ? lpTracks[pos++] : 0;
UINT commands = (xx & 0x08) ? lpTracks[pos++] : 0;
UINT command1 = commands & 0x0F;
UINT command2 = commands & 0xF0;
UINT param1 = (xx & 0x10) ? lpTracks[pos++] : 0;
UINT param2 = (xx & 0x20) ? lpTracks[pos++] : 0;
if ((command1 == 0x0E) && ((param1 & 0xF0) == 0xF0) && (!command2))
{
param1 = ((param1 & 0x0F) << 8) | param2;
command1 = 0xEF;
command2 = param2 = 0;
}
if (volume)
{
cmd.volcmd = VOLCMD_VOLUME;
cmd.vol = (volume+1) >> 2;
}
ConvertMDLCommand(&cmd, command1, param1);
if ((cmd.command != CMD_SPEED)
&& (cmd.command != CMD_TEMPO)
&& (cmd.command != CMD_PATTERNBREAK))
ConvertMDLCommand(&cmd, command2, param2);
*m = cmd;
m += nChannels;
row++;
}
break;
// Empty Slots
default:
row += xx+1;
m += (xx+1)*nChannels;
if (row >= nRows) break;
}
}
}
BOOL CSoundFile::ReadMDL(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
DWORD dwMemPos, dwPos, blocklen, dwTrackPos;
const MDLSONGHEADER *pmsh = (const MDLSONGHEADER *)lpStream;
MDLINFOBLOCK *pmib;
MDLPATTERNDATA *pmpd;
UINT i,j, norders = 0, npatterns = 0, ntracks = 0;
UINT ninstruments = 0, nsamples = 0;
WORD block;
WORD patterntracks[MAX_PATTERNS*32];
BYTE smpinfo[MAX_SAMPLES];
BYTE insvolenv[MAX_INSTRUMENTS];
BYTE inspanenv[MAX_INSTRUMENTS];
LPCBYTE pvolenv, ppanenv, ppitchenv;
UINT nvolenv, npanenv, npitchenv;
if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
if ((pmsh->id != 0x4C444D44) || ((pmsh->version & 0xF0) > 0x10)) return FALSE;
memset(patterntracks, 0, sizeof(patterntracks));
memset(smpinfo, 0, sizeof(smpinfo));
memset(insvolenv, 0, sizeof(insvolenv));
memset(inspanenv, 0, sizeof(inspanenv));
dwMemPos = 5;
dwTrackPos = 0;
pvolenv = ppanenv = ppitchenv = NULL;
nvolenv = npanenv = npitchenv = 0;
m_nSamples = m_nInstruments = 0;
while (dwMemPos+6 < dwMemLength)
{
block = *((WORD *)(lpStream+dwMemPos));
blocklen = *((DWORD *)(lpStream+dwMemPos+2));
dwMemPos += 6;
if (dwMemPos + blocklen > dwMemLength)
{
if (dwMemPos == 11) return FALSE;
break;
}
switch(block)
{
// IN: infoblock
case 0x4E49:
pmib = (MDLINFOBLOCK *)(lpStream+dwMemPos);
memcpy(m_szNames[0], pmib->songname, 32);
norders = pmib->norders;
if (norders > MAX_ORDERS) norders = MAX_ORDERS;
m_nRestartPos = pmib->repeatpos;
m_nDefaultGlobalVolume = pmib->globalvol;
m_nDefaultTempo = pmib->tempo;
m_nDefaultSpeed = pmib->speed;
m_nChannels = 4;
for (i=0; i<32; i++)
{
ChnSettings[i].nVolume = 64;
ChnSettings[i].nPan = (pmib->channelinfo[i] & 0x7F) << 1;
if (pmib->channelinfo[i] & 0x80)
ChnSettings[i].dwFlags |= CHN_MUTE;
else
m_nChannels = i+1;
}
for (j=0; j<norders; j++) Order[j] = pmib->seq[j];
break;
// ME: song message
case 0x454D:
if (blocklen)
{
if (m_lpszSongComments) delete m_lpszSongComments;
m_lpszSongComments = new char[blocklen];
if (m_lpszSongComments)
{
memcpy(m_lpszSongComments, lpStream+dwMemPos, blocklen);
m_lpszSongComments[blocklen-1] = 0;
}
}
break;
// PA: Pattern Data
case 0x4150:
npatterns = lpStream[dwMemPos];
if (npatterns > MAX_PATTERNS) npatterns = MAX_PATTERNS;
dwPos = dwMemPos + 1;
for (i=0; i<npatterns; i++)
{
if (dwPos+18 >= dwMemLength) break;
pmpd = (MDLPATTERNDATA *)(lpStream + dwPos);
if (pmpd->channels > 32) break;
PatternSize[i] = pmpd->lastrow+1;
if (m_nChannels < pmpd->channels) m_nChannels = pmpd->channels;
dwPos += 18 + 2*pmpd->channels;
for (j=0; j<pmpd->channels; j++)
{
patterntracks[i*32+j] = pmpd->data[j];
}
}
break;
// TR: Track Data
case 0x5254:
if (dwTrackPos) break;
ntracks = *((WORD *)(lpStream+dwMemPos));
dwTrackPos = dwMemPos+2;
break;
// II: Instruments
case 0x4949:
ninstruments = lpStream[dwMemPos];
dwPos = dwMemPos+1;
for (i=0; i<ninstruments; i++)
{
UINT nins = lpStream[dwPos];
if ((nins >= MAX_INSTRUMENTS) || (!nins)) break;
if (m_nInstruments < nins) m_nInstruments = nins;
if (!Headers[nins])
{
UINT note = 12;
if ((Headers[nins] = new INSTRUMENTHEADER) == NULL) break;
INSTRUMENTHEADER *penv = Headers[nins];
memset(penv, 0, sizeof(INSTRUMENTHEADER));
memcpy(penv->name, lpStream+dwPos+2, 32);
penv->nGlobalVol = 64;
penv->nPPC = 5*12;
for (j=0; j<lpStream[dwPos+1]; j++)
{
const BYTE *ps = lpStream+dwPos+34+14*j;
while ((note < (UINT)(ps[1]+12)) && (note < 120))
{
penv->NoteMap[note] = note+1;
if (ps[0] < MAX_SAMPLES)
{
int ismp = ps[0];
penv->Keyboard[note] = ps[0];
Ins[ismp].nVolume = ps[2];
Ins[ismp].nPan = ps[4] << 1;
Ins[ismp].nVibType = ps[11];
Ins[ismp].nVibSweep = ps[10];
Ins[ismp].nVibDepth = ps[9];
Ins[ismp].nVibRate = ps[8];
}
penv->nFadeOut = (ps[7] << 8) | ps[6];
if (penv->nFadeOut == 0xFFFF) penv->nFadeOut = 0;
note++;
}
// Use volume envelope ?
if (ps[3] & 0x80)
{
penv->dwFlags |= ENV_VOLUME;
insvolenv[nins] = (ps[3] & 0x3F) + 1;
}
// Use panning envelope ?
if (ps[5] & 0x80)
{
penv->dwFlags |= ENV_PANNING;
inspanenv[nins] = (ps[5] & 0x3F) + 1;
}
}
}
dwPos += 34 + 14*lpStream[dwPos+1];
}
for (j=1; j<=m_nInstruments; j++) if (!Headers[j])
{
Headers[j] = new INSTRUMENTHEADER;
if (Headers[j]) memset(Headers[j], 0, sizeof(INSTRUMENTHEADER));
}
break;
// VE: Volume Envelope
case 0x4556:
if ((nvolenv = lpStream[dwMemPos]) == 0) break;
if (dwMemPos + nvolenv*32 + 1 <= dwMemLength) pvolenv = lpStream + dwMemPos + 1;
break;
// PE: Panning Envelope
case 0x4550:
if ((npanenv = lpStream[dwMemPos]) == 0) break;
if (dwMemPos + npanenv*32 + 1 <= dwMemLength) ppanenv = lpStream + dwMemPos + 1;
break;
// FE: Pitch Envelope
case 0x4546:
if ((npitchenv = lpStream[dwMemPos]) == 0) break;
if (dwMemPos + npitchenv*32 + 1 <= dwMemLength) ppitchenv = lpStream + dwMemPos + 1;
break;
// IS: Sample Infoblock
case 0x5349:
nsamples = lpStream[dwMemPos];
dwPos = dwMemPos+1;
for (i=0; i<nsamples; i++, dwPos += 59)
{
UINT nins = lpStream[dwPos];
if ((nins >= MAX_SAMPLES) || (!nins)) continue;
if (m_nSamples < nins) m_nSamples = nins;
MODINSTRUMENT *pins = &Ins[nins];
memcpy(m_szNames[nins], lpStream+dwPos+1, 32);
memcpy(pins->name, lpStream+dwPos+33, 8);
pins->nC4Speed = *((DWORD *)(lpStream+dwPos+41));
pins->nLength = *((DWORD *)(lpStream+dwPos+45));
pins->nLoopStart = *((DWORD *)(lpStream+dwPos+49));
pins->nLoopEnd = pins->nLoopStart + *((DWORD *)(lpStream+dwPos+53));
if (pins->nLoopEnd > pins->nLoopStart) pins->uFlags |= CHN_LOOP;
pins->nGlobalVol = 64;
if (lpStream[dwPos+58] & 0x01)
{
pins->uFlags |= CHN_16BIT;
pins->nLength >>= 1;
pins->nLoopStart >>= 1;
pins->nLoopEnd >>= 1;
}
if (lpStream[dwPos+58] & 0x02) pins->uFlags |= CHN_PINGPONGLOOP;
smpinfo[nins] = (lpStream[dwPos+58] >> 2) & 3;
}
break;
// SA: Sample Data
case 0x4153:
dwPos = dwMemPos;
for (i=1; i<=m_nSamples; i++) if ((Ins[i].nLength) && (!Ins[i].pSample) && (smpinfo[i] != 3) && (dwPos < dwMemLength))
{
MODINSTRUMENT *pins = &Ins[i];
UINT flags = (pins->uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S;
if (!smpinfo[i])
{
dwPos += ReadSample(pins, flags, (LPSTR)(lpStream+dwPos), dwMemLength - dwPos);
} else
{
DWORD dwLen = *((DWORD *)(lpStream+dwPos));
dwPos += 4;
if ((dwPos+dwLen <= dwMemLength) && (dwLen > 4))
{
flags = (pins->uFlags & CHN_16BIT) ? RS_MDL16 : RS_MDL8;
ReadSample(pins, flags, (LPSTR)(lpStream+dwPos), dwLen);
}
dwPos += dwLen;
}
}
break;
}
dwMemPos += blocklen;
}
// Unpack Patterns
if ((dwTrackPos) && (npatterns) && (m_nChannels) && (ntracks))
{
for (UINT ipat=0; ipat<npatterns; ipat++)
{
if ((Patterns[ipat] = AllocatePattern(PatternSize[ipat], m_nChannels)) == NULL) break;
for (UINT chn=0; chn<m_nChannels; chn++) if ((patterntracks[ipat*32+chn]) && (patterntracks[ipat*32+chn] <= ntracks))
{
MODCOMMAND *m = Patterns[ipat] + chn;
UnpackMDLTrack(m, m_nChannels, PatternSize[ipat], patterntracks[ipat*32+chn], lpStream+dwTrackPos);
}
}
}
// Set up envelopes
for (UINT iIns=1; iIns<=m_nInstruments; iIns++) if (Headers[iIns])
{
INSTRUMENTHEADER *penv = Headers[iIns];
// Setup volume envelope
if ((nvolenv) && (pvolenv) && (insvolenv[iIns]))
{
LPCBYTE pve = pvolenv;
for (UINT nve=0; nve<nvolenv; nve++, pve+=33) if (pve[0]+1 == insvolenv[iIns])
{
WORD vtick = 1;
penv->nVolEnv = 15;
for (UINT iv=0; iv<15; iv++)
{
if (iv) vtick += pve[iv*2+1];
penv->VolPoints[iv] = vtick;
penv->VolEnv[iv] = pve[iv*2+2];
if (!pve[iv*2+1])
{
penv->nVolEnv = iv+1;
break;
}
}
penv->nVolSustainBegin = penv->nVolSustainEnd = pve[31] & 0x0F;
if (pve[31] & 0x10) penv->dwFlags |= ENV_VOLSUSTAIN;
if (pve[31] & 0x20) penv->dwFlags |= ENV_VOLLOOP;
penv->nVolLoopStart = pve[32] & 0x0F;
penv->nVolLoopEnd = pve[32] >> 4;
}
}
// Setup panning envelope
if ((npanenv) && (ppanenv) && (inspanenv[iIns]))
{
LPCBYTE ppe = ppanenv;
for (UINT npe=0; npe<npanenv; npe++, ppe+=33) if (ppe[0]+1 == inspanenv[iIns])
{
WORD vtick = 1;
penv->nPanEnv = 15;
for (UINT iv=0; iv<15; iv++)
{
if (iv) vtick += ppe[iv*2+1];
penv->PanPoints[iv] = vtick;
penv->PanEnv[iv] = ppe[iv*2+2];
if (!ppe[iv*2+1])
{
penv->nPanEnv = iv+1;
break;
}
}
if (ppe[31] & 0x10) penv->dwFlags |= ENV_PANSUSTAIN;
if (ppe[31] & 0x20) penv->dwFlags |= ENV_PANLOOP;
penv->nPanLoopStart = ppe[32] & 0x0F;
penv->nPanLoopEnd = ppe[32] >> 4;
}
}
}
m_dwSongFlags |= SONG_LINEARSLIDES;
m_nType = MOD_TYPE_MDL;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////
// MDL Sample Unpacking
// MDL Huffman ReadBits compression
WORD MDLReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n)
//-----------------------------------------------------------------
{
WORD v = (WORD)(bitbuf & ((1 << n) - 1) );
bitbuf >>= n;
bitnum -= n;
if (bitnum <= 24)
{
bitbuf |= (((DWORD)(*ibuf++)) << bitnum);
bitnum += 8;
}
return v;
}

916
src/modplug/load_med.cpp Normal file
View file

@ -0,0 +1,916 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
#include "stdafx.h"
#include "sndfile.h"
//#define MED_LOG
#ifdef MED_LOG
extern void Log(LPCSTR s, ...);
#endif
//////////////////////////////////////////////////////////
// OctaMed MED file support (import only)
// flags
#define MMD_FLAG_FILTERON 0x1
#define MMD_FLAG_JUMPINGON 0x2
#define MMD_FLAG_JUMP8TH 0x4
#define MMD_FLAG_INSTRSATT 0x8 // instruments are attached (this is a module)
#define MMD_FLAG_VOLHEX 0x10
#define MMD_FLAG_STSLIDE 0x20 // SoundTracker mode for slides
#define MMD_FLAG_8CHANNEL 0x40 // OctaMED 8 channel song
#define MMD_FLAG_SLOWHQ 0x80 // HQ slows playing speed (V2-V4 compatibility)
// flags2
#define MMD_FLAG2_BMASK 0x1F
#define MMD_FLAG2_BPM 0x20
#define MMD_FLAG2_MIX 0x80 // uses Mixing (V7+)
// flags3:
#define MMD_FLAG3_STEREO 0x1 // mixing in Stereo mode
#define MMD_FLAG3_FREEPAN 0x2 // free panning
#define MMD_FLAG3_GM 0x4 // module designed for GM/XG compatibility
// generic MMD tags
#define MMDTAG_END 0
#define MMDTAG_PTR 0x80000000 // data needs relocation
#define MMDTAG_MUSTKNOW 0x40000000 // loader must fail if this isn't recognized
#define MMDTAG_MUSTWARN 0x20000000 // loader must warn if this isn't recognized
// ExpData tags
// # of effect groups, including the global group (will
// override settings in MMDSong struct), default = 1
#define MMDTAG_EXP_NUMFXGROUPS 1
#define MMDTAG_TRK_NAME (MMDTAG_PTR|1) // trackinfo tags
#define MMDTAG_TRK_NAMELEN 2 // namelen includes zero term.
#define MMDTAG_TRK_FXGROUP 3
// effectinfo tags
#define MMDTAG_FX_ECHOTYPE 1
#define MMDTAG_FX_ECHOLEN 2
#define MMDTAG_FX_ECHODEPTH 3
#define MMDTAG_FX_STEREOSEP 4
#define MMDTAG_FX_GROUPNAME (MMDTAG_PTR|5) // the Global Effects group shouldn't have name saved!
#define MMDTAG_FX_GRPNAMELEN 6 // namelen includes zero term.
#pragma pack(1)
typedef struct tagMEDMODULEHEADER
{
DWORD id; // MMD1-MMD3
DWORD modlen; // Size of file
DWORD song; // Position in file for this song
WORD psecnum;
WORD pseq;
DWORD blockarr; // Position in file for blocks
DWORD mmdflags;
DWORD smplarr; // Position in file for samples
DWORD reserved;
DWORD expdata; // Absolute offset in file for ExpData (0 if not present)
DWORD reserved2;
WORD pstate;
WORD pblock;
WORD pline;
WORD pseqnum;
WORD actplayline;
BYTE counter;
BYTE extra_songs; // # of songs - 1
} MEDMODULEHEADER;
typedef struct tagMMD0SAMPLE
{
WORD rep, replen;
BYTE midich;
BYTE midipreset;
BYTE svol;
signed char strans;
} MMD0SAMPLE;
// Sample header is immediately followed by sample data...
typedef struct tagMMDSAMPLEHEADER
{
DWORD length; // length of *one* *unpacked* channel in *bytes*
WORD type;
// if non-negative
// bits 0-3 reserved for multi-octave instruments, not supported on the PC
// 0x10: 16 bit (otherwise 8 bit)
// 0x20: Stereo (otherwise mono)
// 0x40: Uses DeltaCode
// 0x80: Packed data
// -1: Synth
// -2: Hybrid
// if type indicates packed data, these fields follow, otherwise we go right to the data
WORD packtype; // Only 1 = ADPCM is supported
WORD subtype; // Packing subtype
// ADPCM subtype
// 1: g723_40
// 2: g721
// 3: g723_24
BYTE commonflags; // flags common to all packtypes (none defined so far)
BYTE packerflags; // flags for the specific packtype
ULONG leftchlen; // packed length of left channel in bytes
ULONG rightchlen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES)
BYTE SampleData[1]; // Sample Data
} MMDSAMPLEHEADER;
// MMD0/MMD1 song header
typedef struct tagMMD0SONGHEADER
{
MMD0SAMPLE sample[63];
WORD numblocks; // # of blocks
WORD songlen; // # of entries used in playseq
BYTE playseq[256]; // Play sequence
WORD deftempo; // BPM tempo
signed char playtransp; // Play transpose
BYTE flags; // 0x10: Hex Volumes | 0x20: ST/NT/PT Slides | 0x40: 8 Channels song
BYTE flags2; // [b4-b0]+1: Tempo LPB, 0x20: tempo mode, 0x80: mix_conv=on
BYTE tempo2; // tempo TPL
BYTE trkvol[16]; // track volumes
BYTE mastervol; // master volume
BYTE numsamples; // # of samples (max=63)
} MMD0SONGHEADER;
// MMD2/MMD3 song header
typedef struct tagMMD2SONGHEADER
{
MMD0SAMPLE sample[63];
WORD numblocks; // # of blocks
WORD numsections; // # of sections
DWORD playseqtable; // filepos of play sequence
DWORD sectiontable; // filepos of sections table (WORD array)
DWORD trackvols; // filepos of tracks volume (BYTE array)
WORD numtracks; // # of tracks (max 64)
WORD numpseqs; // # of play sequences
DWORD trackpans; // filepos of tracks pan values (BYTE array)
LONG flags3; // 0x1:stereo_mix, 0x2:free_panning, 0x4:GM/XG compatibility
WORD voladj; // vol_adjust (set to 100 if 0)
WORD channels; // # of channels (4 if =0)
BYTE mix_echotype; // 1:normal,2:xecho
BYTE mix_echodepth; // 1..6
WORD mix_echolen; // > 0
signed char mix_stereosep; // -4..4
BYTE pad0[223];
WORD deftempo; // BPM tempo
signed char playtransp; // play transpose
BYTE flags; // 0x1:filteron, 0x2:jumpingon, 0x4:jump8th, 0x8:instr_attached, 0x10:hex_vol, 0x20:PT_slides, 0x40:8ch_conv,0x80:hq slows playing speed
BYTE flags2; // 0x80:mix_conv=on, [b4-b0]+1:tempo LPB, 0x20:tempo_mode
BYTE tempo2; // tempo TPL
BYTE pad1[16];
BYTE mastervol; // master volume
BYTE numsamples; // # of samples (max 63)
} MMD2SONGHEADER;
// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we
// number the bits in each byte 0..7, where 0 is the low bit.
// The note is held as bits 5..0 of byte0
// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1
// The command number is bits 3,2,1,0 of byte1, command data is in byte2:
// For command 0, byte2 represents the second data byte, otherwise byte2
// represents the first data byte.
typedef struct tagMMD0BLOCK
{
BYTE numtracks;
BYTE lines; // File value is 1 less than actual, so 0 -> 1 line
} MMD0BLOCK; // BYTE data[lines+1][tracks][3];
// For MMD1,MMD2,MMD3 the note information is carried in 4 bytes, byte0, byte1,
// byte2 and byte3
// The note is held as byte0 (values above 0x84 are ignored)
// The instrument is held as byte1
// The command number is held as byte2, command data is in byte3
// For commands 0 and 0x19 byte3 represents the second data byte,
// otherwise byte2 represents the first data byte.
typedef struct tagMMD1BLOCK
{
WORD numtracks; // Number of tracks, may be > 64, but then that data is skipped.
WORD lines; // Stored value is 1 less than actual, so 0 -> 1 line
DWORD info; // Offset of BlockInfo (if 0, no block_info is present)
} MMD1BLOCK;
typedef struct tagMMD1BLOCKINFO
{
DWORD hlmask; // Unimplemented - ignore
DWORD blockname; // file offset of block name
DWORD blocknamelen; // length of block name (including term. 0)
DWORD pagetable; // file offset of command page table
DWORD cmdexttable; // file offset of command extension table
DWORD reserved[4]; // future expansion
} MMD1BLOCKINFO;
// A set of play sequences is stored as an array of ULONG files offsets
// Each offset points to the play sequence itself.
typedef struct tagMMD2PLAYSEQ
{
CHAR name[32];
DWORD command_offs; // filepos of command table
DWORD reserved;
WORD length;
WORD seq[512]; // skip if > 0x8000
} MMD2PLAYSEQ;
// A command table contains commands that effect a particular play sequence
// entry. The only commands read in are STOP or POSJUMP, all others are ignored
// POSJUMP is presumed to have extra bytes containing a WORD for the position
typedef struct tagMMDCOMMAND
{
WORD offset; // Offset within current sequence entry
BYTE cmdnumber; // STOP (537) or POSJUMP (538) (others skipped)
BYTE extra_count;
BYTE extra_bytes[4];// [extra_count];
} MMDCOMMAND; // Last entry has offset == 0xFFFF, cmd_number == 0 and 0 extrabytes
typedef struct tagMMD0EXP
{
DWORD nextmod; // File offset of next Hdr
DWORD exp_smp; // Pointer to extra instrument data
WORD s_ext_entries; // Number of extra instrument entries
WORD s_ext_entrsz; // Size of extra instrument data
DWORD annotxt;
DWORD annolen;
DWORD iinfo; // Instrument names
WORD i_ext_entries;
WORD i_ext_entrsz;
DWORD jumpmask;
DWORD rgbtable;
BYTE channelsplit[4]; // Only used if 8ch_conv (extra channel for every nonzero entry)
DWORD n_info;
DWORD songname; // Song name
DWORD songnamelen;
DWORD dumps;
DWORD mmdinfo;
DWORD mmdrexx;
DWORD mmdcmd3x;
DWORD trackinfo_ofs; // ptr to song->numtracks ptrs to tag lists
DWORD effectinfo_ofs; // ptr to group ptrs
DWORD tag_end;
} MMD0EXP;
#pragma pack()
static void MedConvert(MODCOMMAND *p, const MMD0SONGHEADER *pmsh)
//---------------------------------------------------------------
{
const BYTE bpmvals[9] = { 179,164,152,141,131,123,116,110,104};
UINT command = p->command;
UINT param = p->param;
switch(command)
{
case 0x00: if (param) command = CMD_ARPEGGIO; else command = 0; break;
case 0x01: command = CMD_PORTAMENTOUP; break;
case 0x02: command = CMD_PORTAMENTODOWN; break;
case 0x03: command = CMD_TONEPORTAMENTO; break;
case 0x04: command = CMD_VIBRATO; break;
case 0x05: command = CMD_TONEPORTAVOL; break;
case 0x06: command = CMD_VIBRATOVOL; break;
case 0x07: command = CMD_TREMOLO; break;
case 0x0A: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break;
case 0x0B: command = CMD_POSITIONJUMP; break;
case 0x0C: command = CMD_VOLUME;
if (pmsh->flags & MMD_FLAG_VOLHEX)
{
if (param < 0x80)
{
param = (param+1) / 2;
} else command = 0;
} else
{
if (param <= 0x99)
{
param = (param >> 4)*10+((param & 0x0F) % 10);
if (param > 64) param = 64;
} else command = 0;
}
break;
case 0x09: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break;
case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break;
case 0x0F: // Set Tempo / Special
// F.00 = Pattern Break
if (!param) command = CMD_PATTERNBREAK; else
// F.01 - F.F0: Set tempo/speed
if (param <= 0xF0)
{
if (pmsh->flags & MMD_FLAG_8CHANNEL)
{
param = (param > 10) ? 99 : bpmvals[param-1];
} else
// F.01 - F.0A: Set Speed
if (param <= 0x0A)
{
command = CMD_SPEED;
} else
// Old tempo
if (!(pmsh->flags2 & MMD_FLAG2_BPM))
{
param = _muldiv(param, 5*715909, 2*474326);
}
// F.0B - F.F0: Set Tempo (assumes LPB=4)
if (param > 0x0A)
{
command = CMD_TEMPO;
if (param < 0x21) param = 0x21;
if (param > 240) param = 240;
}
} else
switch(param)
{
// F.F1: Retrig 2x
case 0xF1:
command = CMD_MODCMDEX;
param = 0x93;
break;
// F.F2: Note Delay 2x
case 0xF2:
command = CMD_MODCMDEX;
param = 0xD3;
break;
// F.F3: Retrig 3x
case 0xF3:
command = CMD_MODCMDEX;
param = 0x92;
break;
// F.F4: Note Delay 1/3
case 0xF4:
command = CMD_MODCMDEX;
param = 0xD2;
break;
// F.F5: Note Delay 2/3
case 0xF5:
command = CMD_MODCMDEX;
param = 0xD4;
break;
// F.F8: Filter Off
case 0xF8:
command = CMD_MODCMDEX;
param = 0x00;
break;
// F.F9: Filter On
case 0xF9:
command = CMD_MODCMDEX;
param = 0x01;
break;
// F.FD: Very fast tone-portamento
case 0xFD:
command = CMD_TONEPORTAMENTO;
param = 0xFF;
break;
// F.FE: End Song
case 0xFE:
command = CMD_SPEED;
param = 0;
break;
// F.FF: Note Cut
case 0xFF:
command = CMD_MODCMDEX;
param = 0xC0;
break;
default:
#ifdef MED_LOG
Log("Unknown Fxx command: cmd=0x%02X param=0x%02X\n", command, param);
#endif
param = command = 0;
}
break;
// 11.0x: Fine Slide Up
case 0x11:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0x10;
break;
// 12.0x: Fine Slide Down
case 0x12:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0x20;
break;
// 14.xx: Vibrato
case 0x14:
command = CMD_VIBRATO;
break;
// 15.xx: FineTune
case 0x15:
command = CMD_MODCMDEX;
param &= 0x0F;
param |= 0x50;
break;
// 16.xx: Pattern Loop
case 0x16:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0x60;
break;
// 18.xx: Note Cut
case 0x18:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xC0;
break;
// 19.xx: Sample Offset
case 0x19:
command = CMD_OFFSET;
break;
// 1A.0x: Fine Volume Up
case 0x1A:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xA0;
break;
// 1B.0x: Fine Volume Down
case 0x1B:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xB0;
break;
// 1D.xx: Pattern Break
case 0x1D:
command = CMD_PATTERNBREAK;
break;
// 1E.0x: Pattern Delay
case 0x1E:
command = CMD_MODCMDEX;
if (param > 0x0F) param = 0x0F;
param |= 0xE0;
break;
// 1F.xy: Retrig
case 0x1F:
command = CMD_RETRIG;
param &= 0x0F;
break;
// 2E.xx: set panning
case 0x2E:
command = CMD_MODCMDEX;
param = ((param + 0x10) & 0xFF) >> 1;
if (param > 0x0F) param = 0x0F;
param |= 0x80;
break;
default:
#ifdef MED_LOG
// 0x2E ?
Log("Unknown command: cmd=0x%02X param=0x%02X\n", command, param);
#endif
command = param = 0;
}
p->command = command;
p->param = param;
}
BOOL CSoundFile::ReadMed(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
const MEDMODULEHEADER *pmmh;
const MMD0SONGHEADER *pmsh;
const MMD2SONGHEADER *pmsh2;
const MMD0EXP *pmex;
DWORD dwBlockArr, dwSmplArr, dwExpData, wNumBlocks;
LPDWORD pdwTable;
CHAR version;
UINT deftempo;
int playtransp = 0;
if ((!lpStream) || (dwMemLength < 0x200)) return FALSE;
pmmh = (MEDMODULEHEADER *)lpStream;
if (((pmmh->id & 0x00FFFFFF) != 0x444D4D) || (!pmmh->song)) return FALSE;
// Check for 'MMDx'
DWORD dwSong = bswapBE32(pmmh->song);
if ((dwSong >= dwMemLength) || (dwSong + sizeof(MMD0SONGHEADER) >= dwMemLength)) return FALSE;
version = (signed char)((pmmh->id >> 24) & 0xFF);
if ((version < '0') || (version > '3')) return FALSE;
#ifdef MED_LOG
Log("\nLoading MMD%c module (flags=0x%02X)...\n", version, bswapBE32(pmmh->mmdflags));
Log(" modlen = %d\n", bswapBE32(pmmh->modlen));
Log(" song = 0x%08X\n", bswapBE32(pmmh->song));
Log(" psecnum = %d\n", bswapBE16(pmmh->psecnum));
Log(" pseq = %d\n", bswapBE16(pmmh->pseq));
Log(" blockarr = 0x%08X\n", bswapBE32(pmmh->blockarr));
Log(" mmdflags = 0x%08X\n", bswapBE32(pmmh->mmdflags));
Log(" smplarr = 0x%08X\n", bswapBE32(pmmh->smplarr));
Log(" reserved = 0x%08X\n", bswapBE32(pmmh->reserved));
Log(" expdata = 0x%08X\n", bswapBE32(pmmh->expdata));
Log(" reserved2= 0x%08X\n", bswapBE32(pmmh->reserved2));
Log(" pstate = %d\n", bswapBE16(pmmh->pstate));
Log(" pblock = %d\n", bswapBE16(pmmh->pblock));
Log(" pline = %d\n", bswapBE16(pmmh->pline));
Log(" pseqnum = %d\n", bswapBE16(pmmh->pseqnum));
Log(" actplayline=%d\n", bswapBE16(pmmh->actplayline));
Log(" counter = %d\n", pmmh->counter);
Log(" extra_songs = %d\n", pmmh->extra_songs);
Log("\n");
#endif
m_nType = MOD_TYPE_MED;
m_nSongPreAmp = 0x20;
dwBlockArr = bswapBE32(pmmh->blockarr);
dwSmplArr = bswapBE32(pmmh->smplarr);
dwExpData = bswapBE32(pmmh->expdata);
if ((dwExpData) && (dwExpData+sizeof(MMD0EXP) < dwMemLength))
pmex = (MMD0EXP *)(lpStream+dwExpData);
else
pmex = NULL;
pmsh = (MMD0SONGHEADER *)(lpStream + dwSong);
pmsh2 = (MMD2SONGHEADER *)pmsh;
#ifdef MED_LOG
if (version < '2')
{
Log("MMD0 Header:\n");
Log(" numblocks = %d\n", bswapBE16(pmsh->numblocks));
Log(" songlen = %d\n", bswapBE16(pmsh->songlen));
Log(" playseq = ");
for (UINT idbg1=0; idbg1<16; idbg1++) Log("%2d, ", pmsh->playseq[idbg1]);
Log("...\n");
Log(" deftempo = 0x%04X\n", bswapBE16(pmsh->deftempo));
Log(" playtransp = %d\n", (signed char)pmsh->playtransp);
Log(" flags(1,2) = 0x%02X, 0x%02X\n", pmsh->flags, pmsh->flags2);
Log(" tempo2 = %d\n", pmsh->tempo2);
Log(" trkvol = ");
for (UINT idbg2=0; idbg2<16; idbg2++) Log("0x%02X, ", pmsh->trkvol[idbg2]);
Log("...\n");
Log(" mastervol = 0x%02X\n", pmsh->mastervol);
Log(" numsamples = %d\n", pmsh->numsamples);
} else
{
Log("MMD2 Header:\n");
Log(" numblocks = %d\n", bswapBE16(pmsh2->numblocks));
Log(" numsections= %d\n", bswapBE16(pmsh2->numsections));
Log(" playseqptr = 0x%04X\n", bswapBE32(pmsh2->playseqtable));
Log(" sectionptr = 0x%04X\n", bswapBE32(pmsh2->sectiontable));
Log(" trackvols = 0x%04X\n", bswapBE32(pmsh2->trackvols));
Log(" numtracks = %d\n", bswapBE16(pmsh2->numtracks));
Log(" numpseqs = %d\n", bswapBE16(pmsh2->numpseqs));
Log(" trackpans = 0x%04X\n", bswapBE32(pmsh2->trackpans));
Log(" flags3 = 0x%08X\n", bswapBE32(pmsh2->flags3));
Log(" voladj = %d\n", bswapBE16(pmsh2->voladj));
Log(" channels = %d\n", bswapBE16(pmsh2->channels));
Log(" echotype = %d\n", pmsh2->mix_echotype);
Log(" echodepth = %d\n", pmsh2->mix_echodepth);
Log(" echolen = %d\n", bswapBE16(pmsh2->mix_echolen));
Log(" stereosep = %d\n", (signed char)pmsh2->mix_stereosep);
Log(" deftempo = 0x%04X\n", bswapBE16(pmsh2->deftempo));
Log(" playtransp = %d\n", (signed char)pmsh2->playtransp);
Log(" flags(1,2) = 0x%02X, 0x%02X\n", pmsh2->flags, pmsh2->flags2);
Log(" tempo2 = %d\n", pmsh2->tempo2);
Log(" mastervol = 0x%02X\n", pmsh2->mastervol);
Log(" numsamples = %d\n", pmsh->numsamples);
}
Log("\n");
#endif
wNumBlocks = bswapBE16(pmsh->numblocks);
m_nChannels = 4;
m_nSamples = pmsh->numsamples;
if (m_nSamples > 63) m_nSamples = 63;
// Tempo
m_nDefaultTempo = 125;
deftempo = bswapBE16(pmsh->deftempo);
if (!deftempo) deftempo = 125;
if (pmsh->flags2 & MMD_FLAG2_BPM)
{
UINT tempo_tpl = (pmsh->flags2 & MMD_FLAG2_BMASK) + 1;
if (!tempo_tpl) tempo_tpl = 4;
deftempo *= tempo_tpl;
deftempo /= 4;
#ifdef MED_LOG
Log("newtempo: %3d bpm (bpm=%3d lpb=%2d)\n", deftempo, bswapBE16(pmsh->deftempo), (pmsh->flags2 & MMD_FLAG2_BMASK)+1);
#endif
} else
{
deftempo = _muldiv(deftempo, 5*715909, 2*474326);
#ifdef MED_LOG
Log("oldtempo: %3d bpm (bpm=%3d)\n", deftempo, bswapBE16(pmsh->deftempo));
#endif
}
// Speed
m_nDefaultSpeed = pmsh->tempo2;
if (!m_nDefaultSpeed) m_nDefaultSpeed = 6;
if (deftempo < 0x21) deftempo = 0x21;
if (deftempo > 255)
{
while ((m_nDefaultSpeed > 3) && (deftempo > 260))
{
deftempo = (deftempo * (m_nDefaultSpeed - 1)) / m_nDefaultSpeed;
m_nDefaultSpeed--;
}
if (deftempo > 255) deftempo = 255;
}
m_nDefaultTempo = deftempo;
// Reading Samples
for (UINT iSHdr=0; iSHdr<m_nSamples; iSHdr++)
{
MODINSTRUMENT *pins = &Ins[iSHdr+1];
pins->nLoopStart = bswapBE16(pmsh->sample[iSHdr].rep) << 1;
pins->nLoopEnd = pins->nLoopStart + (bswapBE16(pmsh->sample[iSHdr].replen) << 1);
pins->nVolume = (pmsh->sample[iSHdr].svol << 2);
pins->nGlobalVol = 64;
if (pins->nVolume > 256) pins->nVolume = 256;
pins->RelativeTone = -12 * pmsh->sample[iSHdr].strans;
pins->nPan = 128;
if (pins->nLoopEnd) pins->uFlags |= CHN_LOOP;
}
// Common Flags
if (!(pmsh->flags & 0x20)) m_dwSongFlags |= SONG_FASTVOLSLIDES;
// Reading play sequence
if (version < '2')
{
UINT nbo = pmsh->songlen >> 8;
if (nbo >= MAX_ORDERS) nbo = MAX_ORDERS-1;
if (!nbo) nbo = 1;
memcpy(Order, pmsh->playseq, nbo);
playtransp = pmsh->playtransp;
} else
{
UINT nOrders, nSections;
UINT nTrks = bswapBE16(pmsh2->numtracks);
if ((nTrks >= 4) && (nTrks <= 32)) m_nChannels = nTrks;
DWORD playseqtable = bswapBE32(pmsh2->playseqtable);
UINT numplayseqs = bswapBE16(pmsh2->numpseqs);
if (!numplayseqs) numplayseqs = 1;
nOrders = 0;
nSections = bswapBE16(pmsh2->numsections);
DWORD sectiontable = bswapBE32(pmsh2->sectiontable);
if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1;
nOrders = 0;
for (UINT iSection=0; iSection<nSections; iSection++)
{
UINT nplayseq = 0;
if ((sectiontable) && (sectiontable < dwMemLength-2))
{
nplayseq = lpStream[sectiontable+1];
sectiontable += 2; // WORDs
} else
{
nSections = 0;
}
UINT pseq = 0;
if ((playseqtable) && (playseqtable + nplayseq*4 < dwMemLength))
{
pseq = bswapBE32(((LPDWORD)(lpStream+playseqtable))[nplayseq]);
}
if ((pseq) && (pseq < dwMemLength - sizeof(MMD2PLAYSEQ)))
{
MMD2PLAYSEQ *pmps = (MMD2PLAYSEQ *)(lpStream + pseq);
if (!m_szNames[0][0]) memcpy(m_szNames[0], pmps->name, 31);
UINT n = bswapBE16(pmps->length);
if (pseq+n <= dwMemLength)
{
for (UINT i=0; i<n; i++)
{
UINT seqval = pmps->seq[i] >> 8;
if ((seqval < wNumBlocks) && (nOrders < MAX_ORDERS-1))
{
Order[nOrders++] = seqval;
}
}
}
}
}
playtransp = pmsh2->playtransp;
while (nOrders < MAX_ORDERS) Order[nOrders++] = 0xFF;
}
// Reading Expansion structure
if (pmex)
{
// Channel Split
if ((m_nChannels == 4) && (pmsh->flags & 0x40))
{
for (UINT i8ch=0; i8ch<4; i8ch++)
{
if (pmex->channelsplit[i8ch]) m_nChannels++;
}
}
// Song Comments
UINT annotxt = bswapBE32(pmex->annotxt);
UINT annolen = bswapBE32(pmex->annolen);
if ((annotxt) && (annolen) && (annotxt+annolen <= dwMemLength))
{
m_lpszSongComments = new char[annolen+1];
memcpy(m_lpszSongComments, lpStream+annotxt, annolen);
m_lpszSongComments[annolen] = 0;
}
// Song Name
UINT songname = bswapBE32(pmex->songname);
UINT songnamelen = bswapBE32(pmex->songnamelen);
if ((songname) && (songnamelen) && (songname+songnamelen <= dwMemLength))
{
if (songnamelen > 31) songnamelen = 31;
memcpy(m_szNames[0], lpStream+songname, songnamelen);
}
// Sample Names
DWORD smpinfoex = bswapBE32(pmex->iinfo);
if (smpinfoex)
{
DWORD iinfoptr = bswapBE32(pmex->iinfo);
UINT ientries = bswapBE16(pmex->i_ext_entries);
UINT ientrysz = bswapBE16(pmex->i_ext_entrsz);
if ((iinfoptr) && (ientrysz < 256) && (iinfoptr + ientries*ientrysz < dwMemLength))
{
LPCSTR psznames = (LPCSTR)(lpStream + iinfoptr);
UINT maxnamelen = ientrysz;
if (maxnamelen > 32) maxnamelen = 32;
for (UINT i=0; i<ientries; i++) if (i < m_nSamples)
{
lstrcpyn(m_szNames[i+1], psznames + i*ientrysz, maxnamelen);
}
}
}
// Track Names
DWORD trackinfo_ofs = bswapBE32(pmex->trackinfo_ofs);
if ((trackinfo_ofs) && (trackinfo_ofs + m_nChannels * 4 < dwMemLength))
{
DWORD *ptrktags = (DWORD *)(lpStream + trackinfo_ofs);
for (UINT i=0; i<m_nChannels; i++)
{
DWORD trknameofs = 0, trknamelen = 0;
DWORD trktagofs = bswapBE32(ptrktags[i]);
if (trktagofs)
{
while (trktagofs+8 < dwMemLength)
{
DWORD ntag = bswapBE32(*(DWORD *)(lpStream + trktagofs));
if (ntag == MMDTAG_END) break;
DWORD tagdata = bswapBE32(*(DWORD *)(lpStream + trktagofs + 4));
switch(ntag)
{
case MMDTAG_TRK_NAMELEN: trknamelen = tagdata; break;
case MMDTAG_TRK_NAME: trknameofs = tagdata; break;
}
trktagofs += 8;
}
if (trknamelen > MAX_CHANNELNAME) trknamelen = MAX_CHANNELNAME;
if ((trknameofs) && (trknameofs + trknamelen < dwMemLength))
{
lstrcpyn(ChnSettings[i].szName, (LPCSTR)(lpStream+trknameofs), MAX_CHANNELNAME);
}
}
}
}
}
// Reading samples
if (dwSmplArr > dwMemLength - 4*m_nSamples) return TRUE;
pdwTable = (LPDWORD)(lpStream + dwSmplArr);
for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (pdwTable[iSmp])
{
UINT dwPos = bswapBE32(pdwTable[iSmp]);
if ((dwPos >= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength)) continue;
MMDSAMPLEHEADER *psdh = (MMDSAMPLEHEADER *)(lpStream + dwPos);
UINT len = bswapBE32(psdh->length);
#ifdef MED_LOG
Log("SampleData %d: stype=0x%02X len=%d\n", iSmp, bswapBE16(psdh->type), len);
#endif
if ((len > MAX_SAMPLE_LENGTH) || (dwPos + len + 6 > dwMemLength)) len = 0;
UINT flags = RS_PCM8S, stype = bswapBE16(psdh->type);
LPSTR psdata = (LPSTR)(lpStream + dwPos + 6);
if (stype & 0x80)
{
psdata += (stype & 0x20) ? 14 : 6;
} else
{
if (stype & 0x10)
{
Ins[iSmp+1].uFlags |= CHN_16BIT;
len /= 2;
flags = (stype & 0x20) ? RS_STPCM16M : RS_PCM16M;
} else
{
flags = (stype & 0x20) ? RS_STPCM8S : RS_PCM8S;
}
if (stype & 0x20) len /= 2;
}
Ins[iSmp+1].nLength = len;
ReadSample(&Ins[iSmp+1], flags, psdata, dwMemLength - dwPos - 6);
}
// Reading patterns (blocks)
if (wNumBlocks > MAX_PATTERNS) wNumBlocks = MAX_PATTERNS;
if ((!dwBlockArr) || (dwBlockArr > dwMemLength - 4*wNumBlocks)) return TRUE;
pdwTable = (LPDWORD)(lpStream + dwBlockArr);
playtransp += (version == '3') ? 24 : 48;
for (UINT iBlk=0; iBlk<wNumBlocks; iBlk++)
{
UINT dwPos = bswapBE32(pdwTable[iBlk]);
if ((!dwPos) || (dwPos >= dwMemLength) || (dwPos >= dwMemLength - 8)) continue;
UINT lines = 64, tracks = 4;
if (version == '0')
{
const MMD0BLOCK *pmb = (const MMD0BLOCK *)(lpStream + dwPos);
lines = pmb->lines + 1;
tracks = pmb->numtracks;
if (!tracks) tracks = m_nChannels;
if ((Patterns[iBlk] = AllocatePattern(lines, m_nChannels)) == NULL) continue;
PatternSize[iBlk] = lines;
MODCOMMAND *p = Patterns[iBlk];
LPBYTE s = (LPBYTE)(lpStream + dwPos + 2);
UINT maxlen = tracks*lines*3;
if (maxlen + dwPos > dwMemLength - 2) break;
for (UINT y=0; y<lines; y++)
{
for (UINT x=0; x<tracks; x++, s+=3) if (x < m_nChannels)
{
BYTE note = s[0] & 0x3F;
BYTE instr = s[1] >> 4;
if (s[0] & 0x80) instr |= 0x10;
if (s[0] & 0x40) instr |= 0x20;
if ((note) && (note <= 132)) p->note = note + playtransp;
p->instr = instr;
p->command = s[1] & 0x0F;
p->param = s[2];
// if (!iBlk) Log("%02X.%02X.%02X | ", s[0], s[1], s[2]);
MedConvert(p, pmsh);
p++;
}
//if (!iBlk) Log("\n");
}
} else
{
MMD1BLOCK *pmb = (MMD1BLOCK *)(lpStream + dwPos);
#ifdef MED_LOG
Log("MMD1BLOCK: lines=%2d, tracks=%2d, offset=0x%04X\n",
bswapBE16(pmb->lines), bswapBE16(pmb->numtracks), bswapBE32(pmb->info));
#endif
MMD1BLOCKINFO *pbi = NULL;
BYTE *pcmdext = NULL;
lines = (pmb->lines >> 8) + 1;
tracks = pmb->numtracks >> 8;
if (!tracks) tracks = m_nChannels;
if ((Patterns[iBlk] = AllocatePattern(lines, m_nChannels)) == NULL) continue;
PatternSize[iBlk] = (WORD)lines;
DWORD dwBlockInfo = bswapBE32(pmb->info);
if ((dwBlockInfo) && (dwBlockInfo < dwMemLength - sizeof(MMD1BLOCKINFO)))
{
pbi = (MMD1BLOCKINFO *)(lpStream + dwBlockInfo);
#ifdef MED_LOG
Log(" BLOCKINFO: blockname=0x%04X namelen=%d pagetable=0x%04X &cmdexttable=0x%04X\n",
bswapBE32(pbi->blockname), bswapBE32(pbi->blocknamelen), bswapBE32(pbi->pagetable), bswapBE32(pbi->cmdexttable));
#endif
if ((pbi->blockname) && (pbi->blocknamelen))
{
DWORD nameofs = bswapBE32(pbi->blockname);
UINT namelen = bswapBE32(pbi->blocknamelen);
if ((nameofs < dwMemLength) && (nameofs+namelen < dwMemLength))
{
SetPatternName(iBlk, (LPCSTR)(lpStream+nameofs));
}
}
if (pbi->cmdexttable)
{
DWORD cmdexttable = bswapBE32(pbi->cmdexttable);
if (cmdexttable < dwMemLength - 4)
{
cmdexttable = bswapBE32(*(DWORD *)(lpStream + cmdexttable));
if ((cmdexttable) && (cmdexttable <= dwMemLength - lines*tracks))
{
pcmdext = (BYTE *)(lpStream + cmdexttable);
}
}
}
}
MODCOMMAND *p = Patterns[iBlk];
LPBYTE s = (LPBYTE)(lpStream + dwPos + 8);
UINT maxlen = tracks*lines*4;
if (maxlen + dwPos > dwMemLength - 8) break;
for (UINT y=0; y<lines; y++)
{
for (UINT x=0; x<tracks; x++, s+=4) if (x < m_nChannels)
{
BYTE note = s[0];
if ((note) && (note <= 132))
{
int rnote = note + playtransp;
if (rnote < 1) rnote = 1;
if (rnote > 120) rnote = 120;
p->note = (BYTE)rnote;
}
p->instr = s[1];
p->command = s[2];
p->param = s[3];
if (pcmdext) p->vol = pcmdext[x];
MedConvert(p, pmsh);
p++;
}
if (pcmdext) pcmdext += tracks;
}
}
}
// Setup channel pan positions
for (UINT iCh=0; iCh<m_nChannels; iCh++)
{
ChnSettings[iCh].nPan = (((iCh&3) == 1) || ((iCh&3) == 2)) ? 0xC0 : 0x40;
ChnSettings[iCh].nVolume = 64;
}
return TRUE;
}

2019
src/modplug/load_mid.cpp Normal file

File diff suppressed because it is too large Load diff

505
src/modplug/load_mod.cpp Normal file
View file

@ -0,0 +1,505 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
extern WORD ProTrackerPeriodTable[6*12];
//////////////////////////////////////////////////////////
// ProTracker / NoiseTracker MOD/NST file support
void CSoundFile::ConvertModCommand(MODCOMMAND *m) const
//-----------------------------------------------------
{
UINT command = m->command, param = m->param;
switch(command)
{
case 0x00: if (param) command = CMD_ARPEGGIO; break;
case 0x01: command = CMD_PORTAMENTOUP; break;
case 0x02: command = CMD_PORTAMENTODOWN; break;
case 0x03: command = CMD_TONEPORTAMENTO; break;
case 0x04: command = CMD_VIBRATO; break;
case 0x05: command = CMD_TONEPORTAVOL; if (param & 0xF0) param &= 0xF0; break;
case 0x06: command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break;
case 0x07: command = CMD_TREMOLO; break;
case 0x08: command = CMD_PANNING8; break;
case 0x09: command = CMD_OFFSET; break;
case 0x0A: command = CMD_VOLUMESLIDE; if (param & 0xF0) param &= 0xF0; break;
case 0x0B: command = CMD_POSITIONJUMP; break;
case 0x0C: command = CMD_VOLUME; break;
case 0x0D: command = CMD_PATTERNBREAK; param = ((param >> 4) * 10) + (param & 0x0F); break;
case 0x0E: command = CMD_MODCMDEX; break;
case 0x0F: command = (param <= (UINT)((m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? 0x1F : 0x20)) ? CMD_SPEED : CMD_TEMPO;
if ((param == 0xFF) && (m_nSamples == 15)) command = 0; break;
// Extension for XM extended effects
case 'G' - 55: command = CMD_GLOBALVOLUME; break;
case 'H' - 55: command = CMD_GLOBALVOLSLIDE; if (param & 0xF0) param &= 0xF0; break;
case 'K' - 55: command = CMD_KEYOFF; break;
case 'L' - 55: command = CMD_SETENVPOSITION; break;
case 'M' - 55: command = CMD_CHANNELVOLUME; break;
case 'N' - 55: command = CMD_CHANNELVOLSLIDE; break;
case 'P' - 55: command = CMD_PANNINGSLIDE; if (param & 0xF0) param &= 0xF0; break;
case 'R' - 55: command = CMD_RETRIG; break;
case 'T' - 55: command = CMD_TREMOR; break;
case 'X' - 55: command = CMD_XFINEPORTAUPDOWN; break;
case 'Y' - 55: command = CMD_PANBRELLO; break;
case 'Z' - 55: command = CMD_MIDI; break;
default: command = 0;
}
m->command = command;
m->param = param;
}
WORD CSoundFile::ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const
//------------------------------------------------------------------
{
UINT command = m->command & 0x3F, param = m->param;
switch(command)
{
case 0: command = param = 0; break;
case CMD_ARPEGGIO: command = 0; break;
case CMD_PORTAMENTOUP:
if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM))
{
if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x10; break; }
else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x10; break; }
}
command = 0x01;
break;
case CMD_PORTAMENTODOWN:
if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM))
{
if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x20; break; }
else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x20; break; }
}
command = 0x02;
break;
case CMD_TONEPORTAMENTO: command = 0x03; break;
case CMD_VIBRATO: command = 0x04; break;
case CMD_TONEPORTAVOL: command = 0x05; break;
case CMD_VIBRATOVOL: command = 0x06; break;
case CMD_TREMOLO: command = 0x07; break;
case CMD_PANNING8:
command = 0x08;
if (bXM)
{
if ((m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM) && (param <= 0x80))
{
param <<= 1;
if (param > 255) param = 255;
}
} else
{
if ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)) param >>= 1;
}
break;
case CMD_OFFSET: command = 0x09; break;
case CMD_VOLUMESLIDE: command = 0x0A; break;
case CMD_POSITIONJUMP: command = 0x0B; break;
case CMD_VOLUME: command = 0x0C; break;
case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break;
case CMD_MODCMDEX: command = 0x0E; break;
case CMD_SPEED: command = 0x0F; if (param > 0x20) param = 0x20; break;
case CMD_TEMPO: if (param > 0x20) { command = 0x0F; break; }
case CMD_GLOBALVOLUME: command = 'G' - 55; break;
case CMD_GLOBALVOLSLIDE: command = 'H' - 55; break;
case CMD_KEYOFF: command = 'K' - 55; break;
case CMD_SETENVPOSITION: command = 'L' - 55; break;
case CMD_CHANNELVOLUME: command = 'M' - 55; break;
case CMD_CHANNELVOLSLIDE: command = 'N' - 55; break;
case CMD_PANNINGSLIDE: command = 'P' - 55; break;
case CMD_RETRIG: command = 'R' - 55; break;
case CMD_TREMOR: command = 'T' - 55; break;
case CMD_XFINEPORTAUPDOWN: command = 'X' - 55; break;
case CMD_PANBRELLO: command = 'Y' - 55; break;
case CMD_MIDI: command = 'Z' - 55; break;
case CMD_S3MCMDEX:
switch(param & 0xF0)
{
case 0x10: command = 0x0E; param = (param & 0x0F) | 0x30; break;
case 0x20: command = 0x0E; param = (param & 0x0F) | 0x50; break;
case 0x30: command = 0x0E; param = (param & 0x0F) | 0x40; break;
case 0x40: command = 0x0E; param = (param & 0x0F) | 0x70; break;
case 0x90: command = 'X' - 55; break;
case 0xB0: command = 0x0E; param = (param & 0x0F) | 0x60; break;
case 0xA0:
case 0x50:
case 0x70:
case 0x60: command = param = 0; break;
default: command = 0x0E; break;
}
break;
default: command = param = 0;
}
return (WORD)((command << 8) | (param));
}
#pragma pack(1)
typedef struct _MODSAMPLE
{
CHAR name[22];
WORD length;
BYTE finetune;
BYTE volume;
WORD loopstart;
WORD looplen;
} MODSAMPLE, *PMODSAMPLE;
typedef struct _MODMAGIC
{
BYTE nOrders;
BYTE nRestartPos;
BYTE Orders[128];
char Magic[4]; // changed from CHAR
} MODMAGIC, *PMODMAGIC;
#pragma pack()
BOOL IsMagic(LPCSTR s1, LPCSTR s2)
{
return ((*(DWORD *)s1) == (*(DWORD *)s2)) ? TRUE : FALSE;
}
BOOL CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
char s[1024]; // changed from CHAR
DWORD dwMemPos, dwTotalSampleLen;
PMODMAGIC pMagic;
UINT nErr;
if ((!lpStream) || (dwMemLength < 0x600)) return FALSE;
dwMemPos = 20;
m_nSamples = 31;
m_nChannels = 4;
pMagic = (PMODMAGIC)(lpStream+dwMemPos+sizeof(MODSAMPLE)*31);
// Check Mod Magic
memcpy(s, pMagic->Magic, 4);
if ((IsMagic(s, "M.K.")) || (IsMagic(s, "M!K!"))
|| (IsMagic(s, "M&K!")) || (IsMagic(s, "N.T."))) m_nChannels = 4; else
if ((IsMagic(s, "CD81")) || (IsMagic(s, "OKTA"))) m_nChannels = 8; else
if ((s[0]=='F') && (s[1]=='L') && (s[2]=='T') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else
if ((s[0]>='2') && (s[0]<='9') && (s[1]=='C') && (s[2]=='H') && (s[3]=='N')) m_nChannels = s[0] - '0'; else
if ((s[0]=='1') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 10; else
if ((s[0]=='2') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 20; else
if ((s[0]=='3') && (s[1]>='0') && (s[1]<='2') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 30; else
if ((s[0]=='T') && (s[1]=='D') && (s[2]=='Z') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else
if (IsMagic(s,"16CN")) m_nChannels = 16; else
if (IsMagic(s,"32CN")) m_nChannels = 32; else
{
// don't accept any unknown magics. If we did this the code would play all garbage that's
// thrown at it
//if (s[0] || s[1] || s[2] || s[3])
return FALSE;
//m_nSamples = 15;
}
// Load Samples
nErr = 0;
dwTotalSampleLen = 0;
for (UINT i=1; i<=m_nSamples; i++)
{
PMODSAMPLE pms = (PMODSAMPLE)(lpStream+dwMemPos);
MODINSTRUMENT *psmp = &Ins[i];
UINT loopstart, looplen;
memcpy(m_szNames[i], pms->name, 22);
m_szNames[i][22] = 0;
psmp->uFlags = 0;
psmp->nLength = bswapBE16(pms->length)*2;
dwTotalSampleLen += psmp->nLength;
psmp->nFineTune = MOD2XMFineTune(pms->finetune & 0x0F);
psmp->nVolume = 4*pms->volume;
if (psmp->nVolume > 256) { psmp->nVolume = 256; nErr++; }
psmp->nGlobalVol = 64;
psmp->nPan = 128;
loopstart = bswapBE16(pms->loopstart)*2;
looplen = bswapBE16(pms->looplen)*2;
// Fix loops
if ((looplen > 2) && (loopstart+looplen > psmp->nLength)
&& (loopstart/2+looplen <= psmp->nLength))
{
loopstart /= 2;
}
psmp->nLoopStart = loopstart;
psmp->nLoopEnd = loopstart + looplen;
if (psmp->nLength < 4) psmp->nLength = 0;
if (psmp->nLength)
{
UINT derr = 0;
if (psmp->nLoopStart >= psmp->nLength) { psmp->nLoopStart = psmp->nLength-1; derr|=1; }
if (psmp->nLoopEnd > psmp->nLength) { psmp->nLoopEnd = psmp->nLength; derr |= 1; }
if (psmp->nLoopStart > psmp->nLoopEnd) derr |= 1;
if ((psmp->nLoopStart > psmp->nLoopEnd) || (psmp->nLoopEnd <= 8)
|| (psmp->nLoopEnd - psmp->nLoopStart <= 4))
{
psmp->nLoopStart = 0;
psmp->nLoopEnd = 0;
}
if (psmp->nLoopEnd > psmp->nLoopStart)
{
psmp->uFlags |= CHN_LOOP;
}
}
dwMemPos += sizeof(MODSAMPLE);
}
if ((m_nSamples == 15) && (dwTotalSampleLen > dwMemLength * 4)) return FALSE;
pMagic = (PMODMAGIC)(lpStream+dwMemPos);
dwMemPos += sizeof(MODMAGIC);
if (m_nSamples == 15) dwMemPos -= 4;
memset(Order, 0,sizeof(Order));
memcpy(Order, pMagic->Orders, 128);
UINT nbp, nbpbuggy, nbpbuggy2, norders;
norders = pMagic->nOrders;
if ((!norders) || (norders > 0x80))
{
norders = 0x80;
while ((norders > 1) && (!Order[norders-1])) norders--;
}
nbpbuggy = 0;
nbpbuggy2 = 0;
nbp = 0;
for (UINT iord=0; iord<128; iord++)
{
UINT i = Order[iord];
if ((i < 0x80) && (nbp <= i))
{
nbp = i+1;
if (iord<norders) nbpbuggy = nbp;
}
if (i >= nbpbuggy2) nbpbuggy2 = i+1;
}
for (UINT iend=norders; iend<MAX_ORDERS; iend++) Order[iend] = 0xFF;
norders--;
m_nRestartPos = pMagic->nRestartPos;
if (m_nRestartPos >= 0x78) m_nRestartPos = 0;
if (m_nRestartPos + 1 >= (UINT)norders) m_nRestartPos = 0;
if (!nbp) return FALSE;
DWORD dwWowTest = dwTotalSampleLen+dwMemPos;
if ((IsMagic(pMagic->Magic, "M.K.")) && (dwWowTest + nbp*8*256 == dwMemLength)) m_nChannels = 8;
if ((nbp != nbpbuggy) && (dwWowTest + nbp*m_nChannels*256 != dwMemLength))
{
if (dwWowTest + nbpbuggy*m_nChannels*256 == dwMemLength) nbp = nbpbuggy;
else nErr += 8;
} else
if ((nbpbuggy2 > nbp) && (dwWowTest + nbpbuggy2*m_nChannels*256 == dwMemLength))
{
nbp = nbpbuggy2;
}
if ((dwWowTest < 0x600) || (dwWowTest > dwMemLength)) nErr += 8;
if ((m_nSamples == 15) && (nErr >= 16)) return FALSE;
// Default settings
m_nType = MOD_TYPE_MOD;
m_nDefaultSpeed = 6;
m_nDefaultTempo = 125;
m_nMinPeriod = 14 << 2;
m_nMaxPeriod = 3424 << 2;
memcpy(m_szNames, lpStream, 20);
// Setting channels pan
for (UINT ich=0; ich<m_nChannels; ich++)
{
ChnSettings[ich].nVolume = 64;
if (gdwSoundSetup & SNDMIX_MAXDEFAULTPAN)
ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 256 : 0;
else
ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 0xC0 : 0x40;
}
// Reading channels
for (UINT ipat=0; ipat<nbp; ipat++)
{
if (ipat < MAX_PATTERNS)
{
if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break;
PatternSize[ipat] = 64;
if (dwMemPos + m_nChannels*256 >= dwMemLength) break;
MODCOMMAND *m = Patterns[ipat];
LPCBYTE p = lpStream + dwMemPos;
for (UINT j=m_nChannels*64; j; m++,p+=4,j--)
{
BYTE A0=p[0], A1=p[1], A2=p[2], A3=p[3];
UINT n = ((((UINT)A0 & 0x0F) << 8) | (A1));
if ((n) && (n != 0xFFF)) m->note = GetNoteFromPeriod(n << 2);
m->instr = ((UINT)A2 >> 4) | (A0 & 0x10);
m->command = A2 & 0x0F;
m->param = A3;
if ((m->command) || (m->param)) ConvertModCommand(m);
}
}
dwMemPos += m_nChannels*256;
}
// Reading instruments
DWORD dwErrCheck = 0;
for (UINT ismp=1; ismp<=m_nSamples; ismp++) if (Ins[ismp].nLength)
{
LPSTR p = (LPSTR)(lpStream+dwMemPos);
UINT flags = 0;
if (dwMemPos + 5 >= dwMemLength) break;
if (!strnicmp(p, "ADPCM", 5))
{
flags = 3;
p += 5;
dwMemPos += 5;
}
DWORD dwSize = ReadSample(&Ins[ismp], flags, p, dwMemLength - dwMemPos);
if (dwSize)
{
dwMemPos += dwSize;
dwErrCheck++;
}
}
#ifdef MODPLUG_TRACKER
return TRUE;
#else
return (dwErrCheck) ? TRUE : FALSE;
#endif
}
#ifndef MODPLUG_NO_FILESAVE
#pragma warning(disable:4100)
BOOL CSoundFile::SaveMod(LPCSTR lpszFileName, UINT nPacking)
//----------------------------------------------------------
{
BYTE insmap[32];
UINT inslen[32];
BYTE bTab[32];
BYTE ord[128];
FILE *f;
if ((!m_nChannels) || (!lpszFileName)) return FALSE;
if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE;
memset(ord, 0, sizeof(ord));
memset(inslen, 0, sizeof(inslen));
if (m_nInstruments)
{
memset(insmap, 0, sizeof(insmap));
for (UINT i=1; i<32; i++) if (Headers[i])
{
for (UINT j=0; j<128; j++) if (Headers[i]->Keyboard[j])
{
insmap[i] = Headers[i]->Keyboard[j];
break;
}
}
} else
{
for (UINT i=0; i<32; i++) insmap[i] = (BYTE)i;
}
// Writing song name
fwrite(m_szNames, 20, 1, f);
// Writing instrument definition
for (UINT iins=1; iins<=31; iins++)
{
MODINSTRUMENT *pins = &Ins[insmap[iins]];
memcpy(bTab, m_szNames[iins],22);
inslen[iins] = pins->nLength;
if (inslen[iins] > 0x1fff0) inslen[iins] = 0x1fff0;
bTab[22] = inslen[iins] >> 9;
bTab[23] = inslen[iins] >> 1;
if (pins->RelativeTone < 0) bTab[24] = 0x08; else
if (pins->RelativeTone > 0) bTab[24] = 0x07; else
bTab[24] = (BYTE)XM2MODFineTune(pins->nFineTune);
bTab[25] = pins->nVolume >> 2;
bTab[26] = pins->nLoopStart >> 9;
bTab[27] = pins->nLoopStart >> 1;
bTab[28] = (pins->nLoopEnd - pins->nLoopStart) >> 9;
bTab[29] = (pins->nLoopEnd - pins->nLoopStart) >> 1;
fwrite(bTab, 30, 1, f);
}
// Writing number of patterns
UINT nbp=0, norders=128;
for (UINT iord=0; iord<128; iord++)
{
if (Order[iord] == 0xFF)
{
norders = iord;
break;
}
if ((Order[iord] < 0x80) && (nbp<=Order[iord])) nbp = Order[iord]+1;
}
bTab[0] = norders;
bTab[1] = m_nRestartPos;
fwrite(bTab, 2, 1, f);
// Writing pattern list
if (norders) memcpy(ord, Order, norders);
fwrite(ord, 128, 1, f);
// Writing signature
if (m_nChannels == 4)
lstrcpy((LPSTR)&bTab, "M.K.");
else
wsprintf((LPSTR)&bTab, "%luCHN", m_nChannels);
fwrite(bTab, 4, 1, f);
// Writing patterns
for (UINT ipat=0; ipat<nbp; ipat++) if (Patterns[ipat])
{
BYTE s[64*4];
MODCOMMAND *m = Patterns[ipat];
for (UINT i=0; i<64; i++) if (i < PatternSize[ipat])
{
LPBYTE p=s;
for (UINT c=0; c<m_nChannels; c++,p+=4,m++)
{
UINT param = ModSaveCommand(m, FALSE);
UINT command = param >> 8;
param &= 0xFF;
if (command > 0x0F) command = param = 0;
if ((m->vol >= 0x10) && (m->vol <= 0x50) && (!command) && (!param)) { command = 0x0C; param = m->vol - 0x10; }
UINT period = m->note;
if (period)
{
if (period < 37) period = 37;
period -= 37;
if (period >= 6*12) period = 6*12-1;
period = ProTrackerPeriodTable[period];
}
UINT instr = (m->instr > 31) ? 0 : m->instr;
p[0] = ((period >> 8) & 0x0F) | (instr & 0x10);
p[1] = period & 0xFF;
p[2] = ((instr & 0x0F) << 4) | (command & 0x0F);
p[3] = param;
}
fwrite(s, m_nChannels, 4, f);
} else
{
memset(s, 0, m_nChannels*4);
fwrite(s, m_nChannels, 4, f);
}
}
// Writing instruments
for (UINT ismpd=1; ismpd<=31; ismpd++) if (inslen[ismpd])
{
MODINSTRUMENT *pins = &Ins[insmap[ismpd]];
UINT flags = RS_PCM8S;
#ifndef NO_PACKING
if (!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
{
if ((nPacking) && (CanPackSample((char *)pins->pSample, inslen[ismpd], nPacking)))
{
fwrite("ADPCM", 1, 5, f);
flags = RS_ADPCM4;
}
}
#endif
WriteSample(f, pins, flags, inslen[ismpd]);
}
fclose(f);
return TRUE;
}
#pragma warning(default:4100)
#endif // MODPLUG_NO_FILESAVE

635
src/modplug/load_mt2.cpp Normal file
View file

@ -0,0 +1,635 @@
#include "stdafx.h"
#include "sndfile.h"
//#define MT2DEBUG
#pragma pack(1)
typedef struct _MT2FILEHEADER
{
DWORD dwMT20; // 0x3032544D "MT20"
DWORD dwSpecial;
WORD wVersion;
CHAR szTrackerName[32]; // "MadTracker 2.0"
CHAR szSongName[64];
WORD nOrders;
WORD wRestart;
WORD wPatterns;
WORD wChannels;
WORD wSamplesPerTick;
BYTE bTicksPerLine;
BYTE bLinesPerBeat;
DWORD fulFlags; // b0=packed patterns
WORD wInstruments;
WORD wSamples;
BYTE Orders[256];
} MT2FILEHEADER;
typedef struct _MT2PATTERN
{
WORD wLines;
DWORD wDataLen;
} MT2PATTERN;
typedef struct _MT2COMMAND
{
BYTE note; // 0=nothing, 97=note off
BYTE instr;
BYTE vol;
BYTE pan;
BYTE fxcmd;
BYTE fxparam1;
BYTE fxparam2;
} MT2COMMAND;
typedef struct _MT2DRUMSDATA
{
WORD wDrumPatterns;
WORD wDrumSamples[8];
BYTE DrumPatternOrder[256];
} MT2DRUMSDATA;
typedef struct _MT2AUTOMATION
{
DWORD dwFlags;
DWORD dwEffectId;
DWORD nEnvPoints;
} MT2AUTOMATION;
typedef struct _MT2INSTRUMENT
{
CHAR szName[32];
DWORD dwDataLen;
WORD wSamples;
BYTE GroupsMapping[96];
BYTE bVibType;
BYTE bVibSweep;
BYTE bVibDepth;
BYTE bVibRate;
WORD wFadeOut;
WORD wNNA;
WORD wInstrFlags;
WORD wEnvFlags1;
WORD wEnvFlags2;
} MT2INSTRUMENT;
typedef struct _MT2ENVELOPE
{
BYTE nFlags;
BYTE nPoints;
BYTE nSustainPos;
BYTE nLoopStart;
BYTE nLoopEnd;
BYTE bReserved[3];
BYTE EnvData[64];
} MT2ENVELOPE;
typedef struct _MT2SYNTH
{
BYTE nSynthId;
BYTE nFxId;
WORD wCutOff;
BYTE nResonance;
BYTE nAttack;
BYTE nDecay;
BYTE bReserved[25];
} MT2SYNTH;
typedef struct _MT2SAMPLE
{
CHAR szName[32];
DWORD dwDataLen;
DWORD dwLength;
DWORD dwFrequency;
BYTE nQuality;
BYTE nChannels;
BYTE nFlags;
BYTE nLoop;
DWORD dwLoopStart;
DWORD dwLoopEnd;
WORD wVolume;
BYTE nPan;
BYTE nBaseNote;
WORD wSamplesPerBeat;
} MT2SAMPLE;
typedef struct _MT2GROUP
{
BYTE nSmpNo;
BYTE nVolume; // 0-128
BYTE nFinePitch;
BYTE Reserved[5];
} MT2GROUP;
#pragma pack()
static VOID ConvertMT2Command(CSoundFile *that, MODCOMMAND *m, MT2COMMAND *p)
//---------------------------------------------------------------------------
{
// Note
m->note = 0;
if (p->note) m->note = (p->note > 96) ? 0xFF : p->note+12;
// Instrument
m->instr = p->instr;
// Volume Column
if ((p->vol >= 0x10) && (p->vol <= 0x90))
{
m->volcmd = VOLCMD_VOLUME;
m->vol = (p->vol - 0x10) >> 1;
} else
if ((p->vol >= 0xA0) && (p->vol <= 0xAF))
{
m->volcmd = VOLCMD_VOLSLIDEDOWN;
m->vol = (p->vol & 0x0f);
} else
if ((p->vol >= 0xB0) && (p->vol <= 0xBF))
{
m->volcmd = VOLCMD_VOLSLIDEUP;
m->vol = (p->vol & 0x0f);
} else
if ((p->vol >= 0xC0) && (p->vol <= 0xCF))
{
m->volcmd = VOLCMD_FINEVOLDOWN;
m->vol = (p->vol & 0x0f);
} else
if ((p->vol >= 0xD0) && (p->vol <= 0xDF))
{
m->volcmd = VOLCMD_FINEVOLUP;
m->vol = (p->vol & 0x0f);
} else
{
m->volcmd = 0;
m->vol = 0;
}
// Effects
m->command = 0;
m->param = 0;
if ((p->fxcmd) || (p->fxparam1) || (p->fxparam2))
{
if (!p->fxcmd)
{
m->command = p->fxparam2;
m->param = p->fxparam1;
that->ConvertModCommand(m);
} else
{
// TODO: MT2 Effects
}
}
}
BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
MT2FILEHEADER *pfh = (MT2FILEHEADER *)lpStream;
DWORD dwMemPos, dwDrumDataPos, dwExtraDataPos;
UINT nDrumDataLen, nExtraDataLen;
MT2DRUMSDATA *pdd;
MT2INSTRUMENT *InstrMap[255];
MT2SAMPLE *SampleMap[256];
if ((!lpStream) || (dwMemLength < sizeof(MT2FILEHEADER))
|| (pfh->dwMT20 != 0x3032544D)
|| (pfh->wVersion < 0x0200) || (pfh->wVersion >= 0x0300)
|| (pfh->wChannels < 4) || (pfh->wChannels > 64)) return FALSE;
pdd = NULL;
m_nType = MOD_TYPE_MT2;
m_nChannels = pfh->wChannels;
m_nRestartPos = pfh->wRestart;
m_nDefaultSpeed = pfh->bTicksPerLine;
m_nDefaultTempo = 125;
if ((pfh->wSamplesPerTick > 100) && (pfh->wSamplesPerTick < 5000))
{
m_nDefaultTempo = 110250 / pfh->wSamplesPerTick;
}
for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++)
{
Order[iOrd] = (BYTE)((iOrd < pfh->nOrders) ? pfh->Orders[iOrd] : 0xFF);
}
memcpy(m_szNames[0], pfh->szSongName, 32);
m_szNames[0][31] = 0;
dwMemPos = sizeof(MT2FILEHEADER);
nDrumDataLen = *(WORD *)(lpStream + dwMemPos);
dwDrumDataPos = dwMemPos + 2;
if (nDrumDataLen >= 2) pdd = (MT2DRUMSDATA *)(lpStream+dwDrumDataPos);
dwMemPos += 2 + nDrumDataLen;
#ifdef MT2DEBUG
Log("MT2 v%03X: \"%s\" (flags=%04X)\n", pfh->wVersion, m_szNames[0], pfh->fulFlags);
Log("%d Channels, %d Patterns, %d Instruments, %d Samples\n", pfh->wChannels, pfh->wPatterns, pfh->wInstruments, pfh->wSamples);
Log("Drum Data: %d bytes @%04X\n", nDrumDataLen, dwDrumDataPos);
#endif
if (dwMemPos >= dwMemLength-12) return TRUE;
if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4;
if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4;
nExtraDataLen = *(DWORD *)(lpStream+dwMemPos);
dwExtraDataPos = dwMemPos + 4;
dwMemPos += 4;
#ifdef MT2DEBUG
Log("Extra Data: %d bytes @%04X\n", nExtraDataLen, dwExtraDataPos);
#endif
if (dwMemPos + nExtraDataLen >= dwMemLength) return TRUE;
while (dwMemPos+8 < dwExtraDataPos + nExtraDataLen)
{
DWORD dwId = *(DWORD *)(lpStream+dwMemPos);
DWORD dwLen = *(DWORD *)(lpStream+dwMemPos+4);
dwMemPos += 8;
if (dwMemPos + dwLen > dwMemLength) return TRUE;
#ifdef MT2DEBUG
CHAR s[5];
memcpy(s, &dwId, 4);
s[4] = 0;
Log("pos=0x%04X: %s: %d bytes\n", dwMemPos-8, s, dwLen);
#endif
switch(dwId)
{
// MSG
case 0x0047534D:
if ((dwLen > 3) && (!m_lpszSongComments))
{
DWORD nTxtLen = dwLen;
if (nTxtLen > 32000) nTxtLen = 32000;
m_lpszSongComments = new char[nTxtLen]; // changed from CHAR
if (m_lpszSongComments)
{
memcpy(m_lpszSongComments, lpStream+dwMemPos+1, nTxtLen-1);
m_lpszSongComments[nTxtLen-1] = 0;
}
}
break;
// SUM -> author name (or "Unregistered")
// TMAP
// TRKS
case 0x534b5254:
break;
}
dwMemPos += dwLen;
}
// Load Patterns
dwMemPos = dwExtraDataPos + nExtraDataLen;
for (UINT iPat=0; iPat<pfh->wPatterns; iPat++) if (dwMemPos < dwMemLength-6)
{
MT2PATTERN *pmp = (MT2PATTERN *)(lpStream+dwMemPos);
UINT wDataLen = (pmp->wDataLen + 1) & ~1;
dwMemPos += 6;
if (dwMemPos + wDataLen > dwMemLength) break;
UINT nLines = pmp->wLines;
if ((iPat < MAX_PATTERNS) && (nLines > 0) && (nLines <= 256))
{
#ifdef MT2DEBUG
Log("Pattern #%d @%04X: %d lines, %d bytes\n", iPat, dwMemPos-6, nLines, pmp->wDataLen);
#endif
PatternSize[iPat] = nLines;
Patterns[iPat] = AllocatePattern(nLines, m_nChannels);
if (!Patterns[iPat]) return TRUE;
MODCOMMAND *m = Patterns[iPat];
UINT len = wDataLen;
if (pfh->fulFlags & 1) // Packed Patterns
{
BYTE *p = (BYTE *)(lpStream+dwMemPos);
UINT pos = 0, row=0, ch=0;
while (pos < len)
{
MT2COMMAND cmd;
UINT infobyte = p[pos++];
UINT rptcount = 0;
if (infobyte == 0xff)
{
rptcount = p[pos++];
infobyte = p[pos++];
#if 0
Log("(%d.%d) FF(%02X).%02X\n", row, ch, rptcount, infobyte);
} else
{
Log("(%d.%d) %02X\n", row, ch, infobyte);
#endif
}
if (infobyte & 0x7f)
{
UINT patpos = row*m_nChannels+ch;
cmd.note = cmd.instr = cmd.vol = cmd.pan = cmd.fxcmd = cmd.fxparam1 = cmd.fxparam2 = 0;
if (infobyte & 1) cmd.note = p[pos++];
if (infobyte & 2) cmd.instr = p[pos++];
if (infobyte & 4) cmd.vol = p[pos++];
if (infobyte & 8) cmd.pan = p[pos++];
if (infobyte & 16) cmd.fxcmd = p[pos++];
if (infobyte & 32) cmd.fxparam1 = p[pos++];
if (infobyte & 64) cmd.fxparam2 = p[pos++];
#ifdef MT2DEBUG
if (cmd.fxcmd)
{
Log("(%d.%d) MT2 FX=%02X.%02X.%02X\n", row, ch, cmd.fxcmd, cmd.fxparam1, cmd.fxparam2);
}
#endif
ConvertMT2Command(this, &m[patpos], &cmd);
}
row += rptcount+1;
while (row >= nLines) { row-=nLines; ch++; }
if (ch >= m_nChannels) break;
}
} else
{
MT2COMMAND *p = (MT2COMMAND *)(lpStream+dwMemPos);
UINT n = 0;
while ((len > sizeof(MT2COMMAND)) && (n < m_nChannels*nLines))
{
ConvertMT2Command(this, m, p);
len -= sizeof(MT2COMMAND);
n++;
p++;
m++;
}
}
}
dwMemPos += wDataLen;
}
// Skip Drum Patterns
if (pdd)
{
#ifdef MT2DEBUG
Log("%d Drum Patterns at offset 0x%08X\n", pdd->wDrumPatterns, dwMemPos);
#endif
for (UINT iDrm=0; iDrm<pdd->wDrumPatterns; iDrm++)
{
if (dwMemPos > dwMemLength-2) return TRUE;
UINT nLines = *(WORD *)(lpStream+dwMemPos);
#ifdef MT2DEBUG
if (nLines != 64) Log("Drum Pattern %d: %d Lines @%04X\n", iDrm, nLines, dwMemPos);
#endif
dwMemPos += 2 + nLines * 32;
}
}
// Automation
if (pfh->fulFlags & 2)
{
#ifdef MT2DEBUG
Log("Automation at offset 0x%08X\n", dwMemPos);
#endif
UINT nAutoCount = m_nChannels;
if (pfh->fulFlags & 0x10) nAutoCount++; // Master Automation
if ((pfh->fulFlags & 0x08) && (pdd)) nAutoCount += 8; // Drums Automation
nAutoCount *= pfh->wPatterns;
for (UINT iAuto=0; iAuto<nAutoCount; iAuto++)
{
if (dwMemPos+12 >= dwMemLength) return TRUE;
MT2AUTOMATION *pma = (MT2AUTOMATION *)(lpStream+dwMemPos);
dwMemPos += (pfh->wVersion <= 0x201) ? 4 : 8;
for (UINT iEnv=0; iEnv<14; iEnv++)
{
if (pma->dwFlags & (1 << iEnv))
{
#ifdef MT2DEBUG
UINT nPoints = *(DWORD *)(lpStream+dwMemPos);
Log(" Env[%d/%d] %04X @%04X: %d points\n", iAuto, nAutoCount, 1 << iEnv, dwMemPos-8, nPoints);
#endif
dwMemPos += 260;
}
}
}
}
// Load Instruments
#ifdef MT2DEBUG
Log("Loading instruments at offset 0x%08X\n", dwMemPos);
#endif
memset(InstrMap, 0, sizeof(InstrMap));
m_nInstruments = (pfh->wInstruments < MAX_INSTRUMENTS) ? pfh->wInstruments : MAX_INSTRUMENTS-1;
for (UINT iIns=1; iIns<=255; iIns++)
{
if (dwMemPos+36 > dwMemLength) return TRUE;
MT2INSTRUMENT *pmi = (MT2INSTRUMENT *)(lpStream+dwMemPos);
INSTRUMENTHEADER *penv = NULL;
if (iIns <= m_nInstruments)
{
penv = new INSTRUMENTHEADER;
Headers[iIns] = penv;
if (penv)
{
memset(penv, 0, sizeof(INSTRUMENTHEADER));
memcpy(penv->name, pmi->szName, 32);
penv->nGlobalVol = 64;
penv->nPan = 128;
for (UINT i=0; i<120; i++)
{
penv->NoteMap[i] = i+1;
}
}
}
#ifdef MT2DEBUG
if (iIns <= pfh->wInstruments) Log(" Instrument #%d at offset %04X: %d bytes\n", iIns, dwMemPos, pmi->dwDataLen);
#endif
if (((LONG)pmi->dwDataLen > 0) && (dwMemPos + pmi->dwDataLen + 40 <= dwMemLength))
{
InstrMap[iIns-1] = pmi;
if (penv)
{
penv->nFadeOut = pmi->wFadeOut;
penv->nNNA = pmi->wNNA & 3;
penv->nDCT = (pmi->wNNA>>8) & 3;
penv->nDNA = (pmi->wNNA>>12) & 3;
MT2ENVELOPE *pehdr[4];
WORD *pedata[4];
if (pfh->wVersion <= 0x201)
{
DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT) - 4;
pehdr[0] = (MT2ENVELOPE *)(lpStream+dwEnvPos);
pehdr[1] = (MT2ENVELOPE *)(lpStream+dwEnvPos+8);
pehdr[2] = pehdr[3] = NULL;
pedata[0] = (WORD *)(lpStream+dwEnvPos+16);
pedata[1] = (WORD *)(lpStream+dwEnvPos+16+64);
pedata[2] = pedata[3] = NULL;
} else
{
DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT);
for (UINT i=0; i<4; i++)
{
if (pmi->wEnvFlags1 & (1<<i))
{
pehdr[i] = (MT2ENVELOPE *)(lpStream+dwEnvPos);
pedata[i] = (WORD *)pehdr[i]->EnvData;
dwEnvPos += sizeof(MT2ENVELOPE);
} else
{
pehdr[i] = NULL;
pedata[i] = NULL;
}
}
}
// Load envelopes
for (UINT iEnv=0; iEnv<4; iEnv++) if (pehdr[iEnv])
{
MT2ENVELOPE *pme = pehdr[iEnv];
WORD *pEnvPoints = NULL;
BYTE *pEnvData = NULL;
#ifdef MT2DEBUG
Log(" Env %d.%d @%04X: %d points\n", iIns, iEnv, (UINT)(((BYTE *)pme)-lpStream), pme->nPoints);
#endif
switch(iEnv)
{
// Volume Envelope
case 0:
if (pme->nFlags & 1) penv->dwFlags |= ENV_VOLUME;
if (pme->nFlags & 2) penv->dwFlags |= ENV_VOLSUSTAIN;
if (pme->nFlags & 4) penv->dwFlags |= ENV_VOLLOOP;
penv->nVolEnv = (pme->nPoints > 16) ? 16 : pme->nPoints;
penv->nVolSustainBegin = penv->nVolSustainEnd = pme->nSustainPos;
penv->nVolLoopStart = pme->nLoopStart;
penv->nVolLoopEnd = pme->nLoopEnd;
pEnvPoints = penv->VolPoints;
pEnvData = penv->VolEnv;
break;
// Panning Envelope
case 1:
if (pme->nFlags & 1) penv->dwFlags |= ENV_PANNING;
if (pme->nFlags & 2) penv->dwFlags |= ENV_PANSUSTAIN;
if (pme->nFlags & 4) penv->dwFlags |= ENV_PANLOOP;
penv->nPanEnv = (pme->nPoints > 16) ? 16 : pme->nPoints;
penv->nPanSustainBegin = penv->nPanSustainEnd = pme->nSustainPos;
penv->nPanLoopStart = pme->nLoopStart;
penv->nPanLoopEnd = pme->nLoopEnd;
pEnvPoints = penv->PanPoints;
pEnvData = penv->PanEnv;
break;
// Pitch/Filter envelope
default:
if (pme->nFlags & 1) penv->dwFlags |= (iEnv==3) ? (ENV_PITCH|ENV_FILTER) : ENV_PITCH;
if (pme->nFlags & 2) penv->dwFlags |= ENV_PITCHSUSTAIN;
if (pme->nFlags & 4) penv->dwFlags |= ENV_PITCHLOOP;
penv->nPitchEnv = (pme->nPoints > 16) ? 16 : pme->nPoints;
penv->nPitchSustainBegin = penv->nPitchSustainEnd = pme->nSustainPos;
penv->nPitchLoopStart = pme->nLoopStart;
penv->nPitchLoopEnd = pme->nLoopEnd;
pEnvPoints = penv->PitchPoints;
pEnvData = penv->PitchEnv;
}
// Envelope data
if ((pEnvPoints) && (pEnvData) && (pedata[iEnv]))
{
WORD *psrc = pedata[iEnv];
for (UINT i=0; i<16; i++)
{
pEnvPoints[i] = psrc[i*2];
pEnvData[i] = (BYTE)psrc[i*2+1];
}
}
}
}
dwMemPos += pmi->dwDataLen + 36;
if (pfh->wVersion > 0x201) dwMemPos += 4; // ?
} else
{
dwMemPos += 36;
}
}
#ifdef MT2DEBUG
Log("Loading samples at offset 0x%08X\n", dwMemPos);
#endif
memset(SampleMap, 0, sizeof(SampleMap));
m_nSamples = (pfh->wSamples < MAX_SAMPLES) ? pfh->wSamples : MAX_SAMPLES-1;
for (UINT iSmp=1; iSmp<=256; iSmp++)
{
if (dwMemPos+36 > dwMemLength) return TRUE;
MT2SAMPLE *pms = (MT2SAMPLE *)(lpStream+dwMemPos);
#ifdef MT2DEBUG
if (iSmp <= m_nSamples) Log(" Sample #%d at offset %04X: %d bytes\n", iSmp, dwMemPos, pms->dwDataLen);
#endif
if (iSmp < MAX_SAMPLES)
{
memcpy(m_szNames[iSmp], pms->szName, 32);
}
if (pms->dwDataLen > 0)
{
SampleMap[iSmp-1] = pms;
if (iSmp < MAX_SAMPLES)
{
MODINSTRUMENT *psmp = &Ins[iSmp];
psmp->nGlobalVol = 64;
psmp->nVolume = (pms->wVolume >> 7);
psmp->nPan = (pms->nPan == 0x80) ? 128 : (pms->nPan^0x80);
psmp->nLength = pms->dwLength;
psmp->nC4Speed = pms->dwFrequency;
psmp->nLoopStart = pms->dwLoopStart;
psmp->nLoopEnd = pms->dwLoopEnd;
FrequencyToTranspose(psmp);
psmp->RelativeTone -= pms->nBaseNote - 49;
psmp->nC4Speed = TransposeToFrequency(psmp->RelativeTone, psmp->nFineTune);
if (pms->nQuality == 2) { psmp->uFlags |= CHN_16BIT; psmp->nLength >>= 1; }
if (pms->nChannels == 2) { psmp->nLength >>= 1; }
if (pms->nLoop == 1) psmp->uFlags |= CHN_LOOP;
if (pms->nLoop == 2) psmp->uFlags |= CHN_LOOP|CHN_PINGPONGLOOP;
}
dwMemPos += pms->dwDataLen + 36;
} else
{
dwMemPos += 36;
}
}
#ifdef MT2DEBUG
Log("Loading groups at offset 0x%08X\n", dwMemPos);
#endif
for (UINT iMap=0; iMap<255; iMap++) if (InstrMap[iMap])
{
if (dwMemPos+8 > dwMemLength) return TRUE;
MT2INSTRUMENT *pmi = InstrMap[iMap];
INSTRUMENTHEADER *penv = NULL;
if (iMap<m_nInstruments) penv = Headers[iMap+1];
for (UINT iGrp=0; iGrp<pmi->wSamples; iGrp++)
{
if (penv)
{
MT2GROUP *pmg = (MT2GROUP *)(lpStream+dwMemPos);
for (UINT i=0; i<96; i++)
{
if (pmi->GroupsMapping[i] == iGrp)
{
UINT nSmp = pmg->nSmpNo+1;
penv->Keyboard[i+12] = (BYTE)nSmp;
if (nSmp <= m_nSamples)
{
Ins[nSmp].nVibType = pmi->bVibType;
Ins[nSmp].nVibSweep = pmi->bVibSweep;
Ins[nSmp].nVibDepth = pmi->bVibDepth;
Ins[nSmp].nVibRate = pmi->bVibRate;
}
}
}
}
dwMemPos += 8;
}
}
#ifdef MT2DEBUG
Log("Loading sample data at offset 0x%08X\n", dwMemPos);
#endif
for (UINT iData=0; iData<256; iData++) if ((iData < m_nSamples) && (SampleMap[iData]))
{
MT2SAMPLE *pms = SampleMap[iData];
MODINSTRUMENT *psmp = &Ins[iData+1];
if (!(pms->nFlags & 5))
{
if (psmp->nLength > 0)
{
#ifdef MT2DEBUG
Log(" Reading sample #%d at offset 0x%04X (len=%d)\n", iData+1, dwMemPos, psmp->nLength);
#endif
UINT rsflags;
if (pms->nChannels == 2)
rsflags = (psmp->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D;
else
rsflags = (psmp->uFlags & CHN_16BIT) ? RS_PCM16D : RS_PCM8D;
dwMemPos += ReadSample(psmp, rsflags, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos);
}
} else
if (dwMemPos+4 < dwMemLength)
{
UINT nNameLen = *(DWORD *)(lpStream+dwMemPos);
dwMemPos += nNameLen + 16;
}
if (dwMemPos+4 >= dwMemLength) break;
}
return TRUE;
}

168
src/modplug/load_mtm.cpp Normal file
View file

@ -0,0 +1,168 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
//////////////////////////////////////////////////////////
// MTM file support (import only)
#pragma pack(1)
typedef struct tagMTMSAMPLE
{
char samplename[22]; // changed from CHAR
DWORD length;
DWORD reppos;
DWORD repend;
CHAR finetune;
BYTE volume;
BYTE attribute;
} MTMSAMPLE;
typedef struct tagMTMHEADER
{
char id[4]; // MTM file marker + version // changed from CHAR
char songname[20]; // ASCIIZ songname // changed from CHAR
WORD numtracks; // number of tracks saved
BYTE lastpattern; // last pattern number saved
BYTE lastorder; // last order number to play (songlength-1)
WORD commentsize; // length of comment field
BYTE numsamples; // number of samples saved
BYTE attribute; // attribute byte (unused)
BYTE beatspertrack;
BYTE numchannels; // number of channels used
BYTE panpos[32]; // voice pan positions
} MTMHEADER;
#pragma pack()
BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
MTMHEADER *pmh = (MTMHEADER *)lpStream;
DWORD dwMemPos = 66;
if ((!lpStream) || (dwMemLength < 0x100)) return FALSE;
if ((strncmp(pmh->id, "MTM", 3)) || (pmh->numchannels > 32)
|| (pmh->numsamples >= MAX_SAMPLES) || (!pmh->numsamples)
|| (!pmh->numtracks) || (!pmh->numchannels)
|| (!pmh->lastpattern) || (pmh->lastpattern > MAX_PATTERNS))
return FALSE;
strncpy(m_szNames[0], pmh->songname, 20);
m_szNames[0][20] = 0;
if (dwMemPos + 37*pmh->numsamples + 128 + 192*pmh->numtracks
+ 64 * (pmh->lastpattern+1) + pmh->commentsize >= dwMemLength)
return FALSE;
m_nType = MOD_TYPE_MTM;
m_nSamples = pmh->numsamples;
m_nChannels = pmh->numchannels;
// Reading instruments
for (UINT i=1; i<=m_nSamples; i++)
{
MTMSAMPLE *pms = (MTMSAMPLE *)(lpStream + dwMemPos);
strncpy(m_szNames[i], pms->samplename, 22);
m_szNames[i][22] = 0;
Ins[i].nVolume = pms->volume << 2;
Ins[i].nGlobalVol = 64;
DWORD len = pms->length;
if ((len > 4) && (len <= MAX_SAMPLE_LENGTH))
{
Ins[i].nLength = len;
Ins[i].nLoopStart = pms->reppos;
Ins[i].nLoopEnd = pms->repend;
if (Ins[i].nLoopEnd > Ins[i].nLength)
Ins[i].nLoopEnd = Ins[i].nLength;
if (Ins[i].nLoopStart + 4 >= Ins[i].nLoopEnd)
Ins[i].nLoopStart = Ins[i].nLoopEnd = 0;
if (Ins[i].nLoopEnd) Ins[i].uFlags |= CHN_LOOP;
Ins[i].nFineTune = MOD2XMFineTune(pms->finetune);
if (pms->attribute & 0x01)
{
Ins[i].uFlags |= CHN_16BIT;
Ins[i].nLength >>= 1;
Ins[i].nLoopStart >>= 1;
Ins[i].nLoopEnd >>= 1;
}
Ins[i].nPan = 128;
}
dwMemPos += 37;
}
// Setting Channel Pan Position
for (UINT ich=0; ich<m_nChannels; ich++)
{
ChnSettings[ich].nPan = ((pmh->panpos[ich] & 0x0F) << 4) + 8;
ChnSettings[ich].nVolume = 64;
}
// Reading pattern order
memcpy(Order, lpStream + dwMemPos, pmh->lastorder+1);
dwMemPos += 128;
// Reading Patterns
LPCBYTE pTracks = lpStream + dwMemPos;
dwMemPos += 192 * pmh->numtracks;
LPWORD pSeq = (LPWORD)(lpStream + dwMemPos);
for (UINT pat=0; pat<=pmh->lastpattern; pat++)
{
PatternSize[pat] = 64;
if ((Patterns[pat] = AllocatePattern(64, m_nChannels)) == NULL) break;
for (UINT n=0; n<32; n++) if ((pSeq[n]) && (pSeq[n] <= pmh->numtracks) && (n < m_nChannels))
{
LPCBYTE p = pTracks + 192 * (pSeq[n]-1);
MODCOMMAND *m = Patterns[pat] + n;
for (UINT i=0; i<64; i++, m+=m_nChannels, p+=3)
{
if (p[0] & 0xFC) m->note = (p[0] >> 2) + 37;
m->instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
UINT cmd = p[1] & 0x0F;
UINT param = p[2];
if (cmd == 0x0A)
{
if (param & 0xF0) param &= 0xF0; else param &= 0x0F;
}
m->command = cmd;
m->param = param;
if ((cmd) || (param)) ConvertModCommand(m);
}
}
pSeq += 32;
}
dwMemPos += 64*(pmh->lastpattern+1);
if ((pmh->commentsize) && (dwMemPos + pmh->commentsize < dwMemLength))
{
UINT n = pmh->commentsize;
m_lpszSongComments = new char[n+1];
if (m_lpszSongComments)
{
memcpy(m_lpszSongComments, lpStream+dwMemPos, n);
m_lpszSongComments[n] = 0;
for (UINT i=0; i<n; i++)
{
if (!m_lpszSongComments[i])
{
m_lpszSongComments[i] = ((i+1) % 40) ? 0x20 : 0x0D;
}
}
}
}
dwMemPos += pmh->commentsize;
// Reading Samples
for (UINT ismp=1; ismp<=m_nSamples; ismp++)
{
if (dwMemPos >= dwMemLength) break;
dwMemPos += ReadSample(&Ins[ismp], (Ins[ismp].uFlags & CHN_16BIT) ? RS_PCM16U : RS_PCM8U,
(LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
}
m_nMinPeriod = 64;
m_nMaxPeriod = 32767;
return TRUE;
}

197
src/modplug/load_okt.cpp Normal file
View file

@ -0,0 +1,197 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
//////////////////////////////////////////////
// Oktalyzer (OKT) module loader //
//////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
typedef struct OKTFILEHEADER
{
DWORD okta; // "OKTA"
DWORD song; // "SONG"
DWORD cmod; // "CMOD"
DWORD fixed8;
BYTE chnsetup[8];
DWORD samp; // "SAMP"
DWORD samplen;
} OKTFILEHEADER;
typedef struct OKTSAMPLE
{
CHAR name[20];
DWORD length;
WORD loopstart;
WORD looplen;
BYTE pad1;
BYTE volume;
BYTE pad2;
BYTE pad3;
} OKTSAMPLE;
BOOL CSoundFile::ReadOKT(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
OKTFILEHEADER *pfh = (OKTFILEHEADER *)lpStream;
DWORD dwMemPos = sizeof(OKTFILEHEADER);
UINT nsamples = 0, npatterns = 0, norders = 0;
if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
if ((pfh->okta != 0x41544B4F) || (pfh->song != 0x474E4F53)
|| (pfh->cmod != 0x444F4D43) || (pfh->chnsetup[0]) || (pfh->chnsetup[2])
|| (pfh->chnsetup[4]) || (pfh->chnsetup[6]) || (pfh->fixed8 != 0x08000000)
|| (pfh->samp != 0x504D4153)) return FALSE;
m_nType = MOD_TYPE_OKT;
m_nChannels = 4 + pfh->chnsetup[1] + pfh->chnsetup[3] + pfh->chnsetup[5] + pfh->chnsetup[7];
if (m_nChannels > MAX_CHANNELS) m_nChannels = MAX_CHANNELS;
nsamples = bswapBE32(pfh->samplen) >> 5;
m_nSamples = nsamples;
if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1;
// Reading samples
for (UINT smp=1; smp <= nsamples; smp++)
{
if (dwMemPos >= dwMemLength) return TRUE;
if (smp < MAX_SAMPLES)
{
OKTSAMPLE *psmp = (OKTSAMPLE *)(lpStream + dwMemPos);
MODINSTRUMENT *pins = &Ins[smp];
memcpy(m_szNames[smp], psmp->name, 20);
pins->uFlags = 0;
pins->nLength = bswapBE32(psmp->length) & ~1;
pins->nLoopStart = bswapBE16(psmp->loopstart);
pins->nLoopEnd = pins->nLoopStart + bswapBE16(psmp->looplen);
if (pins->nLoopStart + 2 < pins->nLoopEnd) pins->uFlags |= CHN_LOOP;
pins->nGlobalVol = 64;
pins->nVolume = psmp->volume << 2;
pins->nC4Speed = 8363;
}
dwMemPos += sizeof(OKTSAMPLE);
}
// SPEE
if (dwMemPos >= dwMemLength) return TRUE;
if (*((DWORD *)(lpStream + dwMemPos)) == 0x45455053)
{
m_nDefaultSpeed = lpStream[dwMemPos+9];
dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8;
}
// SLEN
if (dwMemPos >= dwMemLength) return TRUE;
if (*((DWORD *)(lpStream + dwMemPos)) == 0x4E454C53)
{
npatterns = lpStream[dwMemPos+9];
dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8;
}
// PLEN
if (dwMemPos >= dwMemLength) return TRUE;
if (*((DWORD *)(lpStream + dwMemPos)) == 0x4E454C50)
{
norders = lpStream[dwMemPos+9];
dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8;
}
// PATT
if (dwMemPos >= dwMemLength) return TRUE;
if (*((DWORD *)(lpStream + dwMemPos)) == 0x54544150)
{
UINT orderlen = norders;
if (orderlen >= MAX_ORDERS) orderlen = MAX_ORDERS-1;
for (UINT i=0; i<orderlen; i++) Order[i] = lpStream[dwMemPos+10+i];
for (UINT j=orderlen; j>1; j--) { if (Order[j-1]) break; Order[j-1] = 0xFF; }
dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8;
}
// PBOD
UINT npat = 0;
while ((dwMemPos+10 < dwMemLength) && (*((DWORD *)(lpStream + dwMemPos)) == 0x444F4250))
{
DWORD dwPos = dwMemPos + 10;
UINT rows = lpStream[dwMemPos+9];
if (!rows) rows = 64;
if (npat < MAX_PATTERNS)
{
if ((Patterns[npat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE;
MODCOMMAND *m = Patterns[npat];
PatternSize[npat] = rows;
UINT imax = m_nChannels*rows;
for (UINT i=0; i<imax; i++, m++, dwPos+=4)
{
if (dwPos+4 > dwMemLength) break;
const BYTE *p = lpStream+dwPos;
UINT note = p[0];
if (note)
{
m->note = note + 48;
m->instr = p[1] + 1;
}
UINT command = p[2];
UINT param = p[3];
m->param = param;
switch(command)
{
// 0: no effect
case 0:
break;
// 1: Portamento Up
case 1:
case 17:
case 30:
if (param) m->command = CMD_PORTAMENTOUP;
break;
// 2: Portamento Down
case 2:
case 13:
case 21:
if (param) m->command = CMD_PORTAMENTODOWN;
break;
// 10: Arpeggio
case 10:
case 11:
case 12:
m->command = CMD_ARPEGGIO;
break;
// 15: Filter
case 15:
m->command = CMD_MODCMDEX;
m->param = param & 0x0F;
break;
// 25: Position Jump
case 25:
m->command = CMD_POSITIONJUMP;
break;
// 28: Set Speed
case 28:
m->command = CMD_SPEED;
break;
// 31: Volume Control
case 31:
if (param <= 0x40) m->command = CMD_VOLUME; else
if (param <= 0x50) { m->command = CMD_VOLUMESLIDE; m->param &= 0x0F; if (!m->param) m->param = 0x0F; } else
if (param <= 0x60) { m->command = CMD_VOLUMESLIDE; m->param = (param & 0x0F) << 4; if (!m->param) m->param = 0xF0; } else
if (param <= 0x70) { m->command = CMD_MODCMDEX; m->param = 0xB0 | (param & 0x0F); if (!(param & 0x0F)) m->param = 0xBF; } else
if (param <= 0x80) { m->command = CMD_MODCMDEX; m->param = 0xA0 | (param & 0x0F); if (!(param & 0x0F)) m->param = 0xAF; }
break;
}
}
}
npat++;
dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8;
}
// SBOD
UINT nsmp = 1;
while ((dwMemPos+10 < dwMemLength) && (*((DWORD *)(lpStream + dwMemPos)) == 0x444F4253))
{
if (nsmp < MAX_SAMPLES) ReadSample(&Ins[nsmp], RS_PCM8S, (LPSTR)(lpStream+dwMemPos+8), dwMemLength-dwMemPos-8);
dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8;
nsmp++;
}
return TRUE;
}

1576
src/modplug/load_pat.cpp Normal file

File diff suppressed because it is too large Load diff

27
src/modplug/load_pat.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef LOAD_PAT_H
#define LOAD_PAT_H
#ifdef __cplusplus
extern "C" {
#endif
void pat_init_patnames(void);
void pat_resetsmp(void);
int pat_numinstr(void);
int pat_numsmp(void);
int pat_smptogm(int smp);
int pat_gmtosmp(int gm);
int pat_gm_drumnr(int n);
int pat_gm_drumnote(int n);
const char *pat_gm_name(int gm);
int pat_modnote(int midinote);
int pat_smplooped(int smp);
//#ifdef NEWMIKMOD
BOOL PAT_Load_Instruments(void *c);
//#endif
#ifdef __cplusplus
}
#endif
#endif

839
src/modplug/load_psm.cpp Normal file
View file

@ -0,0 +1,839 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
///////////////////////////////////////////////////
//
// PSM module loader
//
///////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#define PSM_LOG
#define PSM_ID_NEW 0x204d5350
#define PSM_ID_OLD 0xfe4d5350
#define IFFID_FILE 0x454c4946
#define IFFID_TITL 0x4c544954
#define IFFID_SDFT 0x54464453
#define IFFID_PBOD 0x444f4250
#define IFFID_SONG 0x474e4f53
#define IFFID_PATT 0x54544150
#define IFFID_DSMP 0x504d5344
#define IFFID_OPLH 0x484c504f
#pragma pack(1)
typedef struct _PSMCHUNK
{
DWORD id;
DWORD len;
DWORD listid;
} PSMCHUNK;
typedef struct _PSMSONGHDR
{
CHAR songname[8]; // "MAINSONG"
BYTE reserved1;
BYTE reserved2;
BYTE channels;
} PSMSONGHDR;
typedef struct _PSMPATTERN
{
DWORD size;
DWORD name;
WORD rows;
WORD reserved1;
BYTE data[4];
} PSMPATTERN;
typedef struct _PSMSAMPLE
{
BYTE flags;
CHAR songname[8];
DWORD smpid;
CHAR samplename[34];
DWORD reserved1;
BYTE reserved2;
BYTE insno;
BYTE reserved3;
DWORD length;
DWORD loopstart;
DWORD loopend;
WORD reserved4;
BYTE defvol;
DWORD reserved5;
DWORD samplerate;
BYTE reserved6[19];
} PSMSAMPLE;
#pragma pack()
BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength)
//-----------------------------------------------------------
{
PSMCHUNK *pfh = (PSMCHUNK *)lpStream;
DWORD dwMemPos, dwSongPos;
DWORD smpnames[MAX_SAMPLES];
DWORD patptrs[MAX_PATTERNS];
BYTE samplemap[MAX_SAMPLES];
UINT nPatterns;
// Chunk0: "PSM ",filesize,"FILE"
if (dwMemLength < 256) return FALSE;
if (pfh->id == PSM_ID_OLD)
{
#ifdef PSM_LOG
Log("Old PSM format not supported\n");
#endif
return FALSE;
}
if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE;
m_nType = MOD_TYPE_PSM;
m_nChannels = 16;
m_nSamples = 0;
nPatterns = 0;
dwMemPos = 12;
dwSongPos = 0;
for (UINT iChPan=0; iChPan<16; iChPan++)
{
UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40;
ChnSettings[iChPan].nPan = pan;
}
while (dwMemPos+8 < dwMemLength)
{
PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos);
if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break;
dwMemPos += 8;
PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos);
ULONG len = pchunk->len;
if (len) switch(pchunk->id)
{
// "TITL": Song title
case IFFID_TITL:
if (!pdata[0]) { pdata++; len--; }
memcpy(m_szNames[0], pdata, (len>31) ? 31 : len);
m_szNames[0][31] = 0;
break;
// "PBOD": Pattern
case IFFID_PBOD:
if ((len >= 12) && (nPatterns < MAX_PATTERNS))
{
patptrs[nPatterns++] = dwMemPos-8;
}
break;
// "SONG": Song description
case IFFID_SONG:
if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos))
{
dwSongPos = dwMemPos - 8;
}
break;
// "DSMP": Sample Data
case IFFID_DSMP:
if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES))
{
m_nSamples++;
MODINSTRUMENT *pins = &Ins[m_nSamples];
PSMSAMPLE *psmp = (PSMSAMPLE *)pdata;
smpnames[m_nSamples] = psmp->smpid;
memcpy(m_szNames[m_nSamples], psmp->samplename, 31);
m_szNames[m_nSamples][31] = 0;
samplemap[m_nSamples-1] = (BYTE)m_nSamples;
// Init sample
pins->nGlobalVol = 0x40;
pins->nC4Speed = psmp->samplerate;
pins->nLength = psmp->length;
pins->nLoopStart = psmp->loopstart;
pins->nLoopEnd = psmp->loopend;
pins->nPan = 128;
pins->nVolume = (psmp->defvol+1) * 2;
pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0;
if (pins->nLoopStart > 0) pins->nLoopStart--;
// Point to sample data
pdata += 0x60;
len -= 0x60;
// Load sample data
if ((pins->nLength > 3) && (len > 3))
{
ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len);
} else
{
pins->nLength = 0;
}
}
break;
#if 0
default:
{
CHAR s[8], s2[64];
*(DWORD *)s = pchunk->id;
s[4] = 0;
wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos);
OutputDebugString(s2);
}
#endif
}
dwMemPos += pchunk->len;
}
// Step #1: convert song structure
PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8);
if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE;
m_nChannels = pSong->channels;
// Valid song header -> convert attached chunks
{
DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4);
dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR)
while (dwMemPos + 8 < dwSongEnd)
{
PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos);
dwMemPos += 8;
if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break;
PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos);
ULONG len = pchunk->len;
switch(pchunk->id)
{
case IFFID_OPLH:
if (len >= 0x20)
{
UINT pos = len - 3;
while (pos > 5)
{
BOOL bFound = FALSE;
pos -= 5;
DWORD dwName = *(DWORD *)(pdata+pos);
for (UINT i=0; i<nPatterns; i++)
{
DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name;
if (dwName == dwPatName)
{
bFound = TRUE;
break;
}
}
if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10)
&& (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0))
{
m_nDefaultSpeed = pdata[pos+1];
m_nDefaultTempo = pdata[pos+3];
break;
}
}
UINT iOrd = 0;
while ((pos+5<len) && (iOrd < MAX_ORDERS))
{
DWORD dwName = *(DWORD *)(pdata+pos);
for (UINT i=0; i<nPatterns; i++)
{
DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name;
if (dwName == dwPatName)
{
Order[iOrd++] = i;
break;
}
}
pos += 5;
}
}
break;
}
dwMemPos += pchunk->len;
}
}
// Step #2: convert patterns
for (UINT nPat=0; nPat<nPatterns; nPat++)
{
PSMPATTERN *pPsmPat = (PSMPATTERN *)(lpStream+patptrs[nPat]+8);
ULONG len = *(DWORD *)(lpStream+patptrs[nPat]+4) - 12;
UINT nRows = pPsmPat->rows;
if (len > pPsmPat->size) len = pPsmPat->size;
if ((nRows < 64) || (nRows > 256)) nRows = 64;
PatternSize[nPat] = nRows;
if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break;
MODCOMMAND *m = Patterns[nPat];
BYTE *p = pPsmPat->data;
UINT pos = 0;
UINT row = 0;
UINT oldch = 0;
BOOL bNewRow = FALSE;
#ifdef PSM_LOG
Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream));
#endif
while ((row < nRows) && (pos+1 < len))
{
UINT flags = p[pos++];
UINT ch = p[pos++];
#ifdef PSM_LOG
//Log("flags+ch: %02X.%02X\n", flags, ch);
#endif
if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/)
{
if ((pos+1<len) && (!(p[pos] & 0x0f)) && (p[pos+1] < m_nChannels))
{
#ifdef PSM_LOG
//if (!nPat) Log("Continuing on new row\n");
#endif
row++;
m += m_nChannels;
oldch = ch;
continue;
}
}
if ((pos >= len) || (row >= nRows)) break;
if (!(flags & 0xf0))
{
#ifdef PSM_LOG
//if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]);
#endif
row++;
m += m_nChannels;
bNewRow = TRUE;
oldch = ch;
continue;
}
bNewRow = FALSE;
if (ch >= m_nChannels)
{
#ifdef PSM_LOG
if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch);
#endif
ch = 0;
}
// Note + Instr
if ((flags & 0x40) && (pos+1 < len))
{
UINT note = p[pos++];
UINT nins = p[pos++];
#ifdef PSM_LOG
//if (!nPat) Log("note+ins: %02X.%02X\n", note, nins);
if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins);
#endif
if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1;
m[ch].instr = samplemap[nins];
m[ch].note = note;
}
// Volume
if ((flags & 0x20) && (pos < len))
{
m[ch].volcmd = VOLCMD_VOLUME;
m[ch].vol = p[pos++] / 2;
}
// Effect
if ((flags & 0x10) && (pos+1 < len))
{
UINT command = p[pos++];
UINT param = p[pos++];
// Convert effects
switch(command)
{
// 01: fine volslide up
case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; break;
// 04: fine volslide down
case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break;
// 0C: portamento up
case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break;
// 0E: portamento down
case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break;
// 33: Position Jump
case 0x33: command = CMD_POSITIONJUMP; break;
// 34: Pattern break
case 0x34: command = CMD_PATTERNBREAK; break;
// 3D: speed
case 0x3D: command = CMD_SPEED; break;
// 3E: tempo
case 0x3E: command = CMD_TEMPO; break;
// Unknown
default:
#ifdef PSM_LOG
Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param);
#endif
command = param = 0;
}
m[ch].command = (BYTE)command;
m[ch].param = (BYTE)param;
}
oldch = ch;
}
#ifdef PSM_LOG
if (pos < len)
{
Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos);
}
#endif
}
// Done (finally!)
return TRUE;
}
//////////////////////////////////////////////////////////////
//
// PSM Old Format
//
/*
CONST
c_PSM_MaxOrder = $FF;
c_PSM_MaxSample = $FF;
c_PSM_MaxChannel = $0F;
TYPE
PPSM_Header = ^TPSM_Header;
TPSM_Header = RECORD
PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 }
PSM_SongName : ARRAY[01..58] OF CHAR;
PSM_Byte00 : BYTE;
PSM_Byte1A : BYTE;
PSM_Unknown00 : BYTE;
PSM_Unknown01 : BYTE;
PSM_Unknown02 : BYTE;
PSM_Speed : BYTE;
PSM_Tempo : BYTE;
PSM_Unknown03 : BYTE;
PSM_Unknown04 : WORD;
PSM_OrderLength : WORD;
PSM_PatternNumber : WORD;
PSM_SampleNumber : WORD;
PSM_ChannelNumber : WORD;
PSM_ChannelUsed : WORD;
PSM_OrderPosition : LONGINT;
PSM_ChannelSettingPosition : LONGINT;
PSM_PatternPosition : LONGINT;
PSM_SamplePosition : LONGINT;
{ *** perhaps there are some more infos in a larger header,
but i have not decoded it and so it apears here NOT }
END;
PPSM_Sample = ^TPSM_Sample;
TPSM_Sample = RECORD
PSM_SampleFileName : ARRAY[01..12] OF CHAR;
PSM_SampleByte00 : BYTE;
PSM_SampleName : ARRAY[01..22] OF CHAR;
PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE;
PSM_SamplePosition : LONGINT;
PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE;
PSM_SampleNumber : BYTE;
PSM_SampleFlags : WORD;
PSM_SampleLength : LONGINT;
PSM_SampleLoopBegin : LONGINT;
PSM_SampleLoopEnd : LONGINT;
PSM_Unknown03 : BYTE;
PSM_SampleVolume : BYTE;
PSM_SampleC5Speed : WORD;
END;
PPSM_SampleList = ^TPSM_SampleList;
TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample;
PPSM_Order = ^TPSM_Order;
TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE;
PPSM_ChannelSettings = ^TPSM_ChannelSettings;
TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE;
CONST
PSM_NotesInPattern : BYTE = $00;
PSM_ChannelInPattern : BYTE = $00;
CONST
c_PSM_SetSpeed = 60;
FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT;
BEGIN
END;
PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD);
VAR
Witz : ARRAY[00..04] OF WORD;
I1,I2 : WORD;
I3,I4 : WORD;
TopicalByte : ^BYTE;
Pattern : PUnpackedPattern;
ChannelP : BYTE;
NoteP : BYTE;
InfoByte : BYTE;
CodeByte : BYTE;
InfoWord : WORD;
Effect : BYTE;
Opperand : BYTE;
Panning : BYTE;
Volume : BYTE;
PrevInfo : BYTE;
InfoIndex : BYTE;
BEGIN
Pattern := @Destination;
TopicalByte := @Source;
{ *** Initialize patttern }
FOR I2 := 0 TO c_Maximum_NoteIndex DO
FOR I3 := 0 TO c_Maximum_ChannelIndex DO
BEGIN
Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF;
Pattern^[I2,I3,c_Pattern_SampleIndex] := $00;
Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF;
Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF;
Pattern^[I2,I3,c_Pattern_EffectIndex] := $00;
Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00;
END;
{ *** Byte-pointer on first pattern-entry }
ChannelP := $00;
NoteP := $00;
InfoByte := $00;
PrevInfo := $00;
InfoIndex := $02;
{ *** read notes in pattern }
PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
{ *** unpack pattern }
WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO
BEGIN
{ *** Read info-byte }
InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex);
IF InfoByte <> $00 THEN
BEGIN
ChannelP := InfoByte AND $0F;
IF InfoByte AND 128 = 128 THEN { note and sample }
BEGIN
{ *** read note }
CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
DEC(CodeByte);
CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2;
Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte;
{ *** read sample }
CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte;
END;
IF InfoByte AND 64 = 64 THEN { Volume }
BEGIN
CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte;
END;
IF InfoByte AND 32 = 32 THEN { effect AND opperand }
BEGIN
Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength);
CASE Effect OF
c_PSM_SetSpeed:
BEGIN
Effect := c_I_Set_Speed;
END;
ELSE
BEGIN
Effect := c_I_NoEffect;
Opperand := $00;
END;
END;
Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect;
Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand;
END;
END ELSE INC(NoteP);
END;
END;
PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD);
{ *** caution : Module has to be inited before!!!! }
VAR
Header : PPSM_Header;
Sample : PPSM_SampleList;
Order : PPSM_Order;
ChannelSettings : PPSM_ChannelSettings;
MultiPurposeBuffer : PByteArray;
PatternBuffer : PUnpackedPattern;
TopicalParaPointer : WORD;
InFile : FILE;
I1,I2 : WORD;
I3,I4 : WORD;
TempW : WORD;
TempB : BYTE;
TempP : PByteArray;
TempI : INTEGER;
{ *** copy-vars for loop-extension }
CopySource : LONGINT;
CopyDestination : LONGINT;
CopyLength : LONGINT;
BEGIN
{ *** try to open file }
ASSIGN(InFile,FileName);
{$I-}
RESET(InFile,1);
{$I+}
IF IORESULT <> $00 THEN
BEGIN
EXIT;
END;
{$I-}
{ *** seek start of module }
IF FILESIZE(InFile) < FilePosition THEN
BEGIN
EXIT;
END;
SEEK(InFile,FilePosition);
{ *** look for enough memory for temporary variables }
IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) +
SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) +
SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern)
THEN
BEGIN
EXIT;
END;
{ *** init dynamic variables }
NEW(Header);
NEW(Sample);
NEW(Order);
NEW(ChannelSettings);
NEW(MultiPurposeBuffer);
NEW(PatternBuffer);
{ *** read header }
BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header));
{ *** test if this is a DSM-file }
IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND
(Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN
BEGIN
ErrorCode := c_NoValidFileFormat;
CLOSE(InFile);
EXIT;
END;
{ *** read order }
SEEK(InFile,FilePosition + Header^.PSM_OrderPosition);
BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength);
{ *** read channelsettings }
SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition);
BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings));
{ *** read samplelist }
SEEK(InFile,FilePosition + Header^.PSM_SamplePosition);
BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample));
{ *** copy header to intern NTMIK-structure }
Module^.Module_Sign := 'MF';
Module^.Module_FileFormatVersion := $0100;
Module^.Module_SampleNumber := Header^.PSM_SampleNumber;
Module^.Module_PatternNumber := Header^.PSM_PatternNumber;
Module^.Module_OrderLength := Header^.PSM_OrderLength;
Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1;
Module^.Module_Initial_GlobalVolume := 64;
Module^.Module_Initial_MasterVolume := $C0;
Module^.Module_Initial_Speed := Header^.PSM_Speed;
Module^.Module_Initial_Tempo := Header^.PSM_Tempo;
{ *** paragraph 01 start }
Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) +
c_Module_Flags_Stereo * BYTE(1) +
c_Module_Flags_ForceAmigaLimits * BYTE(0) +
c_Module_Flags_Panning * BYTE(1) +
c_Module_Flags_Surround * BYTE(1) +
c_Module_Flags_QualityMixing * BYTE(1) +
c_Module_Flags_FastVolumeSlides * BYTE(0) +
c_Module_Flags_SpecialCustomData * BYTE(0) +
c_Module_Flags_SongName * BYTE(1);
I1 := $01;
WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO
BEGIN
Module^.Module_Name[I1] := Header^.PSM_SongName[I1];
INC(I1);
END;
Module^.Module_Name[c_Module_SongNameLength] := #00;
{ *** Init channelsettings }
FOR I1 := 0 TO c_Maximum_ChannelIndex DO
BEGIN
IF I1 < Header^.PSM_ChannelUsed THEN
BEGIN
{ *** channel enabled }
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64;
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08;
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) +
c_ChannelSettings_Code_ChannelEnabled * BYTE(1) +
c_ChannelSettings_Code_ChannelDigital * BYTE(1);
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls :=
c_ChannelSettings_Controls_EnhancedMode * BYTE(1) +
c_ChannelSettings_Controls_SurroundMode * BYTE(0);
END
ELSE
BEGIN
{ *** channel disabled }
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00;
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00;
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00;
Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00;
END;
END;
{ *** init and copy order }
FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF);
MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength);
{ *** read pattern }
SEEK(InFile,FilePosition + Header^.PSM_PatternPosition);
NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1;
FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO
BEGIN
NTMIK_LoadPatternProcedure;
{ *** read length }
BLOCKREAD(InFile,TempW,2);
{ *** read pattern }
BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2);
{ *** unpack pattern and set notes per channel to 64 }
PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW);
NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern);
TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00];
GETMEM(Module^.Module_PatternPointer^[I1],TempW);
MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW);
{ *** next pattern }
END;
{ *** read samples }
NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber;
FOR I1 := 1 TO Header^.PSM_SampleNumber DO
BEGIN
NTMIK_LoadSampleProcedure;
{ *** get index for sample }
I3 := Sample^[I1].PSM_SampleNumber;
{ *** clip PSM-sample }
IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength
THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength;
{ *** init intern sample }
NEW(Module^.Module_SamplePointer^[I3]);
FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00);
FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32);
FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32);
{ *** copy informations to intern sample }
I2 := $01;
WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO
BEGIN
Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2];
INC(I2);
END;
Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF';
Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100;
Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000;
Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000;
Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume;
Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00;
Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed;
Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength;
Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin;
Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd;
{ *** now it's time for the flags }
Module^.Module_SamplePointer^[I3]^.Sample_Flags :=
c_Sample_Flags_DigitalSample * BYTE(1) +
c_Sample_Flags_8BitSample * BYTE(1) +
c_Sample_Flags_UnsignedSampleData * BYTE(1) +
c_Sample_Flags_Packed * BYTE(0) +
c_Sample_Flags_LoopCounter * BYTE(0) +
c_Sample_Flags_SampleName * BYTE(1) +
c_Sample_Flags_LoopActive *
BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15));
{ *** alloc memory for sample-data }
E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector,
Module^.Module_SamplePointer^[I3]^.Sample_Position,
Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize);
{ *** read out data }
EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector;
EPT(TempP).p_Offset := $0000;
SEEK(InFile,Sample^[I1].PSM_SamplePosition);
E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length);
{ *** 'coz the samples are signed in a DSM-file -> PC-fy them }
IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN
BEGIN
CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length;
{ *** decode sample }
ASM
DB 066h; MOV CX,WORD PTR CopyLength
{ *** load sample selector }
MOV ES,WORD PTR TempP[00002h]
DB 066h; XOR SI,SI
DB 066h; XOR DI,DI
XOR AH,AH
{ *** conert all bytes }
@@MainLoop:
DB 026h; DB 067h; LODSB
ADD AL,AH
MOV AH,AL
DB 067h; STOSB
DB 066h; LOOP @@MainLoop
END;
{ *** make samples unsigned }
ASM
DB 066h; MOV CX,WORD PTR CopyLength
{ *** load sample selector }
MOV ES,WORD PTR TempP[00002h]
DB 066h; XOR SI,SI
DB 066h; XOR DI,DI
{ *** conert all bytes }
@@MainLoop:
DB 026h; DB 067h; LODSB
SUB AL,080h
DB 067h; STOSB
DB 066h; LOOP @@MainLoop
END;
{ *** Create Loop-Extension }
IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN
BEGIN
CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin;
CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd;
CopyLength := CopyDestination - CopySource;
ASM
{ *** load sample-selector }
MOV ES,WORD PTR TempP[00002h]
DB 066h; MOV DI,WORD PTR CopyDestination
{ *** calculate number of full sample-loops to copy }
XOR DX,DX
MOV AX,c_LoopExtensionSize
MOV BX,WORD PTR CopyLength
DIV BX
OR AX,AX
JE @@NoFullLoop
{ *** copy some full-loops (size=bx) }
MOV CX,AX
@@InnerLoop:
PUSH CX
DB 066h; MOV SI,WORD PTR CopySource
MOV CX,BX
DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] }
POP CX
LOOP @@InnerLoop
@@NoFullLoop:
{ *** calculate number of rest-bytes to copy }
DB 066h; MOV SI,WORD PTR CopySource
MOV CX,DX
DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] }
END;
END
ELSE
BEGIN
CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length;
ASM
{ *** load sample-selector }
MOV ES,WORD PTR TempP[00002h]
DB 066h; MOV DI,WORD PTR CopyDestination
{ *** clear extension }
MOV CX,c_LoopExtensionSize
MOV AL,080h
DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] }
END;
END;
END;
{ *** next sample }
END;
{ *** init period-ranges }
NTMIK_MaximumPeriod := $0000D600 SHR 1;
NTMIK_MinimumPeriod := $0000D600 SHR 8;
{ *** close file }
CLOSE(InFile);
{ *** dispose all dynamic variables }
DISPOSE(Header);
DISPOSE(Sample);
DISPOSE(Order);
DISPOSE(ChannelSettings);
DISPOSE(MultiPurposeBuffer);
DISPOSE(PatternBuffer);
{ *** set errorcode to noerror }
ErrorCode := c_NoError;
END;
*/

207
src/modplug/load_ptm.cpp Normal file
View file

@ -0,0 +1,207 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
//////////////////////////////////////////////
// PTM PolyTracker module loader //
//////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
#pragma pack(1)
typedef struct PTMFILEHEADER
{
CHAR songname[28]; // name of song, asciiz string
CHAR eof; // 26
BYTE version_lo; // 03 version of file, currently 0203h
BYTE version_hi; // 02
BYTE reserved1; // reserved, set to 0
WORD norders; // number of orders (0..256)
WORD nsamples; // number of instruments (1..255)
WORD npatterns; // number of patterns (1..128)
WORD nchannels; // number of channels (voices) used (1..32)
WORD fileflags; // set to 0
WORD reserved2; // reserved, set to 0
DWORD ptmf_id; // song identification, 'PTMF' or 0x464d5450
BYTE reserved3[16]; // reserved, set to 0
BYTE chnpan[32]; // channel panning settings, 0..15, 0 = left, 7 = middle, 15 = right
BYTE orders[256]; // order list, valid entries 0..nOrders-1
WORD patseg[128]; // pattern offsets (*16)
} PTMFILEHEADER, *LPPTMFILEHEADER;
#define SIZEOF_PTMFILEHEADER 608
typedef struct PTMSAMPLE
{
BYTE sampletype; // sample type (bit array)
CHAR filename[12]; // name of external sample file
BYTE volume; // default volume
WORD nC4Spd; // C4 speed
WORD sampleseg; // sample segment (used internally)
WORD fileofs[2]; // offset of sample data
WORD length[2]; // sample size (in bytes)
WORD loopbeg[2]; // start of loop
WORD loopend[2]; // end of loop
WORD gusdata[8];
char samplename[28]; // name of sample, asciiz // changed from CHAR
DWORD ptms_id; // sample identification, 'PTMS' or 0x534d5450
} PTMSAMPLE;
#define SIZEOF_PTMSAMPLE 80
#pragma pack()
BOOL CSoundFile::ReadPTM(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
PTMFILEHEADER pfh = *(LPPTMFILEHEADER)lpStream;
DWORD dwMemPos;
UINT nOrders;
pfh.norders = bswapLE16(pfh.norders);
pfh.nsamples = bswapLE16(pfh.nsamples);
pfh.npatterns = bswapLE16(pfh.npatterns);
pfh.nchannels = bswapLE16(pfh.nchannels);
pfh.fileflags = bswapLE16(pfh.fileflags);
pfh.reserved2 = bswapLE16(pfh.reserved2);
pfh.ptmf_id = bswapLE32(pfh.ptmf_id);
for (UINT j=0; j<128; j++)
{
pfh.patseg[j] = bswapLE16(pfh.patseg[j]);
}
if ((!lpStream) || (dwMemLength < 1024)) return FALSE;
if ((pfh.ptmf_id != 0x464d5450) || (!pfh.nchannels)
|| (pfh.nchannels > 32)
|| (pfh.norders > 256) || (!pfh.norders)
|| (!pfh.nsamples) || (pfh.nsamples > 255)
|| (!pfh.npatterns) || (pfh.npatterns > 128)
|| (SIZEOF_PTMFILEHEADER+pfh.nsamples*SIZEOF_PTMSAMPLE >= (int)dwMemLength)) return FALSE;
memcpy(m_szNames[0], pfh.songname, 28);
m_szNames[0][28] = 0;
m_nType = MOD_TYPE_PTM;
m_nChannels = pfh.nchannels;
m_nSamples = (pfh.nsamples < MAX_SAMPLES) ? pfh.nsamples : MAX_SAMPLES-1;
dwMemPos = SIZEOF_PTMFILEHEADER;
nOrders = (pfh.norders < MAX_ORDERS) ? pfh.norders : MAX_ORDERS-1;
memcpy(Order, pfh.orders, nOrders);
for (UINT ipan=0; ipan<m_nChannels; ipan++)
{
ChnSettings[ipan].nVolume = 64;
ChnSettings[ipan].nPan = ((pfh.chnpan[ipan] & 0x0F) << 4) + 4;
}
for (UINT ismp=0; ismp<m_nSamples; ismp++, dwMemPos += SIZEOF_PTMSAMPLE)
{
MODINSTRUMENT *pins = &Ins[ismp+1];
PTMSAMPLE *psmp = (PTMSAMPLE *)(lpStream+dwMemPos);
lstrcpyn(m_szNames[ismp+1], psmp->samplename, 28);
memcpy(pins->name, psmp->filename, 12);
pins->name[12] = 0;
pins->nGlobalVol = 64;
pins->nPan = 128;
pins->nVolume = psmp->volume << 2;
pins->nC4Speed = bswapLE16(psmp->nC4Spd) << 1;
pins->uFlags = 0;
if ((psmp->sampletype & 3) == 1)
{
UINT smpflg = RS_PCM8D;
DWORD samplepos;
pins->nLength = bswapLE32(*(LPDWORD)(psmp->length));
pins->nLoopStart = bswapLE32(*(LPDWORD)(psmp->loopbeg));
pins->nLoopEnd = bswapLE32(*(LPDWORD)(psmp->loopend));
samplepos = bswapLE32(*(LPDWORD)(&psmp->fileofs));
if (psmp->sampletype & 4) pins->uFlags |= CHN_LOOP;
if (psmp->sampletype & 8) pins->uFlags |= CHN_PINGPONGLOOP;
if (psmp->sampletype & 16)
{
pins->uFlags |= CHN_16BIT;
pins->nLength >>= 1;
pins->nLoopStart >>= 1;
pins->nLoopEnd >>= 1;
smpflg = RS_PTM8DTO16;
}
if ((pins->nLength) && (samplepos) && (samplepos < dwMemLength))
{
ReadSample(pins, smpflg, (LPSTR)(lpStream+samplepos), dwMemLength-samplepos);
}
}
}
// Reading Patterns
for (UINT ipat=0; ipat<pfh.npatterns; ipat++)
{
dwMemPos = ((UINT)pfh.patseg[ipat]) << 4;
if ((!dwMemPos) || (dwMemPos >= dwMemLength)) continue;
PatternSize[ipat] = 64;
if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break;
//
MODCOMMAND *m = Patterns[ipat];
for (UINT row=0; ((row < 64) && (dwMemPos < dwMemLength)); )
{
UINT b = lpStream[dwMemPos++];
if (dwMemPos >= dwMemLength) break;
if (b)
{
UINT nChn = b & 0x1F;
if (b & 0x20)
{
if (dwMemPos + 2 > dwMemLength) break;
m[nChn].note = lpStream[dwMemPos++];
m[nChn].instr = lpStream[dwMemPos++];
}
if (b & 0x40)
{
if (dwMemPos + 2 > dwMemLength) break;
m[nChn].command = lpStream[dwMemPos++];
m[nChn].param = lpStream[dwMemPos++];
if ((m[nChn].command == 0x0E) && ((m[nChn].param & 0xF0) == 0x80))
{
m[nChn].command = CMD_S3MCMDEX;
} else
if (m[nChn].command < 0x10)
{
ConvertModCommand(&m[nChn]);
} else
{
switch(m[nChn].command)
{
case 16:
m[nChn].command = CMD_GLOBALVOLUME;
break;
case 17:
m[nChn].command = CMD_RETRIG;
break;
case 18:
m[nChn].command = CMD_FINEVIBRATO;
break;
default:
m[nChn].command = 0;
}
}
}
if (b & 0x80)
{
if (dwMemPos >= dwMemLength) break;
m[nChn].volcmd = VOLCMD_VOLUME;
m[nChn].vol = lpStream[dwMemPos++];
}
} else
{
row++;
m += m_nChannels;
}
}
}
return TRUE;
}

650
src/modplug/load_s3m.cpp Normal file
View file

@ -0,0 +1,650 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
extern WORD S3MFineTuneTable[16];
//////////////////////////////////////////////////////
// ScreamTracker S3M file support
typedef struct tagS3MSAMPLESTRUCT
{
BYTE type;
CHAR dosname[12];
BYTE hmem;
WORD memseg;
DWORD length;
DWORD loopbegin;
DWORD loopend;
BYTE vol;
BYTE bReserved;
BYTE pack;
BYTE flags;
DWORD finetune;
DWORD dwReserved;
WORD intgp;
WORD int512;
DWORD lastused;
CHAR name[28];
CHAR scrs[4];
} S3MSAMPLESTRUCT;
typedef struct tagS3MFILEHEADER
{
CHAR name[28];
BYTE b1A;
BYTE type;
WORD reserved1;
WORD ordnum;
WORD insnum;
WORD patnum;
WORD flags;
WORD cwtv;
WORD version;
DWORD scrm; // "SCRM" = 0x4D524353
BYTE globalvol;
BYTE speed;
BYTE tempo;
BYTE mastervol;
BYTE ultraclicks;
BYTE panning_present;
BYTE reserved2[8];
WORD special;
BYTE channels[32];
} S3MFILEHEADER;
void CSoundFile::S3MConvert(MODCOMMAND *m, BOOL bIT) const
//--------------------------------------------------------
{
UINT command = m->command;
UINT param = m->param;
switch (command + 0x40)
{
case 'A': command = CMD_SPEED; break;
case 'B': command = CMD_POSITIONJUMP; break;
case 'C': command = CMD_PATTERNBREAK; if (!bIT) param = (param >> 4) * 10 + (param & 0x0F); break;
case 'D': command = CMD_VOLUMESLIDE; break;
case 'E': command = CMD_PORTAMENTODOWN; break;
case 'F': command = CMD_PORTAMENTOUP; break;
case 'G': command = CMD_TONEPORTAMENTO; break;
case 'H': command = CMD_VIBRATO; break;
case 'I': command = CMD_TREMOR; break;
case 'J': command = CMD_ARPEGGIO; break;
case 'K': command = CMD_VIBRATOVOL; break;
case 'L': command = CMD_TONEPORTAVOL; break;
case 'M': command = CMD_CHANNELVOLUME; break;
case 'N': command = CMD_CHANNELVOLSLIDE; break;
case 'O': command = CMD_OFFSET; break;
case 'P': command = CMD_PANNINGSLIDE; break;
case 'Q': command = CMD_RETRIG; break;
case 'R': command = CMD_TREMOLO; break;
case 'S': command = CMD_S3MCMDEX; break;
case 'T': command = CMD_TEMPO; break;
case 'U': command = CMD_FINEVIBRATO; break;
case 'V': command = CMD_GLOBALVOLUME; break;
case 'W': command = CMD_GLOBALVOLSLIDE; break;
case 'X': command = CMD_PANNING8; break;
case 'Y': command = CMD_PANBRELLO; break;
case 'Z': command = CMD_MIDI; break;
default: command = 0;
}
m->command = command;
m->param = param;
}
void CSoundFile::S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const
//---------------------------------------------------------------------
{
UINT command = *pcmd;
UINT param = *pprm;
switch(command)
{
case CMD_SPEED: command = 'A'; break;
case CMD_POSITIONJUMP: command = 'B'; break;
case CMD_PATTERNBREAK: command = 'C'; if (!bIT) param = ((param / 10) << 4) + (param % 10); break;
case CMD_VOLUMESLIDE: command = 'D'; break;
case CMD_PORTAMENTODOWN: command = 'E'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
case CMD_PORTAMENTOUP: command = 'F'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break;
case CMD_TONEPORTAMENTO: command = 'G'; break;
case CMD_VIBRATO: command = 'H'; break;
case CMD_TREMOR: command = 'I'; break;
case CMD_ARPEGGIO: command = 'J'; break;
case CMD_VIBRATOVOL: command = 'K'; break;
case CMD_TONEPORTAVOL: command = 'L'; break;
case CMD_CHANNELVOLUME: command = 'M'; break;
case CMD_CHANNELVOLSLIDE: command = 'N'; break;
case CMD_OFFSET: command = 'O'; break;
case CMD_PANNINGSLIDE: command = 'P'; break;
case CMD_RETRIG: command = 'Q'; break;
case CMD_TREMOLO: command = 'R'; break;
case CMD_S3MCMDEX: command = 'S'; break;
case CMD_TEMPO: command = 'T'; break;
case CMD_FINEVIBRATO: command = 'U'; break;
case CMD_GLOBALVOLUME: command = 'V'; break;
case CMD_GLOBALVOLSLIDE: command = 'W'; break;
case CMD_PANNING8:
command = 'X';
if ((bIT) && (m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM))
{
if (param == 0xA4) { command = 'S'; param = 0x91; } else
if (param <= 0x80) { param <<= 1; if (param > 255) param = 255; } else
command = param = 0;
} else
if ((!bIT) && ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)))
{
param >>= 1;
}
break;
case CMD_PANBRELLO: command = 'Y'; break;
case CMD_MIDI: command = 'Z'; break;
case CMD_XFINEPORTAUPDOWN:
if (param & 0x0F) switch(param & 0xF0)
{
case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break;
case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break;
case 0x90: command = 'S'; break;
default: command = param = 0;
} else command = param = 0;
break;
case CMD_MODCMDEX:
command = 'S';
switch(param & 0xF0)
{
case 0x00: command = param = 0; break;
case 0x10: command = 'F'; param |= 0xF0; break;
case 0x20: command = 'E'; param |= 0xF0; break;
case 0x30: param = (param & 0x0F) | 0x10; break;
case 0x40: param = (param & 0x0F) | 0x30; break;
case 0x50: param = (param & 0x0F) | 0x20; break;
case 0x60: param = (param & 0x0F) | 0xB0; break;
case 0x70: param = (param & 0x0F) | 0x40; break;
case 0x90: command = 'Q'; param &= 0x0F; break;
case 0xA0: if (param & 0x0F) { command = 'D'; param = (param << 4) | 0x0F; } else command=param=0; break;
case 0xB0: if (param & 0x0F) { command = 'D'; param |= 0xF0; } else command=param=0; break;
}
break;
default: command = param = 0;
}
command &= ~0x40;
*pcmd = command;
*pprm = param;
}
BOOL CSoundFile::ReadS3M(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
UINT insnum,patnum,nins,npat;
DWORD insfile[128];
WORD ptr[256];
BYTE s[1024];
DWORD dwMemPos;
BYTE insflags[128], inspack[128];
S3MFILEHEADER psfh = *(S3MFILEHEADER *)lpStream;
psfh.reserved1 = bswapLE16(psfh.reserved1);
psfh.ordnum = bswapLE16(psfh.ordnum);
psfh.insnum = bswapLE16(psfh.insnum);
psfh.patnum = bswapLE16(psfh.patnum);
psfh.flags = bswapLE16(psfh.flags);
psfh.cwtv = bswapLE16(psfh.cwtv);
psfh.version = bswapLE16(psfh.version);
psfh.scrm = bswapLE32(psfh.scrm);
psfh.special = bswapLE16(psfh.special);
if ((!lpStream) || (dwMemLength <= sizeof(S3MFILEHEADER)+sizeof(S3MSAMPLESTRUCT)+64)) return FALSE;
if (psfh.scrm != 0x4D524353) return FALSE;
dwMemPos = 0x60;
m_nType = MOD_TYPE_S3M;
memset(m_szNames,0,sizeof(m_szNames));
memcpy(m_szNames[0], psfh.name, 28);
// Speed
m_nDefaultSpeed = psfh.speed;
if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 6;
if (m_nDefaultSpeed > 0x1F) m_nDefaultSpeed = 0x1F;
// Tempo
m_nDefaultTempo = psfh.tempo;
if (m_nDefaultTempo < 40) m_nDefaultTempo = 40;
if (m_nDefaultTempo > 240) m_nDefaultTempo = 240;
// Global Volume
m_nDefaultGlobalVolume = psfh.globalvol << 2;
if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256;
m_nSongPreAmp = psfh.mastervol & 0x7F;
// Channels
m_nChannels = 4;
for (UINT ich=0; ich<32; ich++)
{
ChnSettings[ich].nPan = 128;
ChnSettings[ich].nVolume = 64;
ChnSettings[ich].dwFlags = CHN_MUTE;
if (psfh.channels[ich] != 0xFF)
{
m_nChannels = ich+1;
UINT b = psfh.channels[ich] & 0x0F;
ChnSettings[ich].nPan = (b & 8) ? 0xC0 : 0x40;
ChnSettings[ich].dwFlags = 0;
}
}
if (m_nChannels < 4) m_nChannels = 4;
if ((psfh.cwtv < 0x1320) || (psfh.flags & 0x40)) m_dwSongFlags |= SONG_FASTVOLSLIDES;
// Reading pattern order
UINT iord = psfh.ordnum;
if (iord<1) iord = 1;
if (iord > MAX_ORDERS) iord = MAX_ORDERS;
if (iord)
{
memcpy(Order, lpStream+dwMemPos, iord);
dwMemPos += iord;
}
if ((iord & 1) && (lpStream[dwMemPos] == 0xFF)) dwMemPos++;
// Reading file pointers
insnum = nins = psfh.insnum;
if (insnum >= MAX_SAMPLES) insnum = MAX_SAMPLES-1;
m_nSamples = insnum;
patnum = npat = psfh.patnum;
if (patnum > MAX_PATTERNS) patnum = MAX_PATTERNS;
memset(ptr, 0, sizeof(ptr));
if (nins+npat)
{
memcpy(ptr, lpStream+dwMemPos, 2*(nins+npat));
dwMemPos += 2*(nins+npat);
for (UINT j = 0; j < (nins+npat); ++j) {
ptr[j] = bswapLE16(ptr[j]);
}
if (psfh.panning_present == 252)
{
const BYTE *chnpan = lpStream+dwMemPos;
for (UINT i=0; i<32; i++) if (chnpan[i] & 0x20)
{
ChnSettings[i].nPan = ((chnpan[i] & 0x0F) << 4) + 8;
}
}
}
if (!m_nChannels) return TRUE;
// Reading instrument headers
memset(insfile, 0, sizeof(insfile));
for (UINT iSmp=1; iSmp<=insnum; iSmp++)
{
UINT nInd = ((DWORD)ptr[iSmp-1])*16;
if ((!nInd) || (nInd + 0x50 > dwMemLength)) continue;
memcpy(s, lpStream+nInd, 0x50);
memcpy(Ins[iSmp].name, s+1, 12);
insflags[iSmp-1] = s[0x1F];
inspack[iSmp-1] = s[0x1E];
s[0x4C] = 0;
lstrcpy(m_szNames[iSmp], (LPCSTR)&s[0x30]);
if ((s[0]==1) && (s[0x4E]=='R') && (s[0x4F]=='S'))
{
UINT j = bswapLE32(*((LPDWORD)(s+0x10)));
if (j > MAX_SAMPLE_LENGTH) j = MAX_SAMPLE_LENGTH;
if (j < 4) j = 0;
Ins[iSmp].nLength = j;
j = bswapLE32(*((LPDWORD)(s+0x14)));
if (j >= Ins[iSmp].nLength) j = Ins[iSmp].nLength - 1;
Ins[iSmp].nLoopStart = j;
j = bswapLE32(*((LPDWORD)(s+0x18)));
if (j > MAX_SAMPLE_LENGTH) j = MAX_SAMPLE_LENGTH;
if (j < 4) j = 0;
if (j > Ins[iSmp].nLength) j = Ins[iSmp].nLength;
Ins[iSmp].nLoopEnd = j;
j = s[0x1C];
if (j > 64) j = 64;
Ins[iSmp].nVolume = j << 2;
Ins[iSmp].nGlobalVol = 64;
if (s[0x1F]&1) Ins[iSmp].uFlags |= CHN_LOOP;
j = bswapLE32(*((LPDWORD)(s+0x20)));
if (!j) j = 8363;
if (j < 1024) j = 1024;
Ins[iSmp].nC4Speed = j;
insfile[iSmp] = ((DWORD)bswapLE16(*((LPWORD)(s+0x0E)))) << 4;
insfile[iSmp] += ((DWORD)(BYTE)s[0x0D]) << 20;
if (insfile[iSmp] > dwMemLength) insfile[iSmp] &= 0xFFFF;
if ((Ins[iSmp].nLoopStart >= Ins[iSmp].nLoopEnd) || (Ins[iSmp].nLoopEnd - Ins[iSmp].nLoopStart < 8))
Ins[iSmp].nLoopStart = Ins[iSmp].nLoopEnd = 0;
Ins[iSmp].nPan = 0x80;
}
}
// Reading patterns
for (UINT iPat=0; iPat<patnum; iPat++)
{
UINT nInd = ((DWORD)ptr[nins+iPat]) << 4;
if (nInd + 0x40 > dwMemLength) continue;
WORD len = bswapLE16(*((WORD *)(lpStream+nInd)));
nInd += 2;
PatternSize[iPat] = 64;
if ((!len) || (nInd + len > dwMemLength - 6)
|| ((Patterns[iPat] = AllocatePattern(64, m_nChannels)) == NULL)) continue;
LPBYTE src = (LPBYTE)(lpStream+nInd);
// Unpacking pattern
MODCOMMAND *p = Patterns[iPat];
UINT row = 0;
UINT j = 0;
while (j < len)
{
BYTE b = src[j++];
if (!b)
{
if (++row >= 64) break;
} else
{
UINT chn = b & 0x1F;
if (chn < m_nChannels)
{
MODCOMMAND *m = &p[row*m_nChannels+chn];
if (b & 0x20)
{
m->note = src[j++];
if (m->note < 0xF0) m->note = (m->note & 0x0F) + 12*(m->note >> 4) + 13;
else if (m->note == 0xFF) m->note = 0;
m->instr = src[j++];
}
if (b & 0x40)
{
UINT vol = src[j++];
if ((vol >= 128) && (vol <= 192))
{
vol -= 128;
m->volcmd = VOLCMD_PANNING;
} else
{
if (vol > 64) vol = 64;
m->volcmd = VOLCMD_VOLUME;
}
m->vol = vol;
}
if (b & 0x80)
{
m->command = src[j++];
m->param = src[j++];
if (m->command) S3MConvert(m, FALSE);
}
} else
{
if (b & 0x20) j += 2;
if (b & 0x40) j++;
if (b & 0x80) j += 2;
}
if (j >= len) break;
}
}
}
// Reading samples
for (UINT iRaw=1; iRaw<=insnum; iRaw++) if ((Ins[iRaw].nLength) && (insfile[iRaw]))
{
UINT flags = (psfh.version == 1) ? RS_PCM8S : RS_PCM8U;
if (insflags[iRaw-1] & 4) flags += 5;
if (insflags[iRaw-1] & 2) flags |= RSF_STEREO;
if (inspack[iRaw-1] == 4) flags = RS_ADPCM4;
dwMemPos = insfile[iRaw];
dwMemPos += ReadSample(&Ins[iRaw], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
}
m_nMinPeriod = 64;
m_nMaxPeriod = 32767;
if (psfh.flags & 0x10) m_dwSongFlags |= SONG_AMIGALIMITS;
return TRUE;
}
#ifndef MODPLUG_NO_FILESAVE
#pragma warning(disable:4100)
static BYTE S3MFiller[16] =
{
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};
BOOL CSoundFile::SaveS3M(LPCSTR lpszFileName, UINT nPacking)
//----------------------------------------------------------
{
FILE *f;
BYTE header[0x60];
UINT nbo,nbi,nbp,i;
WORD patptr[128];
WORD insptr[128];
BYTE buffer[5*1024];
S3MSAMPLESTRUCT insex[128];
if ((!m_nChannels) || (!lpszFileName)) return FALSE;
if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE;
// Writing S3M header
memset(header, 0, sizeof(header));
memset(insex, 0, sizeof(insex));
memcpy(header, m_szNames[0], 0x1C);
header[0x1B] = 0;
header[0x1C] = 0x1A;
header[0x1D] = 0x10;
nbo = (GetNumPatterns() + 15) & 0xF0;
if (!nbo) nbo = 16;
header[0x20] = nbo & 0xFF;
header[0x21] = nbo >> 8;
nbi = m_nInstruments;
if (!nbi) nbi = m_nSamples;
if (nbi > 99) nbi = 99;
header[0x22] = nbi & 0xFF;
header[0x23] = nbi >> 8;
nbp = 0;
for (i=0; Patterns[i]; i++) { nbp = i+1; if (nbp >= MAX_PATTERNS) break; }
for (i=0; i<MAX_ORDERS; i++) if ((Order[i] < MAX_PATTERNS) && (Order[i] >= nbp)) nbp = Order[i] + 1;
header[0x24] = nbp & 0xFF;
header[0x25] = nbp >> 8;
if (m_dwSongFlags & SONG_FASTVOLSLIDES) header[0x26] |= 0x40;
if ((m_nMaxPeriod < 20000) || (m_dwSongFlags & SONG_AMIGALIMITS)) header[0x26] |= 0x10;
header[0x28] = 0x20;
header[0x29] = 0x13;
header[0x2A] = 0x02; // Version = 1 => Signed samples
header[0x2B] = 0x00;
header[0x2C] = 'S';
header[0x2D] = 'C';
header[0x2E] = 'R';
header[0x2F] = 'M';
header[0x30] = m_nDefaultGlobalVolume >> 2;
header[0x31] = m_nDefaultSpeed;
header[0x32] = m_nDefaultTempo;
header[0x33] = ((m_nSongPreAmp < 0x20) ? 0x20 : m_nSongPreAmp) | 0x80; // Stereo
header[0x35] = 0xFC;
for (i=0; i<32; i++)
{
if (i < m_nChannels)
{
UINT tmp = (i & 0x0F) >> 1;
header[0x40+i] = (i & 0x10) | ((i & 1) ? 8+tmp : tmp);
} else header[0x40+i] = 0xFF;
}
fwrite(header, 0x60, 1, f);
fwrite(Order, nbo, 1, f);
memset(patptr, 0, sizeof(patptr));
memset(insptr, 0, sizeof(insptr));
UINT ofs0 = 0x60 + nbo;
UINT ofs1 = ((0x60 + nbo + nbi*2 + nbp*2 + 15) & 0xFFF0) + 0x20;
UINT ofs = ofs1;
for (i=0; i<nbi; i++) insptr[i] = (WORD)((ofs + i*0x50) / 16);
for (i=0; i<nbp; i++) patptr[i] = (WORD)((ofs + nbi*0x50) / 16);
fwrite(insptr, nbi, 2, f);
fwrite(patptr, nbp, 2, f);
if (header[0x35] == 0xFC)
{
BYTE chnpan[32];
for (i=0; i<32; i++)
{
chnpan[i] = 0x20 | (ChnSettings[i].nPan >> 4);
}
fwrite(chnpan, 0x20, 1, f);
}
if ((nbi*2+nbp*2) & 0x0F)
{
fwrite(S3MFiller, 0x10 - ((nbi*2+nbp*2) & 0x0F), 1, f);
}
ofs1 = ftell(f);
fwrite(insex, nbi, 0x50, f);
// Packing patterns
ofs += nbi*0x50;
for (i=0; i<nbp; i++)
{
WORD len = 64;
memset(buffer, 0, sizeof(buffer));
patptr[i] = ofs / 16;
if (Patterns[i])
{
len = 2;
MODCOMMAND *p = Patterns[i];
for (int row=0; row<64; row++) if (row < PatternSize[i])
{
for (UINT j=0; j<m_nChannels; j++)
{
UINT b = j;
MODCOMMAND *m = &p[row*m_nChannels+j];
UINT note = m->note;
UINT volcmd = m->volcmd;
UINT vol = m->vol;
UINT command = m->command;
UINT param = m->param;
if ((note) || (m->instr)) b |= 0x20;
if (!note) note = 0xFF; else
if (note >= 0xFE) note = 0xFE; else
if (note < 13) note = 0; else note -= 13;
if (note < 0xFE) note = (note % 12) + ((note / 12) << 4);
if (command == CMD_VOLUME)
{
command = 0;
if (param > 64) param = 64;
volcmd = VOLCMD_VOLUME;
vol = param;
}
if (volcmd == VOLCMD_VOLUME) b |= 0x40; else
if (volcmd == VOLCMD_PANNING) { vol |= 0x80; b |= 0x40; }
if (command)
{
S3MSaveConvert(&command, &param, FALSE);
if (command) b |= 0x80;
}
if (b & 0xE0)
{
buffer[len++] = b;
if (b & 0x20)
{
buffer[len++] = note;
buffer[len++] = m->instr;
}
if (b & 0x40)
{
buffer[len++] = vol;
}
if (b & 0x80)
{
buffer[len++] = command;
buffer[len++] = param;
}
if (len > sizeof(buffer) - 20) break;
}
}
buffer[len++] = 0;
if (len > sizeof(buffer) - 20) break;
}
}
buffer[0] = (len - 2) & 0xFF;
buffer[1] = (len - 2) >> 8;
len = (len+15) & (~0x0F);
fwrite(buffer, len, 1, f);
ofs += len;
}
// Writing samples
for (i=1; i<=nbi; i++)
{
MODINSTRUMENT *pins = &Ins[i];
if (m_nInstruments)
{
pins = Ins;
if (Headers[i])
{
for (UINT j=0; j<128; j++)
{
UINT n = Headers[i]->Keyboard[j];
if ((n) && (n < MAX_INSTRUMENTS))
{
pins = &Ins[n];
break;
}
}
}
}
memcpy(insex[i-1].dosname, pins->name, 12);
memcpy(insex[i-1].name, m_szNames[i], 28);
memcpy(insex[i-1].scrs, "SCRS", 4);
insex[i-1].hmem = (BYTE)((DWORD)ofs >> 20);
insex[i-1].memseg = (WORD)((DWORD)ofs >> 4);
if (pins->pSample)
{
insex[i-1].type = 1;
insex[i-1].length = pins->nLength;
insex[i-1].loopbegin = pins->nLoopStart;
insex[i-1].loopend = pins->nLoopEnd;
insex[i-1].vol = pins->nVolume / 4;
insex[i-1].flags = (pins->uFlags & CHN_LOOP) ? 1 : 0;
if (pins->nC4Speed)
insex[i-1].finetune = pins->nC4Speed;
else
insex[i-1].finetune = TransposeToFrequency(pins->RelativeTone, pins->nFineTune);
UINT flags = RS_PCM8U;
#ifndef NO_PACKING
if (nPacking)
{
if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
&& (CanPackSample((char *)pins->pSample, pins->nLength, nPacking)))
{
insex[i-1].pack = 4;
flags = RS_ADPCM4;
}
} else
#endif // NO_PACKING
{
if (pins->uFlags & CHN_16BIT)
{
insex[i-1].flags |= 4;
flags = RS_PCM16U;
}
if (pins->uFlags & CHN_STEREO)
{
insex[i-1].flags |= 2;
flags = (pins->uFlags & CHN_16BIT) ? RS_STPCM16U : RS_STPCM8U;
}
}
DWORD len = WriteSample(f, pins, flags);
if (len & 0x0F)
{
fwrite(S3MFiller, 0x10 - (len & 0x0F), 1, f);
}
ofs += (len + 15) & (~0x0F);
} else
{
insex[i-1].length = 0;
}
}
// Updating parapointers
fseek(f, ofs0, SEEK_SET);
fwrite(insptr, nbi, 2, f);
fwrite(patptr, nbp, 2, f);
fseek(f, ofs1, SEEK_SET);
fwrite(insex, 0x50, nbi, f);
fclose(f);
return TRUE;
}
#pragma warning(default:4100)
#endif // MODPLUG_NO_FILESAVE

186
src/modplug/load_stm.cpp Normal file
View file

@ -0,0 +1,186 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
#pragma pack(1)
typedef struct tagSTMNOTE
{
BYTE note;
BYTE insvol;
BYTE volcmd;
BYTE cmdinf;
} STMNOTE;
// Raw STM sampleinfo struct:
typedef struct tagSTMSAMPLE
{
CHAR filename[14]; // Can't have long comments - just filename comments :)
WORD reserved; // ISA in memory when in ST 2
WORD length; // Sample length
WORD loopbeg; // Loop start point
WORD loopend; // Loop end point
BYTE volume; // Volume
BYTE reserved2; // More reserved crap
WORD c2spd; // Good old c2spd
BYTE reserved3[6]; // Yet more of PSi's reserved crap
} STMSAMPLE;
// Raw STM header struct:
typedef struct tagSTMHEADER
{
char songname[20]; // changed from CHAR
char trackername[8]; // !SCREAM! for ST 2.xx // changed from CHAR
CHAR unused; // 0x1A
CHAR filetype; // 1=song, 2=module (only 2 is supported, of course) :)
CHAR ver_major; // Like 2
CHAR ver_minor; // "ditto"
BYTE inittempo; // initspeed= stm inittempo>>4
BYTE numpat; // number of patterns
BYTE globalvol; // <- WoW! a RiGHT TRiANGLE =8*)
BYTE reserved[13]; // More of PSi's internal crap
STMSAMPLE sample[31]; // STM sample data
BYTE patorder[128]; // Docs say 64 - actually 128
} STMHEADER;
#pragma pack()
BOOL CSoundFile::ReadSTM(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
STMHEADER *phdr = (STMHEADER *)lpStream;
DWORD dwMemPos = 0;
if ((!lpStream) || (dwMemLength < sizeof(STMHEADER))) return FALSE;
if ((phdr->filetype != 2) || (phdr->unused != 0x1A)
|| ((strnicmp(phdr->trackername, "!SCREAM!", 8))
&& (strnicmp(phdr->trackername, "BMOD2STM", 8)))) return FALSE;
memcpy(m_szNames[0], phdr->songname, 20);
// Read STM header
m_nType = MOD_TYPE_STM;
m_nSamples = 31;
m_nChannels = 4;
m_nInstruments = 0;
m_nMinPeriod = 64;
m_nMaxPeriod = 0x7FFF;
m_nDefaultSpeed = phdr->inittempo >> 4;
if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 1;
m_nDefaultTempo = 125;
m_nDefaultGlobalVolume = phdr->globalvol << 2;
if (m_nDefaultGlobalVolume > 256) m_nDefaultGlobalVolume = 256;
memcpy(Order, phdr->patorder, 128);
// Setting up channels
for (UINT nSet=0; nSet<4; nSet++)
{
ChnSettings[nSet].dwFlags = 0;
ChnSettings[nSet].nVolume = 64;
ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0;
}
// Reading samples
for (UINT nIns=0; nIns<31; nIns++)
{
MODINSTRUMENT *pIns = &Ins[nIns+1];
STMSAMPLE *pStm = &phdr->sample[nIns]; // STM sample data
memcpy(pIns->name, pStm->filename, 13);
memcpy(m_szNames[nIns+1], pStm->filename, 12);
pIns->nC4Speed = bswapLE16(pStm->c2spd);
pIns->nGlobalVol = 64;
pIns->nVolume = pStm->volume << 2;
if (pIns->nVolume > 256) pIns->nVolume = 256;
pIns->nLength = bswapLE16(pStm->length);
if ((pIns->nLength < 4) || (!pIns->nVolume)) pIns->nLength = 0;
pIns->nLoopStart = bswapLE16(pStm->loopbeg);
pIns->nLoopEnd = bswapLE16(pStm->loopend);
if ((pIns->nLoopEnd > pIns->nLoopStart) && (pIns->nLoopEnd != 0xFFFF)) pIns->uFlags |= CHN_LOOP;
}
dwMemPos = sizeof(STMHEADER);
for (UINT nOrd=0; nOrd<MAX_ORDERS; nOrd++) if (Order[nOrd] >= 99) Order[nOrd] = 0xFF;
UINT nPatterns = phdr->numpat;
for (UINT nPat=0; nPat<nPatterns; nPat++)
{
if (dwMemPos + 64*4*4 > dwMemLength) return TRUE;
PatternSize[nPat] = 64;
if ((Patterns[nPat] = AllocatePattern(64, m_nChannels)) == NULL) return TRUE;
MODCOMMAND *m = Patterns[nPat];
STMNOTE *p = (STMNOTE *)(lpStream + dwMemPos);
for (UINT n=0; n<64*4; n++, p++, m++)
{
UINT note,ins,vol,cmd;
// extract the various information from the 4 bytes that
// make up a single note
note = p->note;
ins = p->insvol >> 3;
vol = (p->insvol & 0x07) + (p->volcmd >> 1);
cmd = p->volcmd & 0x0F;
if ((ins) && (ins < 32)) m->instr = ins;
// special values of [SBYTE0] are handled here ->
// we have no idea if these strange values will ever be encountered
// but it appears as though stms sound correct.
if ((note == 0xFE) || (note == 0xFC)) m->note = 0xFE; else
// if note < 251, then all three bytes are stored in the file
if (note < 0xFC) m->note = (note >> 4)*12 + (note&0xf) + 37;
if (vol <= 64) { m->volcmd = VOLCMD_VOLUME; m->vol = vol; }
m->param = p->cmdinf;
switch(cmd)
{
// Axx set speed to xx
case 1: m->command = CMD_SPEED; m->param >>= 4; break;
// Bxx position jump
case 2: m->command = CMD_POSITIONJUMP; break;
// Cxx patternbreak to row xx
case 3: m->command = CMD_PATTERNBREAK; m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); break;
// Dxy volumeslide
case 4: m->command = CMD_VOLUMESLIDE; break;
// Exy toneslide down
case 5: m->command = CMD_PORTAMENTODOWN; break;
// Fxy toneslide up
case 6: m->command = CMD_PORTAMENTOUP; break;
// Gxx Tone portamento,speed xx
case 7: m->command = CMD_TONEPORTAMENTO; break;
// Hxy vibrato
case 8: m->command = CMD_VIBRATO; break;
// Ixy tremor, ontime x, offtime y
case 9: m->command = CMD_TREMOR; break;
// Jxy arpeggio
case 10: m->command = CMD_ARPEGGIO; break;
// Kxy Dual command H00 & Dxy
case 11: m->command = CMD_VIBRATOVOL; break;
// Lxy Dual command G00 & Dxy
case 12: m->command = CMD_TONEPORTAVOL; break;
// Xxx amiga command 8xx
case 0x18: m->command = CMD_PANNING8; break;
default:
m->command = m->param = 0;
}
}
dwMemPos += 64*4*4;
}
// Reading Samples
for (UINT nSmp=1; nSmp<=31; nSmp++)
{
MODINSTRUMENT *pIns = &Ins[nSmp];
dwMemPos = (dwMemPos + 15) & (~15);
if (pIns->nLength)
{
UINT nPos = ((UINT)phdr->sample[nSmp-1].reserved) << 4;
if ((nPos >= sizeof(STMHEADER)) && (nPos+pIns->nLength <= dwMemLength)) dwMemPos = nPos;
if (dwMemPos < dwMemLength)
{
dwMemPos += ReadSample(pIns, RS_PCM8S, (LPSTR)(lpStream+dwMemPos),dwMemLength-dwMemPos);
}
}
}
return TRUE;
}

222
src/modplug/load_ult.cpp Normal file
View file

@ -0,0 +1,222 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
//#pragma warning(disable:4244)
#define ULT_16BIT 0x04
#define ULT_LOOP 0x08
#define ULT_BIDI 0x10
#pragma pack(1)
// Raw ULT header struct:
typedef struct tagULTHEADER
{
char id[15]; // changed from CHAR
char songtitle[32]; // changed from CHAR
BYTE reserved;
} ULTHEADER;
// Raw ULT sampleinfo struct:
typedef struct tagULTSAMPLE
{
CHAR samplename[32];
CHAR dosname[12];
LONG loopstart;
LONG loopend;
LONG sizestart;
LONG sizeend;
BYTE volume;
BYTE flags;
WORD finetune;
} ULTSAMPLE;
#pragma pack()
BOOL CSoundFile::ReadUlt(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
ULTHEADER *pmh = (ULTHEADER *)lpStream;
ULTSAMPLE *pus;
UINT nos, nop;
DWORD dwMemPos = 0;
// try to read module header
if ((!lpStream) || (dwMemLength < 0x100)) return FALSE;
if (strncmp(pmh->id,"MAS_UTrack_V00",14)) return FALSE;
// Warning! Not supported ULT format, trying anyway
// if ((pmh->id[14] < '1') || (pmh->id[14] > '4')) return FALSE;
m_nType = MOD_TYPE_ULT;
m_nDefaultSpeed = 6;
m_nDefaultTempo = 125;
memcpy(m_szNames[0], pmh->songtitle, 32);
// read songtext
dwMemPos = sizeof(ULTHEADER);
if ((pmh->reserved) && (dwMemPos + pmh->reserved * 32 < dwMemLength))
{
UINT len = pmh->reserved * 32;
m_lpszSongComments = new char[len + 1 + pmh->reserved];
if (m_lpszSongComments)
{
for (UINT l=0; l<pmh->reserved; l++)
{
memcpy(m_lpszSongComments+l*33, lpStream+dwMemPos+l*32, 32);
m_lpszSongComments[l*33+32] = 0x0D;
}
m_lpszSongComments[len] = 0;
}
dwMemPos += len;
}
if (dwMemPos >= dwMemLength) return TRUE;
nos = lpStream[dwMemPos++];
m_nSamples = nos;
if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1;
UINT smpsize = 64;
if (pmh->id[14] >= '4') smpsize += 2;
if (dwMemPos + nos*smpsize + 256 + 2 > dwMemLength) return TRUE;
for (UINT ins=1; ins<=nos; ins++, dwMemPos+=smpsize) if (ins<=m_nSamples)
{
pus = (ULTSAMPLE *)(lpStream+dwMemPos);
MODINSTRUMENT *pins = &Ins[ins];
memcpy(m_szNames[ins], pus->samplename, 32);
memcpy(pins->name, pus->dosname, 12);
pins->nLoopStart = pus->loopstart;
pins->nLoopEnd = pus->loopend;
pins->nLength = pus->sizeend - pus->sizestart;
pins->nVolume = pus->volume;
pins->nGlobalVol = 64;
pins->nC4Speed = 8363;
if (pmh->id[14] >= '4')
{
pins->nC4Speed = pus->finetune;
}
if (pus->flags & ULT_LOOP) pins->uFlags |= CHN_LOOP;
if (pus->flags & ULT_BIDI) pins->uFlags |= CHN_PINGPONGLOOP;
if (pus->flags & ULT_16BIT)
{
pins->uFlags |= CHN_16BIT;
pins->nLoopStart >>= 1;
pins->nLoopEnd >>= 1;
}
}
memcpy(Order, lpStream+dwMemPos, 256);
dwMemPos += 256;
m_nChannels = lpStream[dwMemPos] + 1;
nop = lpStream[dwMemPos+1] + 1;
dwMemPos += 2;
if (m_nChannels > 32) m_nChannels = 32;
// Default channel settings
for (UINT nSet=0; nSet<m_nChannels; nSet++)
{
ChnSettings[nSet].nVolume = 64;
ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0;
}
// read pan position table for v1.5 and higher
if(pmh->id[14]>='3')
{
if (dwMemPos + m_nChannels > dwMemLength) return TRUE;
for(UINT t=0; t<m_nChannels; t++)
{
ChnSettings[t].nPan = (lpStream[dwMemPos++] << 4) + 8;
if (ChnSettings[t].nPan > 256) ChnSettings[t].nPan = 256;
}
}
// Allocating Patterns
for (UINT nAllocPat=0; nAllocPat<nop; nAllocPat++)
{
if (nAllocPat < MAX_PATTERNS)
{
PatternSize[nAllocPat] = 64;
Patterns[nAllocPat] = AllocatePattern(64, m_nChannels);
}
}
// Reading Patterns
for (UINT nChn=0; nChn<m_nChannels; nChn++)
{
for (UINT nPat=0; nPat<nop; nPat++)
{
MODCOMMAND *pat = NULL;
if (nPat < MAX_PATTERNS)
{
pat = Patterns[nPat];
if (pat) pat += nChn;
}
UINT row = 0;
while (row < 64)
{
if (dwMemPos + 6 > dwMemLength) return TRUE;
UINT rep = 1;
UINT note = lpStream[dwMemPos++];
if (note == 0xFC)
{
rep = lpStream[dwMemPos];
note = lpStream[dwMemPos+1];
dwMemPos += 2;
}
UINT instr = lpStream[dwMemPos++];
UINT eff = lpStream[dwMemPos++];
UINT dat1 = lpStream[dwMemPos++];
UINT dat2 = lpStream[dwMemPos++];
UINT cmd1 = eff & 0x0F;
UINT cmd2 = eff >> 4;
if (cmd1 == 0x0C) dat1 >>= 2; else
if (cmd1 == 0x0B) { cmd1 = dat1 = 0; }
if (cmd2 == 0x0C) dat2 >>= 2; else
if (cmd2 == 0x0B) { cmd2 = dat2 = 0; }
while ((rep != 0) && (row < 64))
{
if (pat)
{
pat->instr = instr;
if (note) pat->note = note + 36;
if (cmd1 | dat1)
{
if (cmd1 == 0x0C)
{
pat->volcmd = VOLCMD_VOLUME;
pat->vol = dat1;
} else
{
pat->command = cmd1;
pat->param = dat1;
ConvertModCommand(pat);
}
}
if (cmd2 == 0x0C)
{
pat->volcmd = VOLCMD_VOLUME;
pat->vol = dat2;
} else
if ((cmd2 | dat2) && (!pat->command))
{
pat->command = cmd2;
pat->param = dat2;
ConvertModCommand(pat);
}
pat += m_nChannels;
}
row++;
rep--;
}
}
}
}
// Reading Instruments
for (UINT smp=1; smp<=m_nSamples; smp++) if (Ins[smp].nLength)
{
if (dwMemPos >= dwMemLength) return TRUE;
UINT flags = (Ins[smp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S;
dwMemPos += ReadSample(&Ins[smp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
}
return TRUE;
}

53
src/modplug/load_umx.cpp Normal file
View file

@ -0,0 +1,53 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
#define MODMAGIC_OFFSET (20+31*30+130)
BOOL CSoundFile::ReadUMX(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
if ((!lpStream) || (dwMemLength < 0x800)) return FALSE;
// Rip Mods from UMX
if ((bswapLE32(*((DWORD *)(lpStream+0x20))) < dwMemLength)
&& (bswapLE32(*((DWORD *)(lpStream+0x18))) <= dwMemLength - 0x10)
&& (bswapLE32(*((DWORD *)(lpStream+0x18))) >= dwMemLength - 0x200))
{
for (UINT uscan=0x40; uscan<0x500; uscan++)
{
DWORD dwScan = bswapLE32(*((DWORD *)(lpStream+uscan)));
// IT
if (dwScan == 0x4D504D49)
{
DWORD dwRipOfs = uscan;
return ReadIT(lpStream + dwRipOfs, dwMemLength - dwRipOfs);
}
// S3M
if (dwScan == 0x4D524353)
{
DWORD dwRipOfs = uscan - 44;
return ReadS3M(lpStream + dwRipOfs, dwMemLength - dwRipOfs);
}
// XM
if (!strnicmp((LPCSTR)(lpStream+uscan), "Extended Module", 15))
{
DWORD dwRipOfs = uscan;
return ReadXM(lpStream + dwRipOfs, dwMemLength - dwRipOfs);
}
// MOD
if ((uscan > MODMAGIC_OFFSET) && (dwScan == 0x2e4b2e4d))
{
DWORD dwRipOfs = uscan - MODMAGIC_OFFSET;
return ReadMod(lpStream+dwRipOfs, dwMemLength-dwRipOfs);
}
}
}
return FALSE;
}

220
src/modplug/load_wav.cpp Normal file
View file

@ -0,0 +1,220 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
#ifndef WAVE_FORMAT_EXTENSIBLE
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#endif
/////////////////////////////////////////////////////////////
// WAV file support
BOOL CSoundFile::ReadWav(const BYTE *lpStream, DWORD dwMemLength)
//---------------------------------------------------------------
{
DWORD dwMemPos = 0;
WAVEFILEHEADER *phdr = (WAVEFILEHEADER *)lpStream;
WAVEFORMATHEADER *pfmt = (WAVEFORMATHEADER *)(lpStream + sizeof(WAVEFILEHEADER));
if ((!lpStream) || (dwMemLength < (DWORD)sizeof(WAVEFILEHEADER))) return FALSE;
if ((phdr->id_RIFF != IFFID_RIFF) || (phdr->id_WAVE != IFFID_WAVE)
|| (pfmt->id_fmt != IFFID_fmt)) return FALSE;
dwMemPos = sizeof(WAVEFILEHEADER) + 8 + pfmt->hdrlen;
if ((dwMemPos + 8 >= dwMemLength)
|| ((pfmt->format != WAVE_FORMAT_PCM) && (pfmt->format != WAVE_FORMAT_EXTENSIBLE))
|| (pfmt->channels > 4)
|| (!pfmt->channels)
|| (!pfmt->freqHz)
|| (pfmt->bitspersample & 7)
|| (pfmt->bitspersample < 8)
|| (pfmt->bitspersample > 32)) return FALSE;
WAVEDATAHEADER *pdata;
for (;;)
{
pdata = (WAVEDATAHEADER *)(lpStream + dwMemPos);
if (pdata->id_data == IFFID_data) break;
dwMemPos += pdata->length + 8;
if (dwMemPos + 8 >= dwMemLength) return FALSE;
}
m_nType = MOD_TYPE_WAV;
m_nSamples = 0;
m_nInstruments = 0;
m_nChannels = 4;
m_nDefaultSpeed = 8;
m_nDefaultTempo = 125;
m_dwSongFlags |= SONG_LINEARSLIDES; // For no resampling
Order[0] = 0;
Order[1] = 0xFF;
PatternSize[0] = PatternSize[1] = 64;
if ((Patterns[0] = AllocatePattern(64, 4)) == NULL) return TRUE;
if ((Patterns[1] = AllocatePattern(64, 4)) == NULL) return TRUE;
UINT samplesize = (pfmt->channels * pfmt->bitspersample) >> 3;
UINT len = pdata->length, bytelen;
if (dwMemPos + len > dwMemLength - 8) len = dwMemLength - dwMemPos - 8;
len /= samplesize;
bytelen = len;
if (pfmt->bitspersample >= 16) bytelen *= 2;
if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
if (!len) return TRUE;
// Setting up module length
DWORD dwTime = ((len * 50) / pfmt->freqHz) + 1;
DWORD framesperrow = (dwTime + 63) / 63;
if (framesperrow < 4) framesperrow = 4;
UINT norders = 1;
while (framesperrow >= 0x20)
{
Order[norders++] = 1;
Order[norders] = 0xFF;
framesperrow = (dwTime + (64 * norders - 1)) / (64 * norders);
if (norders >= MAX_ORDERS-1) break;
}
m_nDefaultSpeed = framesperrow;
for (UINT iChn=0; iChn<4; iChn++)
{
ChnSettings[iChn].nPan = (iChn & 1) ? 256 : 0;
ChnSettings[iChn].nVolume = 64;
ChnSettings[iChn].dwFlags = 0;
}
// Setting up speed command
MODCOMMAND *pcmd = Patterns[0];
pcmd[0].command = CMD_SPEED;
pcmd[0].param = (BYTE)m_nDefaultSpeed;
pcmd[0].note = 5*12+1;
pcmd[0].instr = 1;
pcmd[1].note = pcmd[0].note;
pcmd[1].instr = pcmd[0].instr;
m_nSamples = pfmt->channels;
// Support for Multichannel Wave
for (UINT nChn=0; nChn<m_nSamples; nChn++)
{
MODINSTRUMENT *pins = &Ins[nChn+1];
pcmd[nChn].note = pcmd[0].note;
pcmd[nChn].instr = (BYTE)(nChn+1);
pins->nLength = len;
pins->nC4Speed = pfmt->freqHz;
pins->nVolume = 256;
pins->nPan = 128;
pins->nGlobalVol = 64;
pins->uFlags = (WORD)((pfmt->bitspersample >= 16) ? CHN_16BIT : 0);
pins->uFlags |= CHN_PANNING;
if (m_nSamples > 1)
{
switch(nChn)
{
case 0: pins->nPan = 0; break;
case 1: pins->nPan = 256; break;
case 2: pins->nPan = (WORD)((m_nSamples == 3) ? 128 : 64); pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break;
case 3: pins->nPan = 192; pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break;
default: pins->nPan = 128; break;
}
}
if ((pins->pSample = AllocateSample(bytelen+8)) == NULL) return TRUE;
if (pfmt->bitspersample >= 16)
{
int slsize = pfmt->bitspersample >> 3;
signed short *p = (signed short *)pins->pSample;
signed char *psrc = (signed char *)(lpStream+dwMemPos+8+nChn*slsize+slsize-2);
for (UINT i=0; i<len; i++)
{
p[i] = *((signed short *)psrc);
psrc += samplesize;
}
p[len+1] = p[len] = p[len-1];
} else
{
signed char *p = (signed char *)pins->pSample;
signed char *psrc = (signed char *)(lpStream+dwMemPos+8+nChn);
for (UINT i=0; i<len; i++)
{
p[i] = (signed char)((*psrc) + 0x80);
psrc += samplesize;
}
p[len+1] = p[len] = p[len-1];
}
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
// IMA ADPCM Support
#pragma pack(1)
typedef struct IMAADPCMBLOCK
{
WORD sample;
BYTE index;
BYTE Reserved;
} DVI_ADPCMBLOCKHEADER;
#pragma pack()
static const int gIMAUnpackTable[90] =
{
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767, 0
};
BOOL IMAADPCMUnpack16(signed short *pdest, UINT nLen, LPBYTE psrc, DWORD dwBytes, UINT pkBlkAlign)
//------------------------------------------------------------------------------------------------
{
static const int gIMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
UINT nPos;
int value;
if ((nLen < 4) || (!pdest) || (!psrc)
|| (pkBlkAlign < 5) || (pkBlkAlign > dwBytes)) return FALSE;
nPos = 0;
while ((nPos < nLen) && (dwBytes > 4))
{
int nIndex;
value = *((short int *)psrc);
nIndex = psrc[2];
psrc += 4;
dwBytes -= 4;
pdest[nPos++] = (short int)value;
for (UINT i=0; ((i<(pkBlkAlign-4)*2) && (nPos < nLen) && (dwBytes)); i++)
{
BYTE delta;
if (i & 1)
{
delta = (BYTE)(((*(psrc++)) >> 4) & 0x0F);
dwBytes--;
} else
{
delta = (BYTE)((*psrc) & 0x0F);
}
int v = gIMAUnpackTable[nIndex] >> 3;
if (delta & 1) v += gIMAUnpackTable[nIndex] >> 2;
if (delta & 2) v += gIMAUnpackTable[nIndex] >> 1;
if (delta & 4) v += gIMAUnpackTable[nIndex];
if (delta & 8) value -= v; else value += v;
nIndex += gIMAIndexTab[delta & 7];
if (nIndex < 0) nIndex = 0; else
if (nIndex > 88) nIndex = 88;
if (value > 32767) value = 32767; else
if (value < -32768) value = -32768;
pdest[nPos++] = (short int)value;
}
}
return TRUE;
}

892
src/modplug/load_xm.cpp Normal file
View file

@ -0,0 +1,892 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
#include "stdafx.h"
#include "sndfile.h"
////////////////////////////////////////////////////////
// FastTracker II XM file support
#ifdef MSC_VER
#pragma warning(disable:4244)
#endif
#pragma pack(1)
typedef struct tagXMFILEHEADER
{
DWORD size;
WORD norder;
WORD restartpos;
WORD channels;
WORD patterns;
WORD instruments;
WORD flags;
WORD speed;
WORD tempo;
BYTE order[256];
} XMFILEHEADER;
typedef struct tagXMINSTRUMENTHEADER
{
DWORD size;
CHAR name[22];
BYTE type;
BYTE samples;
BYTE samplesh;
} XMINSTRUMENTHEADER;
typedef struct tagXMSAMPLEHEADER
{
DWORD shsize;
BYTE snum[96];
WORD venv[24];
WORD penv[24];
BYTE vnum, pnum;
BYTE vsustain, vloops, vloope, psustain, ploops, ploope;
BYTE vtype, ptype;
BYTE vibtype, vibsweep, vibdepth, vibrate;
WORD volfade;
WORD res;
BYTE reserved1[20];
} XMSAMPLEHEADER;
typedef struct tagXMSAMPLESTRUCT
{
DWORD samplen;
DWORD loopstart;
DWORD looplen;
BYTE vol;
signed char finetune;
BYTE type;
BYTE pan;
signed char relnote;
BYTE res;
char name[22];
} XMSAMPLESTRUCT;
#pragma pack()
BOOL CSoundFile::ReadXM(const BYTE *lpStream, DWORD dwMemLength)
//--------------------------------------------------------------
{
XMSAMPLEHEADER xmsh;
XMSAMPLESTRUCT xmss;
DWORD dwMemPos, dwHdrSize;
WORD norders=0, restartpos=0, channels=0, patterns=0, instruments=0;
WORD xmflags=0, deftempo=125, defspeed=6;
BOOL InstUsed[256];
BYTE channels_used[MAX_CHANNELS];
BYTE pattern_map[256];
BOOL samples_used[MAX_SAMPLES];
UINT unused_samples;
m_nChannels = 0;
if ((!lpStream) || (dwMemLength < 0x200)) return FALSE;
if (strnicmp((LPCSTR)lpStream, "Extended Module", 15)) return FALSE;
memcpy(m_szNames[0], lpStream+17, 20);
dwHdrSize = bswapLE32(*((DWORD *)(lpStream+60)));
norders = bswapLE16(*((WORD *)(lpStream+64)));
if ((!norders) || (norders > MAX_ORDERS)) return FALSE;
restartpos = bswapLE16(*((WORD *)(lpStream+66)));
channels = bswapLE16(*((WORD *)(lpStream+68)));
if ((!channels) || (channels > 64)) return FALSE;
m_nType = MOD_TYPE_XM;
m_nMinPeriod = 27;
m_nMaxPeriod = 54784;
m_nChannels = channels;
if (restartpos < norders) m_nRestartPos = restartpos;
patterns = bswapLE16(*((WORD *)(lpStream+70)));
if (patterns > 256) patterns = 256;
instruments = bswapLE16(*((WORD *)(lpStream+72)));
if (instruments >= MAX_INSTRUMENTS) instruments = MAX_INSTRUMENTS-1;
m_nInstruments = instruments;
m_nSamples = 0;
memcpy(&xmflags, lpStream+74, 2);
xmflags = bswapLE16(xmflags);
if (xmflags & 1) m_dwSongFlags |= SONG_LINEARSLIDES;
if (xmflags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE;
defspeed = bswapLE16(*((WORD *)(lpStream+76)));
deftempo = bswapLE16(*((WORD *)(lpStream+78)));
if ((deftempo >= 32) && (deftempo < 256)) m_nDefaultTempo = deftempo;
if ((defspeed > 0) && (defspeed < 40)) m_nDefaultSpeed = defspeed;
memcpy(Order, lpStream+80, norders);
memset(InstUsed, 0, sizeof(InstUsed));
if (patterns > MAX_PATTERNS)
{
UINT i, j;
for (i=0; i<norders; i++)
{
if (Order[i] < patterns) InstUsed[Order[i]] = TRUE;
}
j = 0;
for (i=0; i<256; i++)
{
if (InstUsed[i]) pattern_map[i] = j++;
}
for (i=0; i<256; i++)
{
if (!InstUsed[i])
{
pattern_map[i] = (j < MAX_PATTERNS) ? j : 0xFE;
j++;
}
}
for (i=0; i<norders; i++)
{
Order[i] = pattern_map[Order[i]];
}
} else
{
for (UINT i=0; i<256; i++) pattern_map[i] = i;
}
memset(InstUsed, 0, sizeof(InstUsed));
dwMemPos = dwHdrSize + 60;
if (dwMemPos + 8 >= dwMemLength) return TRUE;
// Reading patterns
memset(channels_used, 0, sizeof(channels_used));
for (UINT ipat=0; ipat<patterns; ipat++)
{
UINT ipatmap = pattern_map[ipat];
DWORD dwSize = 0;
WORD rows=64, packsize=0;
dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos)));
while ((dwMemPos + dwSize >= dwMemLength) || (dwSize & 0xFFFFFF00))
{
if (dwMemPos + 4 >= dwMemLength) break;
dwMemPos++;
dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos)));
}
rows = bswapLE16(*((WORD *)(lpStream+dwMemPos+5)));
if ((!rows) || (rows > 256)) rows = 64;
packsize = bswapLE16(*((WORD *)(lpStream+dwMemPos+7)));
if (dwMemPos + dwSize + 4 > dwMemLength) return TRUE;
dwMemPos += dwSize;
if (dwMemPos + packsize + 4 > dwMemLength) return TRUE;
MODCOMMAND *p;
if (ipatmap < MAX_PATTERNS)
{
PatternSize[ipatmap] = rows;
if ((Patterns[ipatmap] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE;
if (!packsize) continue;
p = Patterns[ipatmap];
} else p = NULL;
const BYTE *src = lpStream+dwMemPos;
UINT j=0;
for (UINT row=0; row<rows; row++)
{
for (UINT chn=0; chn<m_nChannels; chn++)
{
if ((p) && (j < packsize))
{
BYTE b = src[j++];
UINT vol = 0;
if (b & 0x80)
{
if (b & 1) p->note = src[j++];
if (b & 2) p->instr = src[j++];
if (b & 4) vol = src[j++];
if (b & 8) p->command = src[j++];
if (b & 16) p->param = src[j++];
} else
{
p->note = b;
p->instr = src[j++];
vol = src[j++];
p->command = src[j++];
p->param = src[j++];
}
if (p->note == 97) p->note = 0xFF; else
if ((p->note) && (p->note < 97)) p->note += 12;
if (p->note) channels_used[chn] = 1;
if (p->command | p->param) ConvertModCommand(p);
if (p->instr == 0xff) p->instr = 0;
if (p->instr) InstUsed[p->instr] = TRUE;
if ((vol >= 0x10) && (vol <= 0x50))
{
p->volcmd = VOLCMD_VOLUME;
p->vol = vol - 0x10;
} else
if (vol >= 0x60)
{
UINT v = vol & 0xF0;
vol &= 0x0F;
p->vol = vol;
switch(v)
{
// 60-6F: Volume Slide Down
case 0x60: p->volcmd = VOLCMD_VOLSLIDEDOWN; break;
// 70-7F: Volume Slide Up:
case 0x70: p->volcmd = VOLCMD_VOLSLIDEUP; break;
// 80-8F: Fine Volume Slide Down
case 0x80: p->volcmd = VOLCMD_FINEVOLDOWN; break;
// 90-9F: Fine Volume Slide Up
case 0x90: p->volcmd = VOLCMD_FINEVOLUP; break;
// A0-AF: Set Vibrato Speed
case 0xA0: p->volcmd = VOLCMD_VIBRATOSPEED; break;
// B0-BF: Vibrato
case 0xB0: p->volcmd = VOLCMD_VIBRATO; break;
// C0-CF: Set Panning
case 0xC0: p->volcmd = VOLCMD_PANNING; p->vol = (vol << 2) + 2; break;
// D0-DF: Panning Slide Left
case 0xD0: p->volcmd = VOLCMD_PANSLIDELEFT; break;
// E0-EF: Panning Slide Right
case 0xE0: p->volcmd = VOLCMD_PANSLIDERIGHT; break;
// F0-FF: Tone Portamento
case 0xF0: p->volcmd = VOLCMD_TONEPORTAMENTO; break;
}
}
p++;
} else
if (j < packsize)
{
BYTE b = src[j++];
if (b & 0x80)
{
if (b & 1) j++;
if (b & 2) j++;
if (b & 4) j++;
if (b & 8) j++;
if (b & 16) j++;
} else j += 4;
} else break;
}
}
dwMemPos += packsize;
}
// Wrong offset check
while (dwMemPos + 4 < dwMemLength)
{
DWORD d = bswapLE32(*((DWORD *)(lpStream+dwMemPos)));
if (d < 0x300) break;
dwMemPos++;
}
memset(samples_used, 0, sizeof(samples_used));
unused_samples = 0;
// Reading instruments
for (UINT iIns=1; iIns<=instruments; iIns++)
{
XMINSTRUMENTHEADER *pih;
BYTE flags[32];
DWORD samplesize[32];
UINT samplemap[32];
WORD nsamples;
if (dwMemPos + sizeof(XMINSTRUMENTHEADER) >= dwMemLength) return TRUE;
pih = (XMINSTRUMENTHEADER *)(lpStream+dwMemPos);
if (dwMemPos + bswapLE32(pih->size) > dwMemLength) return TRUE;
if ((Headers[iIns] = new INSTRUMENTHEADER) == NULL) continue;
memset(Headers[iIns], 0, sizeof(INSTRUMENTHEADER));
memcpy(Headers[iIns]->name, pih->name, 22);
if ((nsamples = pih->samples) > 0)
{
if (dwMemPos + sizeof(XMSAMPLEHEADER) > dwMemLength) return TRUE;
memcpy(&xmsh, lpStream+dwMemPos+sizeof(XMINSTRUMENTHEADER), sizeof(XMSAMPLEHEADER));
xmsh.shsize = bswapLE32(xmsh.shsize);
for (int i = 0; i < 24; ++i) {
xmsh.venv[i] = bswapLE16(xmsh.venv[i]);
xmsh.penv[i] = bswapLE16(xmsh.penv[i]);
}
xmsh.volfade = bswapLE16(xmsh.volfade);
xmsh.res = bswapLE16(xmsh.res);
dwMemPos += bswapLE32(pih->size);
} else
{
if (bswapLE32(pih->size)) dwMemPos += bswapLE32(pih->size);
else dwMemPos += sizeof(XMINSTRUMENTHEADER);
continue;
}
memset(samplemap, 0, sizeof(samplemap));
if (nsamples > 32) return TRUE;
UINT newsamples = m_nSamples;
for (UINT nmap=0; nmap<nsamples; nmap++)
{
UINT n = m_nSamples+nmap+1;
if (n >= MAX_SAMPLES)
{
n = m_nSamples;
while (n > 0)
{
if (!Ins[n].pSample)
{
for (UINT xmapchk=0; xmapchk < nmap; xmapchk++)
{
if (samplemap[xmapchk] == n) goto alreadymapped;
}
for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs])
{
INSTRUMENTHEADER *pks = Headers[clrs];
for (UINT ks=0; ks<128; ks++)
{
if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0;
}
}
break;
}
alreadymapped:
n--;
}
#ifndef MODPLUG_FASTSOUNDLIB
// Damn! more than 200 samples: look for duplicates
if (!n)
{
if (!unused_samples)
{
unused_samples = DetectUnusedSamples(samples_used);
if (!unused_samples) unused_samples = 0xFFFF;
}
if ((unused_samples) && (unused_samples != 0xFFFF))
{
for (UINT iext=m_nSamples; iext>=1; iext--) if (!samples_used[iext])
{
unused_samples--;
samples_used[iext] = TRUE;
DestroySample(iext);
n = iext;
for (UINT mapchk=0; mapchk<nmap; mapchk++)
{
if (samplemap[mapchk] == n) samplemap[mapchk] = 0;
}
for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs])
{
INSTRUMENTHEADER *pks = Headers[clrs];
for (UINT ks=0; ks<128; ks++)
{
if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0;
}
}
memset(&Ins[n], 0, sizeof(Ins[0]));
break;
}
}
}
#endif // MODPLUG_FASTSOUNDLIB
}
if (newsamples < n) newsamples = n;
samplemap[nmap] = n;
}
m_nSamples = newsamples;
// Reading Volume Envelope
INSTRUMENTHEADER *penv = Headers[iIns];
penv->nMidiProgram = pih->type;
penv->nFadeOut = xmsh.volfade;
penv->nPan = 128;
penv->nPPC = 5*12;
if (xmsh.vtype & 1) penv->dwFlags |= ENV_VOLUME;
if (xmsh.vtype & 2) penv->dwFlags |= ENV_VOLSUSTAIN;
if (xmsh.vtype & 4) penv->dwFlags |= ENV_VOLLOOP;
if (xmsh.ptype & 1) penv->dwFlags |= ENV_PANNING;
if (xmsh.ptype & 2) penv->dwFlags |= ENV_PANSUSTAIN;
if (xmsh.ptype & 4) penv->dwFlags |= ENV_PANLOOP;
if (xmsh.vnum > 12) xmsh.vnum = 12;
if (xmsh.pnum > 12) xmsh.pnum = 12;
penv->nVolEnv = xmsh.vnum;
if (!xmsh.vnum) penv->dwFlags &= ~ENV_VOLUME;
if (!xmsh.pnum) penv->dwFlags &= ~ENV_PANNING;
penv->nPanEnv = xmsh.pnum;
penv->nVolSustainBegin = penv->nVolSustainEnd = xmsh.vsustain;
if (xmsh.vsustain >= 12) penv->dwFlags &= ~ENV_VOLSUSTAIN;
penv->nVolLoopStart = xmsh.vloops;
penv->nVolLoopEnd = xmsh.vloope;
if (penv->nVolLoopEnd >= 12) penv->nVolLoopEnd = 0;
if (penv->nVolLoopStart >= penv->nVolLoopEnd) penv->dwFlags &= ~ENV_VOLLOOP;
penv->nPanSustainBegin = penv->nPanSustainEnd = xmsh.psustain;
if (xmsh.psustain >= 12) penv->dwFlags &= ~ENV_PANSUSTAIN;
penv->nPanLoopStart = xmsh.ploops;
penv->nPanLoopEnd = xmsh.ploope;
if (penv->nPanLoopEnd >= 12) penv->nPanLoopEnd = 0;
if (penv->nPanLoopStart >= penv->nPanLoopEnd) penv->dwFlags &= ~ENV_PANLOOP;
penv->nGlobalVol = 64;
for (UINT ienv=0; ienv<12; ienv++)
{
penv->VolPoints[ienv] = (WORD)xmsh.venv[ienv*2];
penv->VolEnv[ienv] = (BYTE)xmsh.venv[ienv*2+1];
penv->PanPoints[ienv] = (WORD)xmsh.penv[ienv*2];
penv->PanEnv[ienv] = (BYTE)xmsh.penv[ienv*2+1];
if (ienv)
{
if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1])
{
penv->VolPoints[ienv] &= 0xFF;
penv->VolPoints[ienv] += penv->VolPoints[ienv-1] & 0xFF00;
if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1]) penv->VolPoints[ienv] += 0x100;
}
if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1])
{
penv->PanPoints[ienv] &= 0xFF;
penv->PanPoints[ienv] += penv->PanPoints[ienv-1] & 0xFF00;
if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1]) penv->PanPoints[ienv] += 0x100;
}
}
}
for (UINT j=0; j<96; j++)
{
penv->NoteMap[j+12] = j+1+12;
if (xmsh.snum[j] < nsamples)
penv->Keyboard[j+12] = samplemap[xmsh.snum[j]];
}
// Reading samples
for (UINT ins=0; ins<nsamples; ins++)
{
if ((dwMemPos + sizeof(xmss) > dwMemLength)
|| (dwMemPos + xmsh.shsize > dwMemLength)) return TRUE;
memcpy(&xmss, lpStream+dwMemPos, sizeof(xmss));
xmss.samplen = bswapLE32(xmss.samplen);
xmss.loopstart = bswapLE32(xmss.loopstart);
xmss.looplen = bswapLE32(xmss.looplen);
dwMemPos += xmsh.shsize;
flags[ins] = (xmss.type & 0x10) ? RS_PCM16D : RS_PCM8D;
if (xmss.type & 0x20) flags[ins] = (xmss.type & 0x10) ? RS_STPCM16D : RS_STPCM8D;
samplesize[ins] = xmss.samplen;
if (!samplemap[ins]) continue;
if (xmss.type & 0x10)
{
xmss.looplen >>= 1;
xmss.loopstart >>= 1;
xmss.samplen >>= 1;
}
if (xmss.type & 0x20)
{
xmss.looplen >>= 1;
xmss.loopstart >>= 1;
xmss.samplen >>= 1;
}
if (xmss.samplen > MAX_SAMPLE_LENGTH) xmss.samplen = MAX_SAMPLE_LENGTH;
if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3;
xmss.looplen += xmss.loopstart;
if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen;
if (!xmss.looplen) xmss.type &= ~3;
UINT imapsmp = samplemap[ins];
memcpy(m_szNames[imapsmp], xmss.name, 22);
m_szNames[imapsmp][22] = 0;
MODINSTRUMENT *pins = &Ins[imapsmp];
pins->nLength = (xmss.samplen > MAX_SAMPLE_LENGTH) ? MAX_SAMPLE_LENGTH : xmss.samplen;
pins->nLoopStart = xmss.loopstart;
pins->nLoopEnd = xmss.looplen;
if (pins->nLoopEnd > pins->nLength) pins->nLoopEnd = pins->nLength;
if (pins->nLoopStart >= pins->nLoopEnd)
{
pins->nLoopStart = pins->nLoopEnd = 0;
}
if (xmss.type & 3) pins->uFlags |= CHN_LOOP;
if (xmss.type & 2) pins->uFlags |= CHN_PINGPONGLOOP;
pins->nVolume = xmss.vol << 2;
if (pins->nVolume > 256) pins->nVolume = 256;
pins->nGlobalVol = 64;
if ((xmss.res == 0xAD) && (!(xmss.type & 0x30)))
{
flags[ins] = RS_ADPCM4;
samplesize[ins] = (samplesize[ins]+1)/2 + 16;
}
pins->nFineTune = xmss.finetune;
pins->RelativeTone = (int)xmss.relnote;
pins->nPan = xmss.pan;
pins->uFlags |= CHN_PANNING;
pins->nVibType = xmsh.vibtype;
pins->nVibSweep = xmsh.vibsweep;
pins->nVibDepth = xmsh.vibdepth;
pins->nVibRate = xmsh.vibrate;
memcpy(pins->name, xmss.name, 22);
pins->name[21] = 0;
}
#if 0
if ((xmsh.reserved2 > nsamples) && (xmsh.reserved2 <= 16))
{
dwMemPos += (((UINT)xmsh.reserved2) - nsamples) * xmsh.shsize;
}
#endif
for (UINT ismpd=0; ismpd<nsamples; ismpd++)
{
if ((samplemap[ismpd]) && (samplesize[ismpd]) && (dwMemPos < dwMemLength))
{
ReadSample(&Ins[samplemap[ismpd]], flags[ismpd], (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
}
dwMemPos += samplesize[ismpd];
if (dwMemPos >= dwMemLength) break;
}
}
// Read song comments: "TEXT"
if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x74786574))
{
UINT len = *((DWORD *)(lpStream+dwMemPos+4));
dwMemPos += 8;
if ((dwMemPos + len <= dwMemLength) && (len < 16384))
{
m_lpszSongComments = new char[len+1];
if (m_lpszSongComments)
{
memcpy(m_lpszSongComments, lpStream+dwMemPos, len);
m_lpszSongComments[len] = 0;
}
dwMemPos += len;
}
}
// Read midi config: "MIDI"
if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4944494D))
{
UINT len = *((DWORD *)(lpStream+dwMemPos+4));
dwMemPos += 8;
if (len == sizeof(MODMIDICFG))
{
memcpy(&m_MidiCfg, lpStream+dwMemPos, len);
m_dwSongFlags |= SONG_EMBEDMIDICFG;
}
}
// Read pattern names: "PNAM"
if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e50))
{
UINT len = *((DWORD *)(lpStream+dwMemPos+4));
dwMemPos += 8;
if ((dwMemPos + len <= dwMemLength) && (len <= MAX_PATTERNS*MAX_PATTERNNAME) && (len >= MAX_PATTERNNAME))
{
m_lpszPatternNames = new char[len];
if (m_lpszPatternNames)
{
m_nPatternNames = len / MAX_PATTERNNAME;
memcpy(m_lpszPatternNames, lpStream+dwMemPos, len);
}
dwMemPos += len;
}
}
// Read channel names: "CNAM"
if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e43))
{
UINT len = *((DWORD *)(lpStream+dwMemPos+4));
dwMemPos += 8;
if ((dwMemPos + len <= dwMemLength) && (len <= MAX_BASECHANNELS*MAX_CHANNELNAME))
{
UINT n = len / MAX_CHANNELNAME;
for (UINT i=0; i<n; i++)
{
memcpy(ChnSettings[i].szName, (lpStream+dwMemPos+i*MAX_CHANNELNAME), MAX_CHANNELNAME);
ChnSettings[i].szName[MAX_CHANNELNAME-1] = 0;
}
dwMemPos += len;
}
}
// Read mix plugins information
if (dwMemPos + 8 < dwMemLength)
{
dwMemPos += LoadMixPlugins(lpStream+dwMemPos, dwMemLength-dwMemPos);
}
return TRUE;
}
#ifndef MODPLUG_NO_FILESAVE
BOOL CSoundFile::SaveXM(LPCSTR lpszFileName, UINT nPacking)
//---------------------------------------------------------
{
BYTE s[64*64*5];
XMFILEHEADER header;
XMINSTRUMENTHEADER xmih;
XMSAMPLEHEADER xmsh;
XMSAMPLESTRUCT xmss;
BYTE smptable[32];
BYTE xmph[9];
FILE *f;
int i;
if ((!m_nChannels) || (!lpszFileName)) return FALSE;
if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE;
fwrite("Extended Module: ", 17, 1, f);
fwrite(m_szNames[0], 20, 1, f);
s[0] = 0x1A;
lstrcpy((LPSTR)&s[1], (nPacking) ? "MOD Plugin packed " : "FastTracker v2.00 ");
s[21] = 0x04;
s[22] = 0x01;
fwrite(s, 23, 1, f);
// Writing song header
memset(&header, 0, sizeof(header));
header.size = sizeof(XMFILEHEADER);
header.norder = 0;
header.restartpos = m_nRestartPos;
header.channels = m_nChannels;
header.patterns = 0;
for (i=0; i<MAX_ORDERS; i++)
{
if (Order[i] == 0xFF) break;
header.norder++;
if ((Order[i] >= header.patterns) && (Order[i] < MAX_PATTERNS)) header.patterns = Order[i]+1;
}
header.instruments = m_nInstruments;
if (!header.instruments) header.instruments = m_nSamples;
header.flags = (m_dwSongFlags & SONG_LINEARSLIDES) ? 0x01 : 0x00;
if (m_dwSongFlags & SONG_EXFILTERRANGE) header.flags |= 0x1000;
header.tempo = m_nDefaultTempo;
header.speed = m_nDefaultSpeed;
memcpy(header.order, Order, header.norder);
fwrite(&header, 1, sizeof(header), f);
// Writing patterns
for (i=0; i<header.patterns; i++) if (Patterns[i])
{
MODCOMMAND *p = Patterns[i];
UINT len = 0;
memset(&xmph, 0, sizeof(xmph));
xmph[0] = 9;
xmph[5] = (BYTE)(PatternSize[i] & 0xFF);
xmph[6] = (BYTE)(PatternSize[i] >> 8);
for (UINT j=m_nChannels*PatternSize[i]; j; j--,p++)
{
UINT note = p->note;
UINT param = ModSaveCommand(p, TRUE);
UINT command = param >> 8;
param &= 0xFF;
if (note >= 0xFE) note = 97; else
if ((note <= 12) || (note > 96+12)) note = 0; else
note -= 12;
UINT vol = 0;
if (p->volcmd)
{
UINT volcmd = p->volcmd;
switch(volcmd)
{
case VOLCMD_VOLUME: vol = 0x10 + p->vol; break;
case VOLCMD_VOLSLIDEDOWN: vol = 0x60 + (p->vol & 0x0F); break;
case VOLCMD_VOLSLIDEUP: vol = 0x70 + (p->vol & 0x0F); break;
case VOLCMD_FINEVOLDOWN: vol = 0x80 + (p->vol & 0x0F); break;
case VOLCMD_FINEVOLUP: vol = 0x90 + (p->vol & 0x0F); break;
case VOLCMD_VIBRATOSPEED: vol = 0xA0 + (p->vol & 0x0F); break;
case VOLCMD_VIBRATO: vol = 0xB0 + (p->vol & 0x0F); break;
case VOLCMD_PANNING: vol = 0xC0 + (p->vol >> 2); if (vol > 0xCF) vol = 0xCF; break;
case VOLCMD_PANSLIDELEFT: vol = 0xD0 + (p->vol & 0x0F); break;
case VOLCMD_PANSLIDERIGHT: vol = 0xE0 + (p->vol & 0x0F); break;
case VOLCMD_TONEPORTAMENTO: vol = 0xF0 + (p->vol & 0x0F); break;
}
}
if ((note) && (p->instr) && (vol > 0x0F) && (command) && (param))
{
s[len++] = note;
s[len++] = p->instr;
s[len++] = vol;
s[len++] = command;
s[len++] = param;
} else
{
BYTE b = 0x80;
if (note) b |= 0x01;
if (p->instr) b |= 0x02;
if (vol >= 0x10) b |= 0x04;
if (command) b |= 0x08;
if (param) b |= 0x10;
s[len++] = b;
if (b & 1) s[len++] = note;
if (b & 2) s[len++] = p->instr;
if (b & 4) s[len++] = vol;
if (b & 8) s[len++] = command;
if (b & 16) s[len++] = param;
}
if (len > sizeof(s) - 5) break;
}
xmph[7] = (BYTE)(len & 0xFF);
xmph[8] = (BYTE)(len >> 8);
fwrite(xmph, 1, 9, f);
fwrite(s, 1, len, f);
} else
{
memset(&xmph, 0, sizeof(xmph));
xmph[0] = 9;
xmph[5] = (BYTE)(PatternSize[i] & 0xFF);
xmph[6] = (BYTE)(PatternSize[i] >> 8);
fwrite(xmph, 1, 9, f);
}
// Writing instruments
for (i=1; i<=header.instruments; i++)
{
MODINSTRUMENT *pins;
BYTE flags[32];
memset(&xmih, 0, sizeof(xmih));
memset(&xmsh, 0, sizeof(xmsh));
xmih.size = sizeof(xmih) + sizeof(xmsh);
memcpy(xmih.name, m_szNames[i], 22);
xmih.type = 0;
xmih.samples = 0;
if (m_nInstruments)
{
INSTRUMENTHEADER *penv = Headers[i];
if (penv)
{
memcpy(xmih.name, penv->name, 22);
xmih.type = penv->nMidiProgram;
xmsh.volfade = penv->nFadeOut;
xmsh.vnum = (BYTE)penv->nVolEnv;
xmsh.pnum = (BYTE)penv->nPanEnv;
if (xmsh.vnum > 12) xmsh.vnum = 12;
if (xmsh.pnum > 12) xmsh.pnum = 12;
for (UINT ienv=0; ienv<12; ienv++)
{
xmsh.venv[ienv*2] = penv->VolPoints[ienv];
xmsh.venv[ienv*2+1] = penv->VolEnv[ienv];
xmsh.penv[ienv*2] = penv->PanPoints[ienv];
xmsh.penv[ienv*2+1] = penv->PanEnv[ienv];
}
if (penv->dwFlags & ENV_VOLUME) xmsh.vtype |= 1;
if (penv->dwFlags & ENV_VOLSUSTAIN) xmsh.vtype |= 2;
if (penv->dwFlags & ENV_VOLLOOP) xmsh.vtype |= 4;
if (penv->dwFlags & ENV_PANNING) xmsh.ptype |= 1;
if (penv->dwFlags & ENV_PANSUSTAIN) xmsh.ptype |= 2;
if (penv->dwFlags & ENV_PANLOOP) xmsh.ptype |= 4;
xmsh.vsustain = (BYTE)penv->nVolSustainBegin;
xmsh.vloops = (BYTE)penv->nVolLoopStart;
xmsh.vloope = (BYTE)penv->nVolLoopEnd;
xmsh.psustain = (BYTE)penv->nPanSustainBegin;
xmsh.ploops = (BYTE)penv->nPanLoopStart;
xmsh.ploope = (BYTE)penv->nPanLoopEnd;
for (UINT j=0; j<96; j++) if (penv->Keyboard[j+12])
{
UINT k;
for (k=0; k<xmih.samples; k++) if (smptable[k] == penv->Keyboard[j+12]) break;
if (k == xmih.samples)
{
smptable[xmih.samples++] = penv->Keyboard[j+12];
}
if (xmih.samples >= 32) break;
xmsh.snum[j] = k;
}
// xmsh.reserved2 = xmih.samples;
}
} else
{
xmih.samples = 1;
// xmsh.reserved2 = 1;
smptable[0] = i;
}
xmsh.shsize = (xmih.samples) ? 40 : 0;
fwrite(&xmih, 1, sizeof(xmih), f);
if (smptable[0])
{
MODINSTRUMENT *pvib = &Ins[smptable[0]];
xmsh.vibtype = pvib->nVibType;
xmsh.vibsweep = pvib->nVibSweep;
xmsh.vibdepth = pvib->nVibDepth;
xmsh.vibrate = pvib->nVibRate;
}
fwrite(&xmsh, 1, xmih.size - sizeof(xmih), f);
if (!xmih.samples) continue;
for (UINT ins=0; ins<xmih.samples; ins++)
{
memset(&xmss, 0, sizeof(xmss));
if (smptable[ins]) memcpy(xmss.name, m_szNames[smptable[ins]], 22);
pins = &Ins[smptable[ins]];
xmss.samplen = pins->nLength;
xmss.loopstart = pins->nLoopStart;
xmss.looplen = pins->nLoopEnd - pins->nLoopStart;
xmss.vol = pins->nVolume / 4;
xmss.finetune = (char)pins->nFineTune;
xmss.type = 0;
if (pins->uFlags & CHN_LOOP) xmss.type = (pins->uFlags & CHN_PINGPONGLOOP) ? 2 : 1;
flags[ins] = RS_PCM8D;
#ifndef NO_PACKING
if (nPacking)
{
if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
&& (CanPackSample((char *)pins->pSample, pins->nLength, nPacking)))
{
flags[ins] = RS_ADPCM4;
xmss.res = 0xAD;
}
} else
#endif
{
if (pins->uFlags & CHN_16BIT)
{
flags[ins] = RS_PCM16D;
xmss.type |= 0x10;
xmss.looplen *= 2;
xmss.loopstart *= 2;
xmss.samplen *= 2;
}
if (pins->uFlags & CHN_STEREO)
{
flags[ins] = (pins->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D;
xmss.type |= 0x20;
xmss.looplen *= 2;
xmss.loopstart *= 2;
xmss.samplen *= 2;
}
}
xmss.pan = 255;
if (pins->nPan < 256) xmss.pan = (BYTE)pins->nPan;
xmss.relnote = (signed char)pins->RelativeTone;
fwrite(&xmss, 1, xmsh.shsize, f);
}
for (UINT ismpd=0; ismpd<xmih.samples; ismpd++)
{
pins = &Ins[smptable[ismpd]];
if (pins->pSample)
{
#ifndef NO_PACKING
if ((flags[ismpd] == RS_ADPCM4) && (xmih.samples>1)) CanPackSample((char *)pins->pSample, pins->nLength, nPacking);
#endif // NO_PACKING
WriteSample(f, pins, flags[ismpd]);
}
}
}
// Writing song comments
if ((m_lpszSongComments) && (m_lpszSongComments[0]))
{
DWORD d = 0x74786574;
fwrite(&d, 1, 4, f);
d = (DWORD)strlen(m_lpszSongComments);
fwrite(&d, 1, 4, f);
fwrite(m_lpszSongComments, 1, d, f);
}
// Writing midi cfg
if (m_dwSongFlags & SONG_EMBEDMIDICFG)
{
DWORD d = 0x4944494D;
fwrite(&d, 1, 4, f);
d = sizeof(MODMIDICFG);
fwrite(&d, 1, 4, f);
fwrite(&m_MidiCfg, 1, sizeof(MODMIDICFG), f);
}
// Writing Pattern Names
if ((m_nPatternNames) && (m_lpszPatternNames))
{
DWORD dwLen = m_nPatternNames * MAX_PATTERNNAME;
while ((dwLen >= MAX_PATTERNNAME) && (!m_lpszPatternNames[dwLen-MAX_PATTERNNAME])) dwLen -= MAX_PATTERNNAME;
if (dwLen >= MAX_PATTERNNAME)
{
DWORD d = 0x4d414e50;
fwrite(&d, 1, 4, f);
fwrite(&dwLen, 1, 4, f);
fwrite(m_lpszPatternNames, 1, dwLen, f);
}
}
// Writing Channel Names
{
UINT nChnNames = 0;
for (UINT inam=0; inam<m_nChannels; inam++)
{
if (ChnSettings[inam].szName[0]) nChnNames = inam+1;
}
// Do it!
if (nChnNames)
{
DWORD dwLen = nChnNames * MAX_CHANNELNAME;
DWORD d = 0x4d414e43;
fwrite(&d, 1, 4, f);
fwrite(&dwLen, 1, 4, f);
for (UINT inam=0; inam<nChnNames; inam++)
{
fwrite(ChnSettings[inam].szName, 1, MAX_CHANNELNAME, f);
}
}
}
// Save mix plugins information
SaveMixPlugins(f);
fclose(f);
return TRUE;
}
#endif // MODPLUG_NO_FILESAVE

406
src/modplug/mmcmp.cpp Normal file
View file

@ -0,0 +1,406 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
BOOL PP20_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength);
typedef struct MMCMPFILEHEADER
{
DWORD id_ziRC; // "ziRC"
DWORD id_ONia; // "ONia"
WORD hdrsize;
} MMCMPFILEHEADER, *LPMMCMPFILEHEADER;
typedef struct MMCMPHEADER
{
WORD version;
WORD nblocks;
DWORD filesize;
DWORD blktable;
BYTE glb_comp;
BYTE fmt_comp;
} MMCMPHEADER, *LPMMCMPHEADER;
typedef struct MMCMPBLOCK
{
DWORD unpk_size;
DWORD pk_size;
DWORD xor_chk;
WORD sub_blk;
WORD flags;
WORD tt_entries;
WORD num_bits;
} MMCMPBLOCK, *LPMMCMPBLOCK;
typedef struct MMCMPSUBBLOCK
{
DWORD unpk_pos;
DWORD unpk_size;
} MMCMPSUBBLOCK, *LPMMCMPSUBBLOCK;
#define MMCMP_COMP 0x0001
#define MMCMP_DELTA 0x0002
#define MMCMP_16BIT 0x0004
#define MMCMP_STEREO 0x0100
#define MMCMP_ABS16 0x0200
#define MMCMP_ENDIAN 0x0400
typedef struct MMCMPBITBUFFER
{
UINT bitcount;
DWORD bitbuffer;
LPCBYTE pSrc;
LPCBYTE pEnd;
DWORD GetBits(UINT nBits);
} MMCMPBITBUFFER;
DWORD MMCMPBITBUFFER::GetBits(UINT nBits)
//---------------------------------------
{
DWORD d;
if (!nBits) return 0;
while (bitcount < 24)
{
bitbuffer |= ((pSrc < pEnd) ? *pSrc++ : 0) << bitcount;
bitcount += 8;
}
d = bitbuffer & ((1 << nBits) - 1);
bitbuffer >>= nBits;
bitcount -= nBits;
return d;
}
//#define MMCMP_LOG
#ifdef MMCMP_LOG
extern void Log(LPCSTR s, ...);
#endif
const DWORD MMCMP8BitCommands[8] =
{
0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8
};
const UINT MMCMP8BitFetch[8] =
{
3, 3, 3, 3, 2, 1, 0, 0
};
const DWORD MMCMP16BitCommands[16] =
{
0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0
};
const UINT MMCMP16BitFetch[16] =
{
4, 4, 4, 4, 3, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
BOOL MMCMP_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength)
//---------------------------------------------------------
{
DWORD dwMemLength = *pdwMemLength;
LPCBYTE lpMemFile = *ppMemFile;
LPBYTE pBuffer;
LPMMCMPFILEHEADER pmfh = (LPMMCMPFILEHEADER)(lpMemFile);
LPMMCMPHEADER pmmh = (LPMMCMPHEADER)(lpMemFile+10);
LPDWORD pblk_table;
DWORD dwFileSize;
if (PP20_Unpack(ppMemFile, pdwMemLength))
{
return TRUE;
}
if ((dwMemLength < 256) || (!pmfh) || (pmfh->id_ziRC != 0x4352697A) || (pmfh->id_ONia != 0x61694e4f) || (pmfh->hdrsize < 14)
|| (!pmmh->nblocks) || (pmmh->filesize < 16) || (pmmh->filesize > 0x8000000)
|| (pmmh->blktable >= dwMemLength) || (pmmh->blktable + 4*pmmh->nblocks > dwMemLength)) return FALSE;
dwFileSize = pmmh->filesize;
if ((pBuffer = (LPBYTE)GlobalAllocPtr(GHND, (dwFileSize + 31) & ~15)) == NULL) return FALSE;
pblk_table = (LPDWORD)(lpMemFile+pmmh->blktable);
for (UINT nBlock=0; nBlock<pmmh->nblocks; nBlock++)
{
DWORD dwMemPos = pblk_table[nBlock];
LPMMCMPBLOCK pblk = (LPMMCMPBLOCK)(lpMemFile+dwMemPos);
LPMMCMPSUBBLOCK psubblk = (LPMMCMPSUBBLOCK)(lpMemFile+dwMemPos+20);
if ((dwMemPos + 20 >= dwMemLength) || (dwMemPos + 20 + pblk->sub_blk*8 >= dwMemLength)) break;
dwMemPos += 20 + pblk->sub_blk*8;
#ifdef MMCMP_LOG
Log("block %d: flags=%04X sub_blocks=%d", nBlock, (UINT)pblk->flags, (UINT)pblk->sub_blk);
Log(" pksize=%d unpksize=%d", pblk->pk_size, pblk->unpk_size);
Log(" tt_entries=%d num_bits=%d\n", pblk->tt_entries, pblk->num_bits);
#endif
// Data is not packed
if (!(pblk->flags & MMCMP_COMP))
{
for (UINT i=0; i<pblk->sub_blk; i++)
{
if ((psubblk->unpk_pos > dwFileSize) || (psubblk->unpk_pos + psubblk->unpk_size > dwFileSize)) break;
#ifdef MMCMP_LOG
Log(" Unpacked sub-block %d: offset %d, size=%d\n", i, psubblk->unpk_pos, psubblk->unpk_size);
#endif
memcpy(pBuffer+psubblk->unpk_pos, lpMemFile+dwMemPos, psubblk->unpk_size);
dwMemPos += psubblk->unpk_size;
psubblk++;
}
} else
// Data is 16-bit packed
if (pblk->flags & MMCMP_16BIT)
{
MMCMPBITBUFFER bb;
LPWORD pDest = (LPWORD)(pBuffer + psubblk->unpk_pos);
DWORD dwSize = psubblk->unpk_size >> 1;
DWORD dwPos = 0;
UINT numbits = pblk->num_bits;
UINT subblk = 0, oldval = 0;
#ifdef MMCMP_LOG
Log(" 16-bit block: pos=%d size=%d ", psubblk->unpk_pos, psubblk->unpk_size);
if (pblk->flags & MMCMP_DELTA) Log("DELTA ");
if (pblk->flags & MMCMP_ABS16) Log("ABS16 ");
Log("\n");
#endif
bb.bitcount = 0;
bb.bitbuffer = 0;
bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries;
bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size;
while (subblk < pblk->sub_blk)
{
UINT newval = 0x10000;
DWORD d = bb.GetBits(numbits+1);
if (d >= MMCMP16BitCommands[numbits])
{
UINT nFetch = MMCMP16BitFetch[numbits];
UINT newbits = bb.GetBits(nFetch) + ((d - MMCMP16BitCommands[numbits]) << nFetch);
if (newbits != numbits)
{
numbits = newbits & 0x0F;
} else
{
if ((d = bb.GetBits(4)) == 0x0F)
{
if (bb.GetBits(1)) break;
newval = 0xFFFF;
} else
{
newval = 0xFFF0 + d;
}
}
} else
{
newval = d;
}
if (newval < 0x10000)
{
newval = (newval & 1) ? (UINT)(-(LONG)((newval+1) >> 1)) : (UINT)(newval >> 1);
if (pblk->flags & MMCMP_DELTA)
{
newval += oldval;
oldval = newval;
} else
if (!(pblk->flags & MMCMP_ABS16))
{
newval ^= 0x8000;
}
pDest[dwPos++] = (WORD)newval;
}
if (dwPos >= dwSize)
{
subblk++;
dwPos = 0;
dwSize = psubblk[subblk].unpk_size >> 1;
pDest = (LPWORD)(pBuffer + psubblk[subblk].unpk_pos);
}
}
} else
// Data is 8-bit packed
{
MMCMPBITBUFFER bb;
LPBYTE pDest = pBuffer + psubblk->unpk_pos;
DWORD dwSize = psubblk->unpk_size;
DWORD dwPos = 0;
UINT numbits = pblk->num_bits;
UINT subblk = 0, oldval = 0;
LPCBYTE ptable = lpMemFile+dwMemPos;
bb.bitcount = 0;
bb.bitbuffer = 0;
bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries;
bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size;
while (subblk < pblk->sub_blk)
{
UINT newval = 0x100;
DWORD d = bb.GetBits(numbits+1);
if (d >= MMCMP8BitCommands[numbits])
{
UINT nFetch = MMCMP8BitFetch[numbits];
UINT newbits = bb.GetBits(nFetch) + ((d - MMCMP8BitCommands[numbits]) << nFetch);
if (newbits != numbits)
{
numbits = newbits & 0x07;
} else
{
if ((d = bb.GetBits(3)) == 7)
{
if (bb.GetBits(1)) break;
newval = 0xFF;
} else
{
newval = 0xF8 + d;
}
}
} else
{
newval = d;
}
if (newval < 0x100)
{
int n = ptable[newval];
if (pblk->flags & MMCMP_DELTA)
{
n += oldval;
oldval = n;
}
pDest[dwPos++] = (BYTE)n;
}
if (dwPos >= dwSize)
{
subblk++;
dwPos = 0;
dwSize = psubblk[subblk].unpk_size;
pDest = pBuffer + psubblk[subblk].unpk_pos;
}
}
}
}
*ppMemFile = pBuffer;
*pdwMemLength = dwFileSize;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
// PowerPack PP20 Unpacker
//
typedef struct _PPBITBUFFER
{
UINT bitcount;
ULONG bitbuffer;
LPCBYTE pStart;
LPCBYTE pSrc;
ULONG GetBits(UINT n);
} PPBITBUFFER;
ULONG PPBITBUFFER::GetBits(UINT n)
{
ULONG result = 0;
for (UINT i=0; i<n; i++)
{
if (!bitcount)
{
bitcount = 8;
if (pSrc != pStart) pSrc--;
bitbuffer = *pSrc;
}
result = (result<<1) | (bitbuffer&1);
bitbuffer >>= 1;
bitcount--;
}
return result;
}
VOID PP20_DoUnpack(const BYTE *pSrc, UINT nSrcLen, BYTE *pDst, UINT nDstLen)
{
PPBITBUFFER BitBuffer;
ULONG nBytesLeft;
BitBuffer.pStart = pSrc;
BitBuffer.pSrc = pSrc + nSrcLen - 4;
BitBuffer.bitbuffer = 0;
BitBuffer.bitcount = 0;
BitBuffer.GetBits(pSrc[nSrcLen-1]);
nBytesLeft = nDstLen;
while (nBytesLeft > 0)
{
if (!BitBuffer.GetBits(1))
{
UINT n = 1;
while (n < nBytesLeft)
{
UINT code = BitBuffer.GetBits(2);
n += code;
if (code != 3) break;
}
for (UINT i=0; i<n; i++)
{
pDst[--nBytesLeft] = (BYTE)BitBuffer.GetBits(8);
}
if (!nBytesLeft) break;
}
{
UINT n = BitBuffer.GetBits(2)+1;
UINT nbits = pSrc[n-1];
UINT nofs;
if (n==4)
{
nofs = BitBuffer.GetBits( (BitBuffer.GetBits(1)) ? nbits : 7 );
while (n < nBytesLeft)
{
UINT code = BitBuffer.GetBits(3);
n += code;
if (code != 7) break;
}
} else
{
nofs = BitBuffer.GetBits(nbits);
}
for (UINT i=0; i<=n; i++)
{
pDst[nBytesLeft-1] = (nBytesLeft+nofs < nDstLen) ? pDst[nBytesLeft+nofs] : 0;
if (!--nBytesLeft) break;
}
}
}
}
BOOL PP20_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength)
{
DWORD dwMemLength = *pdwMemLength;
LPCBYTE lpMemFile = *ppMemFile;
DWORD dwDstLen;
LPBYTE pBuffer;
if ((!lpMemFile) || (dwMemLength < 256) || (*(DWORD *)lpMemFile != 0x30325050)) return FALSE;
dwDstLen = (lpMemFile[dwMemLength-4]<<16) | (lpMemFile[dwMemLength-3]<<8) | (lpMemFile[dwMemLength-2]);
//Log("PP20 detected: Packed length=%d, Unpacked length=%d\n", dwMemLength, dwDstLen);
if ((dwDstLen < 512) || (dwDstLen > 0x400000) || (dwDstLen > 16*dwMemLength)) return FALSE;
if ((pBuffer = (LPBYTE)GlobalAllocPtr(GHND, (dwDstLen + 31) & ~15)) == NULL) return FALSE;
PP20_DoUnpack(lpMemFile+4, dwMemLength-4, pBuffer, dwDstLen);
*ppMemFile = pBuffer;
*pdwMemLength = dwDstLen;
return TRUE;
}

271
src/modplug/modplug.cpp Normal file
View file

@ -0,0 +1,271 @@
/*
* This source code is public domain.
*
* Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
*/
#include "modplug.h"
#include "stdafx.h"
#include "sndfile.h"
struct _ModPlugFile
{
CSoundFile mSoundFile;
};
namespace ModPlug
{
ModPlug_Settings gSettings =
{
MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION,
2,
16,
44100,
MODPLUG_RESAMPLE_LINEAR,
0,
0,
0,
0,
0,
0,
0
};
int gSampleSize;
void UpdateSettings(bool updateBasicConfig)
{
if(gSettings.mFlags & MODPLUG_ENABLE_REVERB)
{
CSoundFile::SetReverbParameters(gSettings.mReverbDepth,
gSettings.mReverbDelay);
}
if(gSettings.mFlags & MODPLUG_ENABLE_MEGABASS)
{
CSoundFile::SetXBassParameters(gSettings.mBassAmount,
gSettings.mBassRange);
}
else // modplug seems to ignore the SetWaveConfigEx() setting for bass boost
CSoundFile::SetXBassParameters(0, 0);
if(gSettings.mFlags & MODPLUG_ENABLE_SURROUND)
{
CSoundFile::SetSurroundParameters(gSettings.mSurroundDepth,
gSettings.mSurroundDelay);
}
if(updateBasicConfig)
{
CSoundFile::SetWaveConfig(gSettings.mFrequency,
gSettings.mBits,
gSettings.mChannels);
gSampleSize = gSettings.mBits / 8 * gSettings.mChannels;
}
CSoundFile::SetWaveConfigEx(gSettings.mFlags & MODPLUG_ENABLE_SURROUND,
!(gSettings.mFlags & MODPLUG_ENABLE_OVERSAMPLING),
gSettings.mFlags & MODPLUG_ENABLE_REVERB,
true,
gSettings.mFlags & MODPLUG_ENABLE_MEGABASS,
gSettings.mFlags & MODPLUG_ENABLE_NOISE_REDUCTION,
false);
CSoundFile::SetResamplingMode(gSettings.mResamplingMode);
}
}
ModPlugFile* ModPlug_Load(const void* data, int size)
{
ModPlugFile* result = new ModPlugFile;
ModPlug::UpdateSettings(true);
if(result->mSoundFile.Create((const BYTE*)data, size))
{
result->mSoundFile.SetRepeatCount(ModPlug::gSettings.mLoopCount);
return result;
}
else
{
delete result;
return NULL;
}
}
void ModPlug_Unload(ModPlugFile* file)
{
file->mSoundFile.Destroy();
delete file;
}
int ModPlug_Read(ModPlugFile* file, void* buffer, int size)
{
return file->mSoundFile.Read(buffer, size) * ModPlug::gSampleSize;
}
const char* ModPlug_GetName(ModPlugFile* file)
{
return file->mSoundFile.GetTitle();
}
int ModPlug_GetLength(ModPlugFile* file)
{
return file->mSoundFile.GetSongTime() * 1000;
}
void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc)
{
file->mSoundFile.gpSndMixHook = (LPSNDMIXHOOKPROC)proc ;
return;
}
void ModPlug_UnloadMixerCallback(ModPlugFile* file)
{
file->mSoundFile.gpSndMixHook = NULL;
return ;
}
unsigned int ModPlug_GetMasterVolume(ModPlugFile* file)
{
return (unsigned int)file->mSoundFile.m_nMasterVolume;
}
void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol)
{
(void)file->mSoundFile.SetMasterVolume( (UINT)cvol,
FALSE );
return ;
}
int ModPlug_GetCurrentSpeed(ModPlugFile* file)
{
return file->mSoundFile.m_nMusicSpeed;
}
int ModPlug_GetCurrentTempo(ModPlugFile* file)
{
return file->mSoundFile.m_nMusicTempo;
}
int ModPlug_GetCurrentOrder(ModPlugFile* file)
{
return file->mSoundFile.GetCurrentOrder();
}
int ModPlug_GetCurrentPattern(ModPlugFile* file)
{
return file->mSoundFile.GetCurrentPattern();
}
int ModPlug_GetCurrentRow(ModPlugFile* file)
{
return file->mSoundFile.m_nRow;
}
int ModPlug_GetPlayingChannels(ModPlugFile* file)
{
return ( file->mSoundFile.m_nMixChannels < file->mSoundFile.m_nMaxMixChannels ? file->mSoundFile.m_nMixChannels : file->mSoundFile.m_nMaxMixChannels );
}
void ModPlug_SeekOrder(ModPlugFile* file,int order)
{
file->mSoundFile.SetCurrentOrder(order);
}
int ModPlug_GetModuleType(ModPlugFile* file)
{
return file->mSoundFile.m_nType;
}
char* ModPlug_GetMessage(ModPlugFile* file)
{
return file->mSoundFile.m_lpszSongComments;
}
#ifndef MODPLUG_NO_FILESAVE
char ModPlug_ExportS3M(ModPlugFile* file,const char* filepath)
{
return (char)file->mSoundFile.SaveS3M(filepath,0);
}
char ModPlug_ExportXM(ModPlugFile* file,const char* filepath)
{
return (char)file->mSoundFile.SaveXM(filepath,0);
}
char ModPlug_ExportMOD(ModPlugFile* file,const char* filepath)
{
return (char)file->mSoundFile.SaveMod(filepath,0);
}
char ModPlug_ExportIT(ModPlugFile* file,const char* filepath)
{
return (char)file->mSoundFile.SaveIT(filepath,0);
}
#endif // MODPLUG_NO_FILESAVE
unsigned int ModPlug_NumInstruments(ModPlugFile* file)
{
return file->mSoundFile.m_nInstruments;
}
unsigned int ModPlug_NumSamples(ModPlugFile* file)
{
return file->mSoundFile.m_nSamples;
}
unsigned int ModPlug_NumPatterns(ModPlugFile* file)
{
return file->mSoundFile.GetNumPatterns();
}
unsigned int ModPlug_NumChannels(ModPlugFile* file)
{
return file->mSoundFile.GetNumChannels();
}
unsigned int ModPlug_SampleName(ModPlugFile* file,unsigned int qual,char* buff)
{
return file->mSoundFile.GetSampleName(qual,buff);
}
unsigned int ModPlug_InstrumentName(ModPlugFile* file,unsigned int qual,char* buff)
{
return file->mSoundFile.GetInstrumentName(qual,buff);
}
ModPlugNote* ModPlug_GetPattern(ModPlugFile* file,int pattern,unsigned int* numrows) {
if ( pattern<MAX_PATTERNS ) {
if (file->mSoundFile.Patterns[pattern]) {
if (numrows) *numrows=(unsigned int)file->mSoundFile.PatternSize[pattern];
return (ModPlugNote*)file->mSoundFile.Patterns[pattern];
}
}
return NULL;
}
void ModPlug_Seek(ModPlugFile* file, int millisecond)
{
int maxpos;
int maxtime = file->mSoundFile.GetSongTime() * 1000;
float postime;
if(millisecond > maxtime)
millisecond = maxtime;
maxpos = file->mSoundFile.GetMaxPosition();
postime = (float)maxpos / (float)maxtime;
file->mSoundFile.SetCurrentPos((int)(millisecond * postime));
}
void ModPlug_GetSettings(ModPlug_Settings* settings)
{
memcpy(settings, &ModPlug::gSettings, sizeof(ModPlug_Settings));
}
void ModPlug_SetSettings(const ModPlug_Settings* settings)
{
memcpy(&ModPlug::gSettings, settings, sizeof(ModPlug_Settings));
ModPlug::UpdateSettings(false); // do not update basic config.
}

168
src/modplug/modplug.h Normal file
View file

@ -0,0 +1,168 @@
/*
* This source code is public domain.
*
* Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
*/
#ifndef MODPLUG_H__INCLUDED
#define MODPLUG_H__INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
struct _ModPlugFile;
typedef struct _ModPlugFile ModPlugFile;
struct _ModPlugNote {
unsigned char Note;
unsigned char Instrument;
unsigned char VolumeEffect;
unsigned char Effect;
unsigned char Volume;
unsigned char Parameter;
};
typedef struct _ModPlugNote ModPlugNote;
typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long);
/* Load a mod file. [data] should point to a block of memory containing the complete
* file, and [size] should be the size of that block.
* Return the loaded mod file on success, or NULL on failure. */
ModPlugFile* ModPlug_Load(const void* data, int size);
/* Unload a mod file. */
void ModPlug_Unload(ModPlugFile* file);
/* Read sample data into the buffer. Returns the number of bytes read. If the end
* of the mod has been reached, zero is returned. */
int ModPlug_Read(ModPlugFile* file, void* buffer, int size);
/* Get the name of the mod. The returned buffer is stored within the ModPlugFile
* structure and will remain valid until you unload the file. */
const char* ModPlug_GetName(ModPlugFile* file);
/* Get the length of the mod, in milliseconds. Note that this result is not always
* accurate, especially in the case of mods with loops. */
int ModPlug_GetLength(ModPlugFile* file);
/* Seek to a particular position in the song. Note that seeking and MODs don't mix very
* well. Some mods will be missing instruments for a short time after a seek, as ModPlug
* does not scan the sequence backwards to find out which instruments were supposed to be
* playing at that time. (Doing so would be difficult and not very reliable.) Also,
* note that seeking is not very exact in some mods -- especially those for which
* ModPlug_GetLength() does not report the full length. */
void ModPlug_Seek(ModPlugFile* file, int millisecond);
enum _ModPlug_Flags
{
MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */
MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */
MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */
MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */
MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */
};
enum _ModPlug_ResamplingMode
{
MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */
MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */
MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */
MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */
};
typedef struct _ModPlug_Settings
{
int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */
/* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then
* down-mixes to the settings you choose. */
int mChannels; /* Number of channels - 1 for mono or 2 for stereo */
int mBits; /* Bits per sample - 8, 16, or 32 */
int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */
int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */
int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */
int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */
int mBassAmount; /* XBass level 0(quiet)-100(loud) */
int mBassRange; /* XBass cutoff in Hz 10-100 */
int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */
int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */
int mLoopCount; /* Number of times to loop. Zero prevents looping.
-1 loops forever. */
} ModPlug_Settings;
/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample,
* sampling rate, and loop count, will take effect immediately. Those options which don't
* take effect immediately will take effect the next time you load a mod. */
void ModPlug_GetSettings(ModPlug_Settings* settings);
void ModPlug_SetSettings(const ModPlug_Settings* settings);
/* New ModPlug API Functions */
/* NOTE: Master Volume (1-512) */
unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ;
void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ;
int ModPlug_GetCurrentSpeed(ModPlugFile* file);
int ModPlug_GetCurrentTempo(ModPlugFile* file);
int ModPlug_GetCurrentOrder(ModPlugFile* file);
int ModPlug_GetCurrentPattern(ModPlugFile* file);
int ModPlug_GetCurrentRow(ModPlugFile* file);
int ModPlug_GetPlayingChannels(ModPlugFile* file);
void ModPlug_SeekOrder(ModPlugFile* file,int order);
int ModPlug_GetModuleType(ModPlugFile* file);
char* ModPlug_GetMessage(ModPlugFile* file);
#ifndef MODPLUG_NO_FILESAVE
/*
* EXPERIMENTAL Export Functions
*/
/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/
char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath);
/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/
char ModPlug_ExportXM(ModPlugFile* file, const char* filepath);
/*Export to a Amiga MOD file. EXPERIMENTAL.*/
char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath);
/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */
char ModPlug_ExportIT(ModPlugFile* file, const char* filepath);
#endif // MODPLUG_NO_FILESAVE
unsigned int ModPlug_NumInstruments(ModPlugFile* file);
unsigned int ModPlug_NumSamples(ModPlugFile* file);
unsigned int ModPlug_NumPatterns(ModPlugFile* file);
unsigned int ModPlug_NumChannels(ModPlugFile* file);
unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff);
unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff);
/*
* Retrieve pattern note-data
*/
ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows);
/*
* =================
* Mixer callback
* =================
*
* Use this callback if you want to 'modify' the mixed data of LibModPlug.
*
* void proc(int* buffer,unsigned long channels,unsigned long nsamples) ;
*
* 'buffer': A buffer of mixed samples
* 'channels': N. of channels in the buffer
* 'nsamples': N. of samples in the buffeer (without taking care of n.channels)
*
* (Samples are signed 32-bit integers)
*/
void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ;
void ModPlug_UnloadMixerCallback(ModPlugFile* file) ;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

485
src/modplug/snd_dsp.cpp Normal file
View file

@ -0,0 +1,485 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
#ifdef MODPLUG_FASTSOUNDLIB
#define MODPLUG_NO_REVERB
#endif
// Delayed Surround Filters
#ifndef MODPLUG_FASTSOUNDLIB
#define nDolbyHiFltAttn 6
#define nDolbyHiFltMask 3
#define DOLBYATTNROUNDUP 31
#else
#define nDolbyHiFltAttn 3
#define nDolbyHiFltMask 3
#define DOLBYATTNROUNDUP 3
#endif
// Bass Expansion
#define XBASS_DELAY 14 // 2.5 ms
// Buffer Sizes
#define XBASSBUFFERSIZE 64 // 2 ms at 50KHz
#define FILTERBUFFERSIZE 64 // 1.25 ms
#define SURROUNDBUFFERSIZE ((MAX_SAMPLE_RATE * 50) / 1000)
#define REVERBBUFFERSIZE ((MAX_SAMPLE_RATE * 200) / 1000)
#define REVERBBUFFERSIZE2 ((REVERBBUFFERSIZE*13) / 17)
#define REVERBBUFFERSIZE3 ((REVERBBUFFERSIZE*7) / 13)
#define REVERBBUFFERSIZE4 ((REVERBBUFFERSIZE*7) / 19)
// DSP Effects: PUBLIC members
UINT CSoundFile::m_nXBassDepth = 6;
UINT CSoundFile::m_nXBassRange = XBASS_DELAY;
UINT CSoundFile::m_nReverbDepth = 1;
UINT CSoundFile::m_nReverbDelay = 100;
UINT CSoundFile::m_nProLogicDepth = 12;
UINT CSoundFile::m_nProLogicDelay = 20;
////////////////////////////////////////////////////////////////////
// DSP Effects internal state
// Bass Expansion: low-pass filter
static LONG nXBassSum = 0;
static LONG nXBassBufferPos = 0;
static LONG nXBassDlyPos = 0;
static LONG nXBassMask = 0;
// Noise Reduction: simple low-pass filter
static LONG nLeftNR = 0;
static LONG nRightNR = 0;
// Surround Encoding: 1 delay line + low-pass filter + high-pass filter
static LONG nSurroundSize = 0;
static LONG nSurroundPos = 0;
static LONG nDolbyDepth = 0;
static LONG nDolbyLoDlyPos = 0;
static LONG nDolbyLoFltPos = 0;
static LONG nDolbyLoFltSum = 0;
static LONG nDolbyHiFltPos = 0;
static LONG nDolbyHiFltSum = 0;
// Reverb: 4 delay lines + high-pass filter + low-pass filter
#ifndef MODPLUG_NO_REVERB
static LONG nReverbSize = 0;
static LONG nReverbBufferPos = 0;
static LONG nReverbSize2 = 0;
static LONG nReverbBufferPos2 = 0;
static LONG nReverbSize3 = 0;
static LONG nReverbBufferPos3 = 0;
static LONG nReverbSize4 = 0;
static LONG nReverbBufferPos4 = 0;
static LONG nReverbLoFltSum = 0;
static LONG nReverbLoFltPos = 0;
static LONG nReverbLoDlyPos = 0;
static LONG nFilterAttn = 0;
static LONG gRvbLowPass[8];
static LONG gRvbLPPos = 0;
static LONG gRvbLPSum = 0;
static LONG ReverbLoFilterBuffer[XBASSBUFFERSIZE];
static LONG ReverbLoFilterDelay[XBASSBUFFERSIZE];
static LONG ReverbBuffer[REVERBBUFFERSIZE];
static LONG ReverbBuffer2[REVERBBUFFERSIZE2];
static LONG ReverbBuffer3[REVERBBUFFERSIZE3];
static LONG ReverbBuffer4[REVERBBUFFERSIZE4];
#endif
static LONG XBassBuffer[XBASSBUFFERSIZE];
static LONG XBassDelay[XBASSBUFFERSIZE];
static LONG DolbyLoFilterBuffer[XBASSBUFFERSIZE];
static LONG DolbyLoFilterDelay[XBASSBUFFERSIZE];
static LONG DolbyHiFilterBuffer[FILTERBUFFERSIZE];
static LONG SurroundBuffer[SURROUNDBUFFERSIZE];
// Access the main temporary mix buffer directly: avoids an extra pointer
extern int MixSoundBuffer[MIXBUFFERSIZE*2];
//cextern int MixReverbBuffer[MIXBUFFERSIZE*2];
extern int MixReverbBuffer[MIXBUFFERSIZE*2];
static UINT GetMaskFromSize(UINT len)
//-----------------------------------
{
UINT n = 2;
while (n <= len) n <<= 1;
return ((n >> 1) - 1);
}
void CSoundFile::InitializeDSP(BOOL bReset)
//-----------------------------------------
{
if (!m_nReverbDelay) m_nReverbDelay = 100;
if (!m_nXBassRange) m_nXBassRange = XBASS_DELAY;
if (!m_nProLogicDelay) m_nProLogicDelay = 20;
if (m_nXBassDepth > 8) m_nXBassDepth = 8;
if (m_nXBassDepth < 2) m_nXBassDepth = 2;
if (bReset)
{
// Noise Reduction
nLeftNR = nRightNR = 0;
}
// Pro-Logic Surround
nSurroundPos = nSurroundSize = 0;
nDolbyLoFltPos = nDolbyLoFltSum = nDolbyLoDlyPos = 0;
nDolbyHiFltPos = nDolbyHiFltSum = 0;
if (gdwSoundSetup & SNDMIX_SURROUND)
{
memset(DolbyLoFilterBuffer, 0, sizeof(DolbyLoFilterBuffer));
memset(DolbyHiFilterBuffer, 0, sizeof(DolbyHiFilterBuffer));
memset(DolbyLoFilterDelay, 0, sizeof(DolbyLoFilterDelay));
memset(SurroundBuffer, 0, sizeof(SurroundBuffer));
nSurroundSize = (gdwMixingFreq * m_nProLogicDelay) / 1000;
if (nSurroundSize > SURROUNDBUFFERSIZE) nSurroundSize = SURROUNDBUFFERSIZE;
if (m_nProLogicDepth < 8) nDolbyDepth = (32 >> m_nProLogicDepth) + 32;
else nDolbyDepth = (m_nProLogicDepth < 16) ? (8 + (m_nProLogicDepth - 8) * 7) : 64;
nDolbyDepth >>= 2;
}
// Reverb Setup
#ifndef MODPLUG_NO_REVERB
if (gdwSoundSetup & SNDMIX_REVERB)
{
UINT nrs = (gdwMixingFreq * m_nReverbDelay) / 1000;
UINT nfa = m_nReverbDepth+1;
if (nrs > REVERBBUFFERSIZE) nrs = REVERBBUFFERSIZE;
if ((bReset) || (nrs != (UINT)nReverbSize) || (nfa != (UINT)nFilterAttn))
{
nFilterAttn = nfa;
nReverbSize = nrs;
nReverbBufferPos = nReverbBufferPos2 = nReverbBufferPos3 = nReverbBufferPos4 = 0;
nReverbLoFltSum = nReverbLoFltPos = nReverbLoDlyPos = 0;
gRvbLPSum = gRvbLPPos = 0;
nReverbSize2 = (nReverbSize * 13) / 17;
if (nReverbSize2 > REVERBBUFFERSIZE2) nReverbSize2 = REVERBBUFFERSIZE2;
nReverbSize3 = (nReverbSize * 7) / 13;
if (nReverbSize3 > REVERBBUFFERSIZE3) nReverbSize3 = REVERBBUFFERSIZE3;
nReverbSize4 = (nReverbSize * 7) / 19;
if (nReverbSize4 > REVERBBUFFERSIZE4) nReverbSize4 = REVERBBUFFERSIZE4;
memset(ReverbLoFilterBuffer, 0, sizeof(ReverbLoFilterBuffer));
memset(ReverbLoFilterDelay, 0, sizeof(ReverbLoFilterDelay));
memset(ReverbBuffer, 0, sizeof(ReverbBuffer));
memset(ReverbBuffer2, 0, sizeof(ReverbBuffer2));
memset(ReverbBuffer3, 0, sizeof(ReverbBuffer3));
memset(ReverbBuffer4, 0, sizeof(ReverbBuffer4));
memset(gRvbLowPass, 0, sizeof(gRvbLowPass));
}
} else nReverbSize = 0;
#endif
BOOL bResetBass = FALSE;
// Bass Expansion Reset
if (gdwSoundSetup & SNDMIX_MEGABASS)
{
UINT nXBassSamples = (gdwMixingFreq * m_nXBassRange) / 10000;
if (nXBassSamples > XBASSBUFFERSIZE) nXBassSamples = XBASSBUFFERSIZE;
UINT mask = GetMaskFromSize(nXBassSamples);
if ((bReset) || (mask != (UINT)nXBassMask))
{
nXBassMask = mask;
bResetBass = TRUE;
}
} else
{
nXBassMask = 0;
bResetBass = TRUE;
}
if (bResetBass)
{
nXBassSum = nXBassBufferPos = nXBassDlyPos = 0;
memset(XBassBuffer, 0, sizeof(XBassBuffer));
memset(XBassDelay, 0, sizeof(XBassDelay));
}
}
void CSoundFile::ProcessStereoDSP(int count)
//------------------------------------------
{
#ifndef MODPLUG_NO_REVERB
// Reverb
if (gdwSoundSetup & SNDMIX_REVERB)
{
int *pr = MixSoundBuffer, *pin = MixReverbBuffer, rvbcount = count;
do
{
int echo = ReverbBuffer[nReverbBufferPos] + ReverbBuffer2[nReverbBufferPos2]
+ ReverbBuffer3[nReverbBufferPos3] + ReverbBuffer4[nReverbBufferPos4]; // echo = reverb signal
// Delay line and remove Low Frequencies // v = original signal
int echodly = ReverbLoFilterDelay[nReverbLoDlyPos]; // echodly = delayed signal
ReverbLoFilterDelay[nReverbLoDlyPos] = echo >> 1;
nReverbLoDlyPos++;
nReverbLoDlyPos &= 0x1F;
int n = nReverbLoFltPos;
nReverbLoFltSum -= ReverbLoFilterBuffer[n];
int tmp = echo / 128;
ReverbLoFilterBuffer[n] = tmp;
nReverbLoFltSum += tmp;
echodly -= nReverbLoFltSum;
nReverbLoFltPos = (n + 1) & 0x3F;
// Reverb
int v = (pin[0]+pin[1]) >> nFilterAttn;
pr[0] += pin[0] + echodly;
pr[1] += pin[1] + echodly;
v += echodly >> 2;
ReverbBuffer3[nReverbBufferPos3] = v;
ReverbBuffer4[nReverbBufferPos4] = v;
v += echodly >> 4;
v >>= 1;
gRvbLPSum -= gRvbLowPass[gRvbLPPos];
gRvbLPSum += v;
gRvbLowPass[gRvbLPPos] = v;
gRvbLPPos++;
gRvbLPPos &= 7;
int vlp = gRvbLPSum >> 2;
ReverbBuffer[nReverbBufferPos] = vlp;
ReverbBuffer2[nReverbBufferPos2] = vlp;
if (++nReverbBufferPos >= nReverbSize) nReverbBufferPos = 0;
if (++nReverbBufferPos2 >= nReverbSize2) nReverbBufferPos2 = 0;
if (++nReverbBufferPos3 >= nReverbSize3) nReverbBufferPos3 = 0;
if (++nReverbBufferPos4 >= nReverbSize4) nReverbBufferPos4 = 0;
pr += 2;
pin += 2;
} while (--rvbcount);
}
#endif
// Dolby Pro-Logic Surround
if (gdwSoundSetup & SNDMIX_SURROUND)
{
int *pr = MixSoundBuffer, n = nDolbyLoFltPos;
for (int r=count; r; r--)
{
int v = (pr[0]+pr[1]+DOLBYATTNROUNDUP) >> (nDolbyHiFltAttn+1);
#ifndef MODPLUG_FASTSOUNDLIB
v *= (int)nDolbyDepth;
#endif
// Low-Pass Filter
nDolbyHiFltSum -= DolbyHiFilterBuffer[nDolbyHiFltPos];
DolbyHiFilterBuffer[nDolbyHiFltPos] = v;
nDolbyHiFltSum += v;
v = nDolbyHiFltSum;
nDolbyHiFltPos++;
nDolbyHiFltPos &= nDolbyHiFltMask;
// Surround
int secho = SurroundBuffer[nSurroundPos];
SurroundBuffer[nSurroundPos] = v;
// Delay line and remove low frequencies
v = DolbyLoFilterDelay[nDolbyLoDlyPos]; // v = delayed signal
DolbyLoFilterDelay[nDolbyLoDlyPos] = secho; // secho = signal
nDolbyLoDlyPos++;
nDolbyLoDlyPos &= 0x1F;
nDolbyLoFltSum -= DolbyLoFilterBuffer[n];
int tmp = secho / 64;
DolbyLoFilterBuffer[n] = tmp;
nDolbyLoFltSum += tmp;
v -= nDolbyLoFltSum;
n++;
n &= 0x3F;
// Add echo
pr[0] += v;
pr[1] -= v;
if (++nSurroundPos >= nSurroundSize) nSurroundPos = 0;
pr += 2;
}
nDolbyLoFltPos = n;
}
// Bass Expansion
if (gdwSoundSetup & SNDMIX_MEGABASS)
{
int *px = MixSoundBuffer;
int xba = m_nXBassDepth+1, xbamask = (1 << xba) - 1;
int n = nXBassBufferPos;
for (int x=count; x; x--)
{
nXBassSum -= XBassBuffer[n];
int tmp0 = px[0] + px[1];
int tmp = (tmp0 + ((tmp0 >> 31) & xbamask)) >> xba;
XBassBuffer[n] = tmp;
nXBassSum += tmp;
int v = XBassDelay[nXBassDlyPos];
XBassDelay[nXBassDlyPos] = px[0];
px[0] = v + nXBassSum;
v = XBassDelay[nXBassDlyPos+1];
XBassDelay[nXBassDlyPos+1] = px[1];
px[1] = v + nXBassSum;
nXBassDlyPos = (nXBassDlyPos + 2) & nXBassMask;
px += 2;
n++;
n &= nXBassMask;
}
nXBassBufferPos = n;
}
// Noise Reduction
if (gdwSoundSetup & SNDMIX_NOISEREDUCTION)
{
int n1 = nLeftNR, n2 = nRightNR;
int *pnr = MixSoundBuffer;
for (int nr=count; nr; nr--)
{
int vnr = pnr[0] >> 1;
pnr[0] = vnr + n1;
n1 = vnr;
vnr = pnr[1] >> 1;
pnr[1] = vnr + n2;
n2 = vnr;
pnr += 2;
}
nLeftNR = n1;
nRightNR = n2;
}
}
void CSoundFile::ProcessMonoDSP(int count)
//----------------------------------------
{
#ifndef MODPLUG_NO_REVERB
// Reverb
if (gdwSoundSetup & SNDMIX_REVERB)
{
int *pr = MixSoundBuffer, rvbcount = count, *pin = MixReverbBuffer;
do
{
int echo = ReverbBuffer[nReverbBufferPos] + ReverbBuffer2[nReverbBufferPos2]
+ ReverbBuffer3[nReverbBufferPos3] + ReverbBuffer4[nReverbBufferPos4]; // echo = reverb signal
// Delay line and remove Low Frequencies // v = original signal
int echodly = ReverbLoFilterDelay[nReverbLoDlyPos]; // echodly = delayed signal
ReverbLoFilterDelay[nReverbLoDlyPos] = echo >> 1;
nReverbLoDlyPos++;
nReverbLoDlyPos &= 0x1F;
int n = nReverbLoFltPos;
nReverbLoFltSum -= ReverbLoFilterBuffer[n];
int tmp = echo / 128;
ReverbLoFilterBuffer[n] = tmp;
nReverbLoFltSum += tmp;
echodly -= nReverbLoFltSum;
nReverbLoFltPos = (n + 1) & 0x3F;
// Reverb
int v = pin[0] >> (nFilterAttn-1);
*pr++ += pin[0] + echodly;
pin++;
v += echodly >> 2;
ReverbBuffer3[nReverbBufferPos3] = v;
ReverbBuffer4[nReverbBufferPos4] = v;
v += echodly >> 4;
v >>= 1;
gRvbLPSum -= gRvbLowPass[gRvbLPPos];
gRvbLPSum += v;
gRvbLowPass[gRvbLPPos] = v;
gRvbLPPos++;
gRvbLPPos &= 7;
int vlp = gRvbLPSum >> 2;
ReverbBuffer[nReverbBufferPos] = vlp;
ReverbBuffer2[nReverbBufferPos2] = vlp;
if (++nReverbBufferPos >= nReverbSize) nReverbBufferPos = 0;
if (++nReverbBufferPos2 >= nReverbSize2) nReverbBufferPos2 = 0;
if (++nReverbBufferPos3 >= nReverbSize3) nReverbBufferPos3 = 0;
if (++nReverbBufferPos4 >= nReverbSize4) nReverbBufferPos4 = 0;
} while (--rvbcount);
}
#endif
// Bass Expansion
if (gdwSoundSetup & SNDMIX_MEGABASS)
{
int *px = MixSoundBuffer;
int xba = m_nXBassDepth, xbamask = (1 << xba)-1;
int n = nXBassBufferPos;
for (int x=count; x; x--)
{
nXBassSum -= XBassBuffer[n];
int tmp0 = *px;
int tmp = (tmp0 + ((tmp0 >> 31) & xbamask)) >> xba;
XBassBuffer[n] = tmp;
nXBassSum += tmp;
int v = XBassDelay[nXBassDlyPos];
XBassDelay[nXBassDlyPos] = *px;
*px++ = v + nXBassSum;
nXBassDlyPos = (nXBassDlyPos + 2) & nXBassMask;
n++;
n &= nXBassMask;
}
nXBassBufferPos = n;
}
// Noise Reduction
if (gdwSoundSetup & SNDMIX_NOISEREDUCTION)
{
int n = nLeftNR;
int *pnr = MixSoundBuffer;
for (int nr=count; nr; pnr++, nr--)
{
int vnr = *pnr >> 1;
*pnr = vnr + n;
n = vnr;
}
nLeftNR = n;
}
}
/////////////////////////////////////////////////////////////////
// Clean DSP Effects interface
// [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms]
BOOL CSoundFile::SetReverbParameters(UINT nDepth, UINT nDelay)
//------------------------------------------------------------
{
if (nDepth > 100) nDepth = 100;
UINT gain = nDepth / 20;
if (gain > 4) gain = 4;
m_nReverbDepth = 4 - gain;
if (nDelay < 40) nDelay = 40;
if (nDelay > 250) nDelay = 250;
m_nReverbDelay = nDelay;
return TRUE;
}
// [XBass level 0(quiet)-100(loud)], [cutoff in Hz 20-100]
BOOL CSoundFile::SetXBassParameters(UINT nDepth, UINT nRange)
//-----------------------------------------------------------
{
if (nDepth > 100) nDepth = 100;
UINT gain = nDepth / 20;
if (gain > 4) gain = 4;
m_nXBassDepth = 8 - gain; // filter attenuation 1/256 .. 1/16
UINT range = nRange / 5;
if (range > 5) range -= 5; else range = 0;
if (nRange > 16) nRange = 16;
m_nXBassRange = 21 - range; // filter average on 0.5-1.6ms
return TRUE;
}
// [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-50ms]
BOOL CSoundFile::SetSurroundParameters(UINT nDepth, UINT nDelay)
//--------------------------------------------------------------
{
UINT gain = (nDepth * 16) / 100;
if (gain > 16) gain = 16;
if (gain < 1) gain = 1;
m_nProLogicDepth = gain;
if (nDelay < 4) nDelay = 4;
if (nDelay > 50) nDelay = 50;
m_nProLogicDelay = nDelay;
return TRUE;
}
BOOL CSoundFile::SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ)
//----------------------------------------------------------------------------------------------------------------------------
{
DWORD d = gdwSoundSetup & ~(SNDMIX_SURROUND | SNDMIX_NORESAMPLING | SNDMIX_REVERB | SNDMIX_HQRESAMPLER | SNDMIX_MEGABASS | SNDMIX_NOISEREDUCTION | SNDMIX_EQ);
if (bSurround) d |= SNDMIX_SURROUND;
if (bNoOverSampling) d |= SNDMIX_NORESAMPLING;
if (bReverb) d |= SNDMIX_REVERB;
if (hqido) d |= SNDMIX_HQRESAMPLER;
if (bMegaBass) d |= SNDMIX_MEGABASS;
if (bNR) d |= SNDMIX_NOISEREDUCTION;
if (bEQ) d |= SNDMIX_EQ;
gdwSoundSetup = d;
InitPlayer(FALSE);
return TRUE;
}

101
src/modplug/snd_flt.cpp Normal file
View file

@ -0,0 +1,101 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
// AWE32: cutoff = reg[0-255] * 31.25 + 100 -> [100Hz-8060Hz]
// EMU10K1 docs: cutoff = reg[0-127]*62+100
#define FILTER_PRECISION 8192
#ifndef NO_FILTER
#ifdef MSC_VER
#define _ASM_MATH
#endif
#ifdef _ASM_MATH
// pow(a,b) returns a^^b -> 2^^(b.log2(a))
static float pow(float a, float b)
{
long tmpint;
float result;
_asm {
fld b // Load b
fld a // Load a
fyl2x // ST(0) = b.log2(a)
fist tmpint // Store integer exponent
fisub tmpint // ST(0) = -1 <= (b*log2(a)) <= 1
f2xm1 // ST(0) = 2^(x)-1
fild tmpint // load integer exponent
fld1 // Load 1
fscale // ST(0) = 2^ST(1)
fstp ST(1) // Remove the integer from the stack
fmul ST(1), ST(0) // multiply with fractional part
faddp ST(1), ST(0) // add integer_part
fstp result // Store the result
}
return result;
}
#else
#include <math.h>
#endif // _ASM_MATH
DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const
//-----------------------------------------------------------------------
{
float Fc;
if (m_dwSongFlags & SONG_EXFILTERRANGE)
Fc = 110.0f * pow(2.0f, 0.25f + ((float)(nCutOff*(flt_modifier+256)))/(21.0f*512.0f));
else
Fc = 110.0f * pow(2.0f, 0.25f + ((float)(nCutOff*(flt_modifier+256)))/(24.0f*512.0f));
LONG freq = (LONG)Fc;
if (freq < 120) return 120;
if (freq > 10000) return 10000;
if (freq*2 > (LONG)gdwMixingFreq) freq = gdwMixingFreq>>1;
return (DWORD)freq;
}
// Simple 2-poles resonant filter
void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const
//----------------------------------------------------------------------------------------
{
float fc = (float)CutOffToFrequency(pChn->nCutOff, flt_modifier);
float fs = (float)gdwMixingFreq;
float fg, fb0, fb1;
fc *= (float)(2.0*3.14159265358/fs);
float dmpfac = pow(10.0f, -((24.0f / 128.0f)*(float)pChn->nResonance) / 20.0f);
float d = (1.0f-2.0f*dmpfac)* fc;
if (d>2.0) d = 2.0;
d = (2.0f*dmpfac - d)/fc;
float e = powf(1.0f/fc,2.0f);
fg=1/(1+d+e);
fb0=(d+e+e)/(1+d+e);
fb1=-e/(1+d+e);
pChn->nFilter_A0 = (int)(fg * FILTER_PRECISION);
pChn->nFilter_B0 = (int)(fb0 * FILTER_PRECISION);
pChn->nFilter_B1 = (int)(fb1 * FILTER_PRECISION);
if (bReset)
{
pChn->nFilter_Y1 = pChn->nFilter_Y2 = 0;
pChn->nFilter_Y3 = pChn->nFilter_Y4 = 0;
}
pChn->dwFlags |= CHN_FILTER;
}
#endif // NO_FILTER

2394
src/modplug/snd_fx.cpp Normal file

File diff suppressed because it is too large Load diff

1883
src/modplug/sndfile.cpp Normal file

File diff suppressed because it is too large Load diff

1014
src/modplug/sndfile.h Normal file

File diff suppressed because it is too large Load diff

1246
src/modplug/sndmix.cpp Normal file

File diff suppressed because it is too large Load diff

112
src/modplug/stdafx.h Normal file
View file

@ -0,0 +1,112 @@
/*
* This source code is public domain.
*
* Authors: Rani Assaf <rani@magic.metawire.com>,
* Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
#ifndef _STDAFX_H_
#define _STDAFX_H_
#ifdef _MSC_VER
#pragma warning (disable:4201)
#pragma warning (disable:4514)
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <stdio.h>
typedef __int64 LONGLONG;
typedef unsigned __int64 ULONGLONG;
inline void ProcessPlugins(int n) {}
#else
#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED)
# include "config.h"
# define CONFIG_H_INCLUDED 1
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef int8_t CHAR;
typedef uint8_t UCHAR;
typedef uint8_t* PUCHAR;
typedef uint16_t USHORT;
typedef uint32_t ULONG;
typedef uint32_t UINT;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef int64_t LONGLONG;
typedef uint64_t ULONGLONG;
typedef int32_t* LPLONG;
typedef uint32_t* LPDWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;
typedef uint8_t* LPBYTE;
typedef bool BOOL;
typedef char* LPSTR;
typedef void* LPVOID;
typedef uint16_t* LPWORD;
typedef const char* LPCSTR;
typedef void* PVOID;
typedef void VOID;
inline LONG MulDiv (long a, long b, long c)
{
// if (!c) return 0;
return ((uint64_t) a * (uint64_t) b ) / c;
}
#define MODPLUG_NO_FILESAVE
#define NO_AGC
#define LPCTSTR LPCSTR
#define lstrcpyn strncpy
#define lstrcpy strcpy
#define lstrcmp strcmp
#define WAVE_FORMAT_PCM 1
//#define ENABLE_EQ
#define GHND 0
inline int8_t * GlobalAllocPtr(unsigned int, size_t size)
{
int8_t * p = (int8_t *) malloc(size);
if (p != NULL) memset(p, 0, size);
return p;
}
inline void ProcessPlugins(int n) {}
#define GlobalFreePtr(p) free((void *)(p))
#define strnicmp(a,b,c) strncasecmp(a,b,c)
#define wsprintf sprintf
#ifndef FALSE
#define FALSE false
#endif
#ifndef TRUE
#define TRUE true
#endif
#endif // MSC_VER
#define MODPLUG_NO_FILESAVE // not needed and contains bugs
#endif

377
src/modplug/tables_mp.cpp Normal file
View file

@ -0,0 +1,377 @@
/*
* This source code is public domain.
*
* Authors: Olivier Lapicque <olivierl@jps.net>
*/
#include "stdafx.h"
#include "sndfile.h"
#ifndef MODPLUG_FASTSOUNDLIB
//#pragma data_seg(".tables")
#endif
BYTE ImpulseTrackerPortaVolCmd[16] =
{
0x00, 0x01, 0x04, 0x08, 0x10, 0x20, 0x40, 0x60,
0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
// Period table for Protracker octaves 0-5:
WORD ProTrackerPeriodTable[6*12] =
{
1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907,
856,808,762,720,678,640,604,570,538,508,480,453,
428,404,381,360,339,320,302,285,269,254,240,226,
214,202,190,180,170,160,151,143,135,127,120,113,
107,101,95,90,85,80,75,71,67,63,60,56,
53,50,47,45,42,40,37,35,33,31,30,28
};
WORD ProTrackerTunedPeriods[16*12] =
{
1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907,
1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900,
1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894,
1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888,
1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882,
1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874,
1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868,
1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862,
1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,
1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954,
1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,
1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,
1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934,
1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,
1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,
1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914
};
// S3M C-4 periods
WORD FreqS3MTable[16] =
{
1712,1616,1524,1440,1356,1280,
1208,1140,1076,1016,960,907,
0,0,0,0
};
// S3M FineTune frequencies
WORD S3MFineTuneTable[16] =
{
7895,7941,7985,8046,8107,8169,8232,8280,
8363,8413,8463,8529,8581,8651,8723,8757, // 8363*2^((i-8)/(12*8))
};
// Sinus table
short int ModSinusTable[64] =
{
0,12,25,37,49,60,71,81,90,98,106,112,117,122,125,126,
127,126,125,122,117,112,106,98,90,81,71,60,49,37,25,12,
0,-12,-25,-37,-49,-60,-71,-81,-90,-98,-106,-112,-117,-122,-125,-126,
-127,-126,-125,-122,-117,-112,-106,-98,-90,-81,-71,-60,-49,-37,-25,-12
};
// Triangle wave table (ramp down)
short int ModRampDownTable[64] =
{
0,-4,-8,-12,-16,-20,-24,-28,-32,-36,-40,-44,-48,-52,-56,-60,
-64,-68,-72,-76,-80,-84,-88,-92,-96,-100,-104,-108,-112,-116,-120,-124,
127,123,119,115,111,107,103,99,95,91,87,83,79,75,71,67,
63,59,55,51,47,43,39,35,31,27,23,19,15,11,7,3
};
// Square wave table
short int ModSquareTable[64] =
{
127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,
127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,
-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,
-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127
};
// Random wave table
short int ModRandomTable[64] =
{
98,-127,-43,88,102,41,-65,-94,125,20,-71,-86,-70,-32,-16,-96,
17,72,107,-5,116,-69,-62,-40,10,-61,65,109,-18,-38,-13,-76,
-23,88,21,-94,8,106,21,-112,6,109,20,-88,-30,9,-127,118,
42,-34,89,-4,-51,-72,21,-29,112,123,84,-101,-92,98,-54,-95
};
// volume fade tables for Retrig Note:
signed char retrigTable1[16] =
{ 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 0, 0, 24, 32 };
signed char retrigTable2[16] =
{ 0, -1, -2, -4, -8, -16, 0, 0, 0, 1, 2, 4, 8, 16, 0, 0 };
WORD XMPeriodTable[104] =
{
907,900,894,887,881,875,868,862,856,850,844,838,832,826,820,814,
808,802,796,791,785,779,774,768,762,757,752,746,741,736,730,725,
720,715,709,704,699,694,689,684,678,675,670,665,660,655,651,646,
640,636,632,628,623,619,614,610,604,601,597,592,588,584,580,575,
570,567,563,559,555,551,547,543,538,535,532,528,524,520,516,513,
508,505,502,498,494,491,487,484,480,477,474,470,467,463,460,457,
453,450,447,443,440,437,434,431
};
UINT XMLinearTable[768] =
{
535232,534749,534266,533784,533303,532822,532341,531861,
531381,530902,530423,529944,529466,528988,528511,528034,
527558,527082,526607,526131,525657,525183,524709,524236,
523763,523290,522818,522346,521875,521404,520934,520464,
519994,519525,519057,518588,518121,517653,517186,516720,
516253,515788,515322,514858,514393,513929,513465,513002,
512539,512077,511615,511154,510692,510232,509771,509312,
508852,508393,507934,507476,507018,506561,506104,505647,
505191,504735,504280,503825,503371,502917,502463,502010,
501557,501104,500652,500201,499749,499298,498848,498398,
497948,497499,497050,496602,496154,495706,495259,494812,
494366,493920,493474,493029,492585,492140,491696,491253,
490809,490367,489924,489482,489041,488600,488159,487718,
487278,486839,486400,485961,485522,485084,484647,484210,
483773,483336,482900,482465,482029,481595,481160,480726,
480292,479859,479426,478994,478562,478130,477699,477268,
476837,476407,475977,475548,475119,474690,474262,473834,
473407,472979,472553,472126,471701,471275,470850,470425,
470001,469577,469153,468730,468307,467884,467462,467041,
466619,466198,465778,465358,464938,464518,464099,463681,
463262,462844,462427,462010,461593,461177,460760,460345,
459930,459515,459100,458686,458272,457859,457446,457033,
456621,456209,455797,455386,454975,454565,454155,453745,
453336,452927,452518,452110,451702,451294,450887,450481,
450074,449668,449262,448857,448452,448048,447644,447240,
446836,446433,446030,445628,445226,444824,444423,444022,
443622,443221,442821,442422,442023,441624,441226,440828,
440430,440033,439636,439239,438843,438447,438051,437656,
437261,436867,436473,436079,435686,435293,434900,434508,
434116,433724,433333,432942,432551,432161,431771,431382,
430992,430604,430215,429827,429439,429052,428665,428278,
427892,427506,427120,426735,426350,425965,425581,425197,
424813,424430,424047,423665,423283,422901,422519,422138,
421757,421377,420997,420617,420237,419858,419479,419101,
418723,418345,417968,417591,417214,416838,416462,416086,
415711,415336,414961,414586,414212,413839,413465,413092,
412720,412347,411975,411604,411232,410862,410491,410121,
409751,409381,409012,408643,408274,407906,407538,407170,
406803,406436,406069,405703,405337,404971,404606,404241,
403876,403512,403148,402784,402421,402058,401695,401333,
400970,400609,400247,399886,399525,399165,398805,398445,
398086,397727,397368,397009,396651,396293,395936,395579,
395222,394865,394509,394153,393798,393442,393087,392733,
392378,392024,391671,391317,390964,390612,390259,389907,
389556,389204,388853,388502,388152,387802,387452,387102,
386753,386404,386056,385707,385359,385012,384664,384317,
383971,383624,383278,382932,382587,382242,381897,381552,
381208,380864,380521,380177,379834,379492,379149,378807,
378466,378124,377783,377442,377102,376762,376422,376082,
375743,375404,375065,374727,374389,374051,373714,373377,
373040,372703,372367,372031,371695,371360,371025,370690,
370356,370022,369688,369355,369021,368688,368356,368023,
367691,367360,367028,366697,366366,366036,365706,365376,
365046,364717,364388,364059,363731,363403,363075,362747,
362420,362093,361766,361440,361114,360788,360463,360137,
359813,359488,359164,358840,358516,358193,357869,357547,
357224,356902,356580,356258,355937,355616,355295,354974,
354654,354334,354014,353695,353376,353057,352739,352420,
352103,351785,351468,351150,350834,350517,350201,349885,
349569,349254,348939,348624,348310,347995,347682,347368,
347055,346741,346429,346116,345804,345492,345180,344869,
344558,344247,343936,343626,343316,343006,342697,342388,
342079,341770,341462,341154,340846,340539,340231,339924,
339618,339311,339005,338700,338394,338089,337784,337479,
337175,336870,336566,336263,335959,335656,335354,335051,
334749,334447,334145,333844,333542,333242,332941,332641,
332341,332041,331741,331442,331143,330844,330546,330247,
329950,329652,329355,329057,328761,328464,328168,327872,
327576,327280,326985,326690,326395,326101,325807,325513,
325219,324926,324633,324340,324047,323755,323463,323171,
322879,322588,322297,322006,321716,321426,321136,320846,
320557,320267,319978,319690,319401,319113,318825,318538,
318250,317963,317676,317390,317103,316817,316532,316246,
315961,315676,315391,315106,314822,314538,314254,313971,
313688,313405,313122,312839,312557,312275,311994,311712,
311431,311150,310869,310589,310309,310029,309749,309470,
309190,308911,308633,308354,308076,307798,307521,307243,
306966,306689,306412,306136,305860,305584,305308,305033,
304758,304483,304208,303934,303659,303385,303112,302838,
302565,302292,302019,301747,301475,301203,300931,300660,
300388,300117,299847,299576,299306,299036,298766,298497,
298227,297958,297689,297421,297153,296884,296617,296349,
296082,295815,295548,295281,295015,294749,294483,294217,
293952,293686,293421,293157,292892,292628,292364,292100,
291837,291574,291311,291048,290785,290523,290261,289999,
289737,289476,289215,288954,288693,288433,288173,287913,
287653,287393,287134,286875,286616,286358,286099,285841,
285583,285326,285068,284811,284554,284298,284041,283785,
283529,283273,283017,282762,282507,282252,281998,281743,
281489,281235,280981,280728,280475,280222,279969,279716,
279464,279212,278960,278708,278457,278206,277955,277704,
277453,277203,276953,276703,276453,276204,275955,275706,
275457,275209,274960,274712,274465,274217,273970,273722,
273476,273229,272982,272736,272490,272244,271999,271753,
271508,271263,271018,270774,270530,270286,270042,269798,
269555,269312,269069,268826,268583,268341,268099,267857
};
signed char ft2VibratoTable[256] =
{
0,-2,-3,-5,-6,-8,-9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
-24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,
-43,-44,-45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,
-56,-57,-58,-59,-59,-60,-60,-61,-61,-62,-62,-62,-63,-63,
-63,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-63,-63,
-63,-62,-62,-62,-61,-61,-60,-60,-59,-59,-58,-57,-56,-56,
-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42,
-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,-24,-23,
-22,-20,-19,-17,-16,-14,-12,-11,-9,-8,-6,-5,-3,-2,0,
2,3,5,6,8,9,11,12,14,16,17,19,20,22,23,24,26,27,29,30,
32,33,34,36,37,38,39,41,42,43,44,45,46,47,48,49,50,51,
52,53,54,55,56,56,57,58,59,59,60,60,61,61,62,62,62,63,
63,63,64,64,64,64,64,64,64,64,64,64,64,63,63,63,62,62,
62,61,61,60,60,59,59,58,57,56,56,55,54,53,52,51,50,49,
48,47,46,45,44,43,42,41,39,38,37,36,34,33,32,30,29,27,
26,24,23,22,20,19,17,16,14,12,11,9,8,6,5,3,2
};
DWORD FineLinearSlideUpTable[16] =
{
65536, 65595, 65654, 65714, 65773, 65832, 65892, 65951,
66011, 66071, 66130, 66190, 66250, 66309, 66369, 66429
};
DWORD FineLinearSlideDownTable[16] =
{
65535, 65477, 65418, 65359, 65300, 65241, 65182, 65123,
65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645
};
DWORD LinearSlideUpTable[256] =
{
65536, 65773, 66010, 66249, 66489, 66729, 66971, 67213,
67456, 67700, 67945, 68190, 68437, 68685, 68933, 69182,
69432, 69684, 69936, 70189, 70442, 70697, 70953, 71209,
71467, 71725, 71985, 72245, 72507, 72769, 73032, 73296,
73561, 73827, 74094, 74362, 74631, 74901, 75172, 75444,
75717, 75991, 76265, 76541, 76818, 77096, 77375, 77655,
77935, 78217, 78500, 78784, 79069, 79355, 79642, 79930,
80219, 80509, 80800, 81093, 81386, 81680, 81976, 82272,
82570, 82868, 83168, 83469, 83771, 84074, 84378, 84683,
84989, 85297, 85605, 85915, 86225, 86537, 86850, 87164,
87480, 87796, 88113, 88432, 88752, 89073, 89395, 89718,
90043, 90369, 90695, 91023, 91353, 91683, 92015, 92347,
92681, 93017, 93353, 93691, 94029, 94370, 94711, 95053,
95397, 95742, 96088, 96436, 96785, 97135, 97486, 97839,
98193, 98548, 98904, 99262, 99621, 99981, 100343, 100706,
101070, 101435, 101802, 102170, 102540, 102911, 103283, 103657,
104031, 104408, 104785, 105164, 105545, 105926, 106309, 106694,
107080, 107467, 107856, 108246, 108637, 109030, 109425, 109820,
110217, 110616, 111016, 111418, 111821, 112225, 112631, 113038,
113447, 113857, 114269, 114682, 115097, 115514, 115931, 116351,
116771, 117194, 117618, 118043, 118470, 118898, 119328, 119760,
120193, 120628, 121064, 121502, 121941, 122382, 122825, 123269,
123715, 124162, 124611, 125062, 125514, 125968, 126424, 126881,
127340, 127801, 128263, 128727, 129192, 129660, 130129, 130599,
131072, 131546, 132021, 132499, 132978, 133459, 133942, 134426,
134912, 135400, 135890, 136381, 136875, 137370, 137866, 138365,
138865, 139368, 139872, 140378, 140885, 141395, 141906, 142419,
142935, 143451, 143970, 144491, 145014, 145538, 146064, 146593,
147123, 147655, 148189, 148725, 149263, 149803, 150344, 150888,
151434, 151982, 152531, 153083, 153637, 154192, 154750, 155310,
155871, 156435, 157001, 157569, 158138, 158710, 159284, 159860,
160439, 161019, 161601, 162186, 162772, 163361, 163952, 164545,
};
DWORD LinearSlideDownTable[256] =
{
65536, 65299, 65064, 64830, 64596, 64363, 64131, 63900,
63670, 63440, 63212, 62984, 62757, 62531, 62305, 62081,
61857, 61634, 61412, 61191, 60970, 60751, 60532, 60314,
60096, 59880, 59664, 59449, 59235, 59021, 58809, 58597,
58385, 58175, 57965, 57757, 57548, 57341, 57134, 56928,
56723, 56519, 56315, 56112, 55910, 55709, 55508, 55308,
55108, 54910, 54712, 54515, 54318, 54123, 53928, 53733,
53540, 53347, 53154, 52963, 52772, 52582, 52392, 52204,
52015, 51828, 51641, 51455, 51270, 51085, 50901, 50717,
50535, 50353, 50171, 49990, 49810, 49631, 49452, 49274,
49096, 48919, 48743, 48567, 48392, 48218, 48044, 47871,
47698, 47526, 47355, 47185, 47014, 46845, 46676, 46508,
46340, 46173, 46007, 45841, 45676, 45511, 45347, 45184,
45021, 44859, 44697, 44536, 44376, 44216, 44056, 43898,
43740, 43582, 43425, 43268, 43112, 42957, 42802, 42648,
42494, 42341, 42189, 42037, 41885, 41734, 41584, 41434,
41285, 41136, 40988, 40840, 40693, 40546, 40400, 40254,
40109, 39965, 39821, 39677, 39534, 39392, 39250, 39108,
38967, 38827, 38687, 38548, 38409, 38270, 38132, 37995,
37858, 37722, 37586, 37450, 37315, 37181, 37047, 36913,
36780, 36648, 36516, 36384, 36253, 36122, 35992, 35862,
35733, 35604, 35476, 35348, 35221, 35094, 34968, 34842,
34716, 34591, 34466, 34342, 34218, 34095, 33972, 33850,
33728, 33606, 33485, 33364, 33244, 33124, 33005, 32886,
32768, 32649, 32532, 32415, 32298, 32181, 32065, 31950,
31835, 31720, 31606, 31492, 31378, 31265, 31152, 31040,
30928, 30817, 30706, 30595, 30485, 30375, 30266, 30157,
30048, 29940, 29832, 29724, 29617, 29510, 29404, 29298,
29192, 29087, 28982, 28878, 28774, 28670, 28567, 28464,
28361, 28259, 28157, 28056, 27955, 27854, 27754, 27654,
27554, 27455, 27356, 27257, 27159, 27061, 26964, 26866,
26770, 26673, 26577, 26481, 26386, 26291, 26196, 26102,
};
int SpectrumSinusTable[256*2] =
{
0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11,
12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23,
24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 34,
35, 36, 36, 37, 38, 38, 39, 39, 40, 41, 41, 42, 42, 43, 44, 44,
45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 52, 52,
53, 53, 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 57, 58, 58, 58,
59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62,
62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62,
62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59,
59, 58, 58, 58, 57, 57, 57, 56, 56, 55, 55, 55, 54, 54, 53, 53,
53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45,
45, 44, 44, 43, 42, 42, 41, 41, 40, 39, 39, 38, 38, 37, 36, 36,
35, 34, 34, 33, 32, 32, 31, 30, 30, 29, 28, 28, 27, 26, 25, 25,
24, 23, 22, 22, 21, 20, 20, 19, 18, 17, 17, 16, 15, 14, 14, 13,
12, 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 3, 3, 2, 1, 0,
0, -1, -1, -2, -3, -3, -4, -5, -6, -7, -7, -8, -9, -10, -10, -11,
-12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -20, -20, -21, -22, -22, -23,
-24, -25, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34,
-35, -36, -36, -37, -38, -38, -39, -39, -40, -41, -41, -42, -42, -43, -44, -44,
-45, -45, -46, -46, -47, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52,
-53, -53, -53, -54, -54, -55, -55, -55, -56, -56, -57, -57, -57, -58, -58, -58,
-59, -59, -59, -59, -60, -60, -60, -60, -61, -61, -61, -61, -61, -62, -62, -62,
-62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -62, -62,
-62, -62, -62, -62, -61, -61, -61, -61, -61, -60, -60, -60, -60, -59, -59, -59,
-59, -58, -58, -58, -57, -57, -57, -56, -56, -55, -55, -55, -54, -54, -53, -53,
-53, -52, -52, -51, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -46, -45,
-45, -44, -44, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -36,
-35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25,
-24, -23, -22, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13,
-12, -11, -10, -10, -9, -8, -7, -7, -6, -5, -4, -3, -3, -2, -1, 0,
};

View file

@ -555,20 +555,19 @@ void FBehavior::StaticLoadDefaultModules ()
while ((lump = Wads.FindLump ("LOADACS", &lastlump)) != -1)
{
SC_OpenLumpNum (lump, "LOADACS");
while (SC_GetString())
FScanner sc(lump, "LOADACS");
while (sc.GetString())
{
int acslump = Wads.CheckNumForName (sc_String, ns_acslibrary);
int acslump = Wads.CheckNumForName (sc.String, ns_acslibrary);
if (acslump >= 0)
{
StaticLoadModule (acslump);
}
else
{
Printf ("Could not find autoloaded ACS library %s\n", sc_String);
Printf ("Could not find autoloaded ACS library %s\n", sc.String);
}
}
SC_Close ();
}
}
@ -2153,6 +2152,22 @@ void DLevelScript::DoSetFont (int fontnum)
#define APROP_DeathSound 8
#define APROP_ActiveSound 9
// These are needed for ACS's APROP_RenderStyle
static const int LegacyRenderStyleIndices[] =
{
0, // STYLE_None,
1, // STYLE_Normal,
2, // STYLE_Fuzzy,
3, // STYLE_SoulTrans,
4, // STYLE_OptFuzzy,
5, // STYLE_Stencil,
64, // STYLE_Translucent
65, // STYLE_Add,
66, // STYLE_Shaded,
67, // STYLE_TranslucentStencil,
-1
};
void DLevelScript::SetActorProperty (int tid, int property, int value)
{
if (tid == 0)
@ -2200,7 +2215,14 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value)
break;
case APROP_RenderStyle:
actor->RenderStyle = value;
for(int i=0; LegacyRenderStyleIndices[i] >= 0; i++)
{
if (LegacyRenderStyleIndices[i] == value)
{
actor->RenderStyle = ERenderStyle(i);
break;
}
}
break;
case APROP_Ambush:
@ -2290,7 +2312,16 @@ int DLevelScript::GetActorProperty (int tid, int property)
case APROP_Speed: return actor->Speed;
case APROP_Damage: return actor->Damage; // Should this call GetMissileDamage() instead?
case APROP_Alpha: return actor->alpha;
case APROP_RenderStyle: return actor->RenderStyle;
case APROP_RenderStyle: for (int style = STYLE_None; style < STYLE_Count; ++style)
{ // Check for a legacy render style that matches.
if (LegacyRenderStyles[style] == actor->RenderStyle)
{
return LegacyRenderStyleIndices[style];
}
}
// The current render style isn't expressable as a legacy style,
// so pretends it's normal.
return STYLE_Normal;
case APROP_Gravity: return actor->gravity;
case APROP_Ambush: return !!(actor->flags & MF_AMBUSH);
case APROP_ChaseGoal: return !!(actor->flags5 & MF5_CHASEGOAL);

View file

@ -275,7 +275,7 @@ static FStrifeDialogueNode *ReadRetailNode (FWadLump *lump, DWORD &prevSpeakerTy
// The speaker's portrait, if any.
speech.Backdrop[8] = 0;
node->Backdrop = TexMan.AddPatch (speech.Backdrop);
node->Backdrop = TexMan.CheckForTexture (speech.Backdrop, FTexture::TEX_MiscPatch);
// The speaker's voice for this node, if any.
speech.Sound[8] = 0;

View file

@ -802,7 +802,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay)
return rtn;
}
void P_ParseAnimatedDoor()
void P_ParseAnimatedDoor(FScanner &sc)
{
const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny;
FDoorAnimation anim;
@ -810,8 +810,8 @@ void P_ParseAnimatedDoor()
bool error = false;
int v;
SC_MustGetString();
anim.BaseTexture = TexMan.CheckForTexture (sc_String, FTexture::TEX_Wall, texflags);
sc.MustGetString();
anim.BaseTexture = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags);
anim.OpenSound = NULL;
anim.CloseSound = NULL;
@ -820,38 +820,38 @@ void P_ParseAnimatedDoor()
error = true;
}
while (SC_GetString ())
while (sc.GetString ())
{
if (SC_Compare ("opensound"))
if (sc.Compare ("opensound"))
{
SC_MustGetString ();
anim.OpenSound = copystring (sc_String);
sc.MustGetString ();
anim.OpenSound = copystring (sc.String);
}
else if (SC_Compare ("closesound"))
else if (sc.Compare ("closesound"))
{
SC_MustGetString ();
anim.CloseSound = copystring (sc_String);
sc.MustGetString ();
anim.CloseSound = copystring (sc.String);
}
else if (SC_Compare ("pic"))
else if (sc.Compare ("pic"))
{
SC_MustGetString ();
if (IsNum (sc_String))
sc.MustGetString ();
if (IsNum (sc.String))
{
v = atoi(sc_String) + anim.BaseTexture -1;
v = atoi(sc.String) + anim.BaseTexture -1;
}
else
{
v = TexMan.CheckForTexture (sc_String, FTexture::TEX_Wall, texflags);
v = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags);
if (v == -1 && anim.BaseTexture >= 0 && !error)
{
SC_ScriptError ("Unknown texture %s", sc_String);
sc.ScriptError ("Unknown texture %s", sc.String);
}
frames.Push (v);
}
}
else
{
SC_UnGet ();
sc.UnGet ();
break;
}
}

View file

@ -1664,8 +1664,6 @@ void A_Look (AActor *actor)
//==========================================================================
void A_Wander (AActor *self)
{
int delta;
// [RH] Strife probably clears this flag somewhere, but I couldn't find where.
// This seems as good a place as any.
self->flags4 &= ~MF4_INCOMBAT;
@ -1673,9 +1671,9 @@ void A_Wander (AActor *self)
if (self->flags4 & MF4_STANDSTILL)
return;
if (self->threshold != 0)
if (self->reactiontime != 0)
{
self->threshold--;
self->reactiontime--;
return;
}
@ -1683,7 +1681,7 @@ void A_Wander (AActor *self)
if (self->movedir < DI_NODIR)
{
self->angle &= (angle_t)(7<<29);
delta = self->angle - (self->movedir << 29);
int delta = self->angle - (self->movedir << 29);
if (delta > 0)
{
self->angle -= ANG90/2;

Some files were not shown because too many files have changed in this diff Show more