ti80 core
This commit is contained in:
parent
aa099e0941
commit
2c3b6b3cd4
Binary file not shown.
|
@ -811,6 +811,8 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public static readonly IReadOnlyCollection<string> TI83 = new[] { "83g", "83l", "83p" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> TIC80 = new[] { "tic" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> UZE = new[] { "uze" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> VB = new[] { "vb" };
|
||||
|
@ -842,6 +844,7 @@ namespace BizHawk.Client.Common
|
|||
.Concat(SMS)
|
||||
.Concat(SNES)
|
||||
.Concat(TI83)
|
||||
.Concat(TIC80)
|
||||
.Concat(UZE)
|
||||
.Concat(VB)
|
||||
.Concat(VEC)
|
||||
|
@ -872,6 +875,7 @@ namespace BizHawk.Client.Common
|
|||
new FilesystemFilter("ColecoVision", RomFileExtensions.Coleco, addArchiveExts: true),
|
||||
new FilesystemFilter("IntelliVision", RomFileExtensions.INTV, addArchiveExts: true),
|
||||
new FilesystemFilter("TI-83", RomFileExtensions.TI83, addArchiveExts: true),
|
||||
new FilesystemFilter("TIC-80", RomFileExtensions.TIC80, addArchiveExts: true),
|
||||
FilesystemFilter.Archives,
|
||||
new FilesystemFilter("Genesis", RomFileExtensions.GEN.Concat(new[] { "bin", "cue", "ccd" }).ToList(), addArchiveExts: true),
|
||||
new FilesystemFilter("SID Commodore 64 Music File", Array.Empty<string>(), devBuildExtraExts: new[] { "sid" }, devBuildAddArchiveExts: true),
|
||||
|
|
|
@ -393,6 +393,10 @@ namespace BizHawk.Emulation.Common
|
|||
game.System = VSystemID.Raw.UZE;
|
||||
break;
|
||||
|
||||
case ".TIC":
|
||||
game.System = VSystemID.Raw.TIC80;
|
||||
break;
|
||||
|
||||
case ".32X":
|
||||
game.System = VSystemID.Raw.Sega32X;
|
||||
game.AddOption("32X", "true");
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace BizHawk.Emulation.Common
|
|||
[VSystemID.Raw.SMS] = "Sega Master System",
|
||||
[VSystemID.Raw.SNES] = "SNES",
|
||||
[VSystemID.Raw.TI83] = "TI - 83",
|
||||
[VSystemID.Raw.TIC80] = "TIC-80",
|
||||
[VSystemID.Raw.UZE] = "Uzebox",
|
||||
[VSystemID.Raw.VB] = "Virtual Boy",
|
||||
[VSystemID.Raw.VEC] = "Vectrex",
|
||||
|
|
|
@ -51,7 +51,8 @@ namespace BizHawk.Emulation.Common
|
|||
public const string SGXCD = "SGXCD";
|
||||
public const string SMS = "SMS";
|
||||
public const string SNES = "SNES";
|
||||
public const string TI83 = "TI83";
|
||||
public const string TI83 = "TI83";
|
||||
public const string TIC80 = "TIC80";
|
||||
public const string UZE = "UZE";
|
||||
public const string VB = "VB";
|
||||
public const string VEC = "VEC";
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.BizInvoke;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.TIC80
|
||||
{
|
||||
public abstract class LibTIC80 : LibWaterboxCore
|
||||
{
|
||||
[Flags]
|
||||
public enum TI80Gamepad : byte
|
||||
{
|
||||
Up = 0x01,
|
||||
Down = 0x02,
|
||||
Left = 0x04,
|
||||
Right = 0x08,
|
||||
A = 0x10,
|
||||
B = 0x20,
|
||||
X = 0x40,
|
||||
Y = 0x80,
|
||||
}
|
||||
|
||||
public enum TI80Keys : byte
|
||||
{
|
||||
Unknown,
|
||||
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
|
||||
_0,
|
||||
_1,
|
||||
_2,
|
||||
_3,
|
||||
_4,
|
||||
_5,
|
||||
_6,
|
||||
_7,
|
||||
_8,
|
||||
_9,
|
||||
|
||||
Minus,
|
||||
Equals,
|
||||
Left_Bracket,
|
||||
Right_Bracket,
|
||||
Backslash,
|
||||
Semicolon,
|
||||
Apostrophe,
|
||||
Grave,
|
||||
Comma,
|
||||
Period,
|
||||
Slash,
|
||||
|
||||
Space,
|
||||
Tab,
|
||||
|
||||
Return,
|
||||
Backspace,
|
||||
Delete,
|
||||
Insert,
|
||||
|
||||
Page_Up,
|
||||
Page_Down,
|
||||
Home,
|
||||
End,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
|
||||
Caps_Lock,
|
||||
Control,
|
||||
Shift,
|
||||
Alt,
|
||||
|
||||
Escape,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public new class FrameInfo : LibWaterboxCore.FrameInfo
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public TI80Gamepad[] Gamepads = new TI80Gamepad[4];
|
||||
|
||||
public sbyte MouseX;
|
||||
public sbyte MouseY;
|
||||
public ushort MouseButtons;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public TI80Keys[] Keys = new TI80Keys[4];
|
||||
|
||||
public bool Crop;
|
||||
}
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract bool Init(byte[] rom, int sz);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.TIC80
|
||||
{
|
||||
public partial class TIC80 : ISettable<TIC80.TIC80Settings, object>
|
||||
{
|
||||
private TIC80Settings _settings;
|
||||
|
||||
public TIC80Settings GetSettings()
|
||||
=> _settings.Clone();
|
||||
|
||||
public object GetSyncSettings()
|
||||
=> null;
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(TIC80Settings o)
|
||||
{
|
||||
_settings = o;
|
||||
return PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
public PutSettingsDirtyBits PutSyncSettings(object o)
|
||||
=> PutSettingsDirtyBits.None;
|
||||
|
||||
public class TIC80Settings
|
||||
{
|
||||
[DisplayName("Crop")]
|
||||
[Description("")]
|
||||
[DefaultValue(false)]
|
||||
public bool Crop { get; set; }
|
||||
|
||||
public TIC80Settings()
|
||||
=> SettingsUtil.SetDefaultValues(this);
|
||||
|
||||
public TIC80Settings Clone()
|
||||
=> (TIC80Settings)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.TIC80
|
||||
{
|
||||
[PortedCore(CoreNames.TIC80, "nesbox", "v1.0.2164", "https://tic80.com/", isReleased: false)]
|
||||
[ServiceNotApplicable(new[] { typeof(IDriveLight), })]
|
||||
public partial class TIC80 : WaterboxCore
|
||||
{
|
||||
private readonly LibTIC80 _core;
|
||||
|
||||
[CoreConstructor(VSystemID.Raw.TIC80)]
|
||||
public TIC80(CoreLoadParameters<TIC80Settings, object> lp)
|
||||
: base(lp.Comm, new Configuration
|
||||
{
|
||||
DefaultWidth = 240,
|
||||
DefaultHeight = 136,
|
||||
MaxWidth = 256,
|
||||
MaxHeight = 144,
|
||||
MaxSamples = 1024,
|
||||
DefaultFpsNumerator = 60,
|
||||
DefaultFpsDenominator = 1,
|
||||
SystemId = VSystemID.Raw.TIC80,
|
||||
})
|
||||
{
|
||||
_settings = lp.Settings ?? new();
|
||||
|
||||
if (!_settings.Crop)
|
||||
{
|
||||
BufferWidth = 256;
|
||||
BufferHeight = 144;
|
||||
}
|
||||
|
||||
_core = PreInit<LibTIC80>(new WaterboxOptions
|
||||
{
|
||||
Filename = "tic80.wbx",
|
||||
SbrkHeapSizeKB = 2 * 1024,
|
||||
SealedHeapSizeKB = 4,
|
||||
InvisibleHeapSizeKB = 4,
|
||||
PlainHeapSizeKB = 4,
|
||||
MmapHeapSizeKB = 64 * 1024,
|
||||
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
|
||||
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
|
||||
});
|
||||
|
||||
var rom = lp.Roms[0].FileData;
|
||||
|
||||
if (!_core.Init(rom, rom.Length))
|
||||
{
|
||||
throw new InvalidOperationException("Init returned false!");
|
||||
}
|
||||
|
||||
PostInit();
|
||||
}
|
||||
|
||||
private static readonly List<KeyValuePair<string, LibTIC80.TI80Keys>> KeyMap = new();
|
||||
|
||||
public override ControllerDefinition ControllerDefinition => TI80Controller;
|
||||
|
||||
private static readonly ControllerDefinition TI80Controller = CreateControllerDefinition();
|
||||
|
||||
private static ControllerDefinition CreateControllerDefinition()
|
||||
{
|
||||
var ret = new ControllerDefinition("TIC-80 Controller");
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
foreach (var b in Enum.GetValues(typeof(LibTIC80.TI80Gamepad)))
|
||||
{
|
||||
ret.BoolButtons.Add($"P{i + 1} {Enum.GetName(typeof(LibTIC80.TI80Gamepad), b)}");
|
||||
}
|
||||
}
|
||||
|
||||
ret.AddXYPair("Mouse Position {0}", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0);
|
||||
ret.BoolButtons.Add("Mouse Left Click");
|
||||
ret.BoolButtons.Add("Mouse Middle Click");
|
||||
ret.BoolButtons.Add("Mouse Right Click");
|
||||
ret.AddXYPair("Mouse Scroll {0}", AxisPairOrientation.RightAndUp, (-32).RangeTo(31), 0);
|
||||
ret.BoolButtons.Add("Mouse Relative Toggle");
|
||||
|
||||
foreach (var n in ret.BoolButtons)
|
||||
{
|
||||
if (n.StartsWith("Mouse"))
|
||||
{
|
||||
ret.CategoryLabels[n] = "Mouse";
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var n in ret.Axes.Keys)
|
||||
{
|
||||
if (n.StartsWith("Mouse"))
|
||||
{
|
||||
ret.CategoryLabels[n] = "Mouse";
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var k in Enum.GetValues(typeof(LibTIC80.TI80Keys)))
|
||||
{
|
||||
var name = Enum.GetName(typeof(LibTIC80.TI80Keys), k).TrimStart('_').Replace('_', ' ');
|
||||
if (name is "Unknown") continue;
|
||||
KeyMap.Add(new(name, (LibTIC80.TI80Keys)k));
|
||||
ret.BoolButtons.Add(name);
|
||||
ret.CategoryLabels[name] = "Keyboard";
|
||||
}
|
||||
|
||||
return ret.MakeImmutable();
|
||||
}
|
||||
|
||||
private static void GetGamepads(IController controller, LibTIC80.TI80Gamepad[] gamepads)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
gamepads[i] = 0;
|
||||
foreach (var b in Enum.GetValues(typeof(LibTIC80.TI80Gamepad)))
|
||||
{
|
||||
if (controller.IsPressed($"P{i + 1} {Enum.GetName(typeof(LibTIC80.TI80Gamepad), b)}"))
|
||||
{
|
||||
gamepads[i] |= (LibTIC80.TI80Gamepad)b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ushort GetMouseButtons(IController controller)
|
||||
{
|
||||
ushort ret = 0;
|
||||
if (controller.IsPressed("Mouse Left Click"))
|
||||
{
|
||||
ret |= 0x8000;
|
||||
}
|
||||
if (controller.IsPressed("Mouse Middle Click"))
|
||||
{
|
||||
ret |= 0x4000;
|
||||
}
|
||||
if (controller.IsPressed("Mouse Right Click"))
|
||||
{
|
||||
ret |= 0x2000;
|
||||
}
|
||||
var x = (ushort)((sbyte)controller.AxisValue("Mouse Scroll X") + 32);
|
||||
ret |= (ushort)(x << 7);
|
||||
var y = (ushort)((sbyte)controller.AxisValue("Mouse Scroll Y") + 32);
|
||||
ret |= (ushort)(y << 1);
|
||||
if (controller.IsPressed("Mouse Relative Toggle"))
|
||||
{
|
||||
ret |= 0x0001;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void GetKeys(IController controller, LibTIC80.TI80Keys[] keys)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var kvp in KeyMap)
|
||||
{
|
||||
if (controller.IsPressed(kvp.Key))
|
||||
{
|
||||
keys[i++] = kvp.Value;
|
||||
if (i == keys.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
|
||||
{
|
||||
var ret = new LibTIC80.FrameInfo
|
||||
{
|
||||
MouseX = (sbyte)controller.AxisValue("Mouse Position X"),
|
||||
MouseY = (sbyte)controller.AxisValue("Mouse Position Y"),
|
||||
MouseButtons = GetMouseButtons(controller),
|
||||
|
||||
Crop = _settings.Crop,
|
||||
};
|
||||
|
||||
GetGamepads(controller, ret.Gamepads);
|
||||
GetKeys(controller, ret.Keys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,7 +53,8 @@ namespace BizHawk.Emulation.Cores
|
|||
public const string Snes9X = "Snes9x";
|
||||
public const string SubGbHawk = "SubGBHawk";
|
||||
public const string SubNesHawk = "SubNESHawk";
|
||||
public const string TI83Hawk = "TI83Hawk";
|
||||
public const string TI83Hawk = "TI83Hawk";
|
||||
public const string TIC80 = "TIC-80";
|
||||
public const string TST = "T. S. T.";
|
||||
public const string TurboNyma = "TurboNyma";
|
||||
public const string Uzem = "uzem";
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
#include <tic80.h>
|
||||
#include <tic.h>
|
||||
#include <api.h>
|
||||
|
||||
#include <emulibc.h>
|
||||
#include <waterboxcore.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static tic80* tic;
|
||||
|
||||
ECL_EXPORT bool Init(u8* rom, u32 sz)
|
||||
{
|
||||
tic = tic80_create(TIC80_SAMPLERATE, TIC80_PIXEL_COLOR_BGRA8888);
|
||||
if (!tic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tic80_load(tic, rom, sz);
|
||||
return true;
|
||||
}
|
||||
|
||||
ECL_EXPORT void GetMemoryAreas(MemoryArea* m)
|
||||
{
|
||||
tic_mem* mem = (tic_mem*)tic;
|
||||
|
||||
m[0].Data = mem->ram->data;
|
||||
m[0].Name = "RAM";
|
||||
m[0].Size = sizeof(mem->ram->data);
|
||||
m[0].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY;
|
||||
|
||||
m[1].Data = mem->ram->persistent.data;
|
||||
m[1].Name = "SaveRAM";
|
||||
m[1].Size = sizeof(mem->ram->persistent.data);
|
||||
m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_SAVERAMMABLE;
|
||||
|
||||
m[2].Data = mem->ram->vram.data;
|
||||
m[2].Name = "VRAM";
|
||||
m[2].Size = sizeof(mem->ram->vram.data);
|
||||
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FrameInfo b;
|
||||
tic80_input inputs;
|
||||
bool crop;
|
||||
} MyFrameInfo;
|
||||
|
||||
bool lagged;
|
||||
void (*inputcb)() = 0;
|
||||
|
||||
ECL_EXPORT void FrameAdvance(MyFrameInfo* f)
|
||||
{
|
||||
lagged = true;
|
||||
|
||||
tic80_tick(tic, f->inputs);
|
||||
tic80_sound(tic);
|
||||
|
||||
f->b.Samples = tic->samples.count / TIC80_SAMPLE_CHANNELS;
|
||||
memcpy(f->b.SoundBuffer, tic->samples.buffer, tic->samples.count * TIC80_SAMPLESIZE);
|
||||
|
||||
u32* src;
|
||||
u32 width;
|
||||
u32 height;
|
||||
if (f->crop)
|
||||
{
|
||||
src = (u32*)tic->screen + (TIC80_FULLWIDTH * TIC80_OFFSET_TOP) + TIC80_OFFSET_LEFT;
|
||||
width = TIC80_WIDTH;
|
||||
height = TIC80_HEIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = tic->screen;
|
||||
width = TIC80_FULLWIDTH;
|
||||
height = TIC80_FULLHEIGHT;
|
||||
}
|
||||
|
||||
u32* dst = f->b.VideoBuffer;
|
||||
for (u32 i = 0; i < height; i++)
|
||||
{
|
||||
memcpy(dst, src, width * sizeof(u32));
|
||||
dst += width;
|
||||
src += TIC80_FULLWIDTH;
|
||||
}
|
||||
|
||||
f->b.Width = width;
|
||||
f->b.Height = height;
|
||||
|
||||
f->b.Lagged = lagged;
|
||||
}
|
||||
|
||||
ECL_EXPORT void SetInputCallback(void (*callback)())
|
||||
{
|
||||
inputcb = callback;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Vadim Grigoruk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,17 @@
|
|||
INCLUDE_FLAGS = -I./include/ -I./src/ -I./src/core/ -I./vendor/blip-buf/ -I./vendor/duktape/src/ \
|
||||
-I./vendor/fennel/ -I./vendor/giflib/ -I./vendor/lpeg/ -I./vendor/lua/ -I./vendor/moonscript/ \
|
||||
-I./vendor/squirrel/include/ -I./vendor/squirrel/sqstdlib/ -I./vendor/squirrel/squirrel/ \
|
||||
-I./vendor/wasm3/source/ -I./vendor/wren/src/include/ -I./vendor/wren/src/optional/ \
|
||||
-I./vendor/wren/src/vm/ -I./vendor/wren/src/vm/ -I./vendor/zlib/
|
||||
|
||||
CCFLAGS := -std=gnu11 -fno-strict-aliasing -fwrapv -DLUA_COMPAT_5_2 -DBUILD_DEPRECATED $(INCLUDE_FLAGS) \
|
||||
-Werror=int-to-pointer-cast -Wno-missing-braces -Wno-unused-variable \
|
||||
-Wno-pointer-sign -Wno-unused-function -Wno-format
|
||||
|
||||
CXXFLAGS := -std=c++11 $(INCLUDE_FLAGS) -Wno-class-memaccess -fno-strict-aliasing -fwrapv
|
||||
|
||||
TARGET = tic80.wbx
|
||||
|
||||
SRCS = $(shell find ./ -type f -name '*.c') $(shell find ./ -type f -name '*.cpp')
|
||||
|
||||
include ../common.mak
|
|
@ -0,0 +1,166 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic80_config.h"
|
||||
#include "tic80_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TIC80_WIDTH 240
|
||||
#define TIC80_HEIGHT 136
|
||||
#define TIC80_FULLWIDTH_BITS 8
|
||||
#define TIC80_FULLWIDTH (1 << TIC80_FULLWIDTH_BITS)
|
||||
#define TIC80_FULLHEIGHT (TIC80_FULLWIDTH*9/16)
|
||||
|
||||
#define TIC80_MARGIN_TOP ((TIC80_FULLHEIGHT - TIC80_HEIGHT) / 2)
|
||||
#define TIC80_MARGIN_BOTTOM TIC80_MARGIN_TOP
|
||||
#define TIC80_MARGIN_LEFT ((TIC80_FULLWIDTH - TIC80_WIDTH) / 2)
|
||||
#define TIC80_MARGIN_RIGHT TIC80_MARGIN_LEFT
|
||||
|
||||
#define TIC80_KEY_BUFFER 4
|
||||
#define TIC80_SAMPLERATE 44100
|
||||
#define TIC80_SAMPLETYPE s16
|
||||
#define TIC80_SAMPLESIZE sizeof(TIC80_SAMPLETYPE)
|
||||
#define TIC80_SAMPLE_CHANNELS 2
|
||||
#define TIC80_FRAMERATE 60
|
||||
|
||||
typedef enum {
|
||||
TIC80_PIXEL_COLOR_ARGB8888 = (1 << 8) | 32,
|
||||
TIC80_PIXEL_COLOR_ABGR8888 = (2 << 8) | 32,
|
||||
TIC80_PIXEL_COLOR_RGBA8888 = (3 << 8) | 32,
|
||||
TIC80_PIXEL_COLOR_BGRA8888 = (4 << 8) | 32
|
||||
} tic80_pixel_color_format;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
void (*trace)(const char* text, u8 color);
|
||||
void (*error)(const char* info);
|
||||
void (*exit)();
|
||||
} callback;
|
||||
|
||||
struct
|
||||
{
|
||||
TIC80_SAMPLETYPE* buffer;
|
||||
s32 count;
|
||||
} samples;
|
||||
|
||||
u32 *screen;
|
||||
} tic80;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool up:1;
|
||||
bool down:1;
|
||||
bool left:1;
|
||||
bool right:1;
|
||||
bool a:1;
|
||||
bool b:1;
|
||||
bool x:1;
|
||||
bool y:1;
|
||||
};
|
||||
|
||||
u8 data;
|
||||
} tic80_gamepad;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic80_gamepad first;
|
||||
tic80_gamepad second;
|
||||
tic80_gamepad third;
|
||||
tic80_gamepad fourth;
|
||||
};
|
||||
|
||||
u32 data;
|
||||
} tic80_gamepads;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
// absolute pos
|
||||
struct
|
||||
{
|
||||
u8 x;
|
||||
u8 y;
|
||||
};
|
||||
|
||||
// releative values
|
||||
struct
|
||||
{
|
||||
s8 rx;
|
||||
s8 ry;
|
||||
};
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u16 left:1;
|
||||
u16 middle:1;
|
||||
u16 right:1;
|
||||
|
||||
s16 scrollx:6;
|
||||
s16 scrolly:6;
|
||||
|
||||
u16 relative:1;
|
||||
};
|
||||
|
||||
u16 btns;
|
||||
};
|
||||
} tic80_mouse;
|
||||
|
||||
typedef u8 tic_key;
|
||||
|
||||
typedef union
|
||||
{
|
||||
tic_key keys[TIC80_KEY_BUFFER];
|
||||
u32 data;
|
||||
} tic80_keyboard;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic80_gamepads gamepads;
|
||||
tic80_mouse mouse;
|
||||
tic80_keyboard keyboard;
|
||||
|
||||
} tic80_input;
|
||||
|
||||
TIC80_API tic80* tic80_create(s32 samplerate, tic80_pixel_color_format format);
|
||||
TIC80_API void tic80_load(tic80* tic, void* cart, s32 size);
|
||||
TIC80_API void tic80_tick(tic80* tic, tic80_input input);
|
||||
TIC80_API void tic80_sound(tic80* tic);
|
||||
TIC80_API void tic80_delete(tic80* tic);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,83 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(TIC_BUILD_WITH_LUA) && \
|
||||
!defined(TIC_BUILD_WITH_MOON) && \
|
||||
!defined(TIC_BUILD_WITH_FENNEL) && \
|
||||
!defined(TIC_BUILD_WITH_JS) && \
|
||||
!defined(TIC_BUILD_WITH_WREN) && \
|
||||
!defined(TIC_BUILD_WITH_SQUIRREL) && \
|
||||
!defined(TIC_BUILD_WITH_WASM)
|
||||
|
||||
# define TIC_BUILD_WITH_LUA 1
|
||||
# define TIC_BUILD_WITH_MOON 1
|
||||
# define TIC_BUILD_WITH_FENNEL 1
|
||||
# define TIC_BUILD_WITH_JS 1
|
||||
# define TIC_BUILD_WITH_WREN 1
|
||||
# define TIC_BUILD_WITH_SQUIRREL 1
|
||||
# define TIC_BUILD_WITH_WASM 1
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_FENNEL) || defined(TIC_BUILD_WITH_MOON)
|
||||
# define TIC_BUILD_WITH_LUA 1
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// TODO: this disables macos config
|
||||
# include "AvailabilityMacros.h"
|
||||
# include "TargetConditionals.h"
|
||||
// # ifndef TARGET_OS_IPHONE
|
||||
# undef __TIC_MACOSX__
|
||||
# define __TIC_MACOSX__ 1
|
||||
# if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||||
# error SDL for Mac OS X only supports deploying on 10.6 and above.
|
||||
# endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
|
||||
// # endif /* TARGET_OS_IPHONE */
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
|
||||
# undef __TIC_WINDOWS__
|
||||
# define __TIC_WINDOWS__ 1
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
# undef __TIC_ANDROID__
|
||||
# define __TIC_ANDROID__ 1
|
||||
#elif (defined(linux) || defined(__linux) || defined(__linux__))
|
||||
# undef __TIC_LINUX__
|
||||
# define __TIC_LINUX__ 1
|
||||
#endif
|
||||
|
||||
#ifndef TIC80_API
|
||||
# if defined(TIC80_SHARED)
|
||||
# if defined(__TIC_WINDOWS__)
|
||||
# define TIC80_API __declspec(dllexport)
|
||||
# elif defined(__TIC_LINUX__)
|
||||
# define TIC80_API __attribute__ ((visibility("default")))
|
||||
# endif
|
||||
# else
|
||||
# define TIC80_API
|
||||
# endif
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
|
@ -0,0 +1,861 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic.h"
|
||||
#include "time.h"
|
||||
|
||||
// convenience macros to loop languages
|
||||
#define FOR_EACH_LANG(ln) for (tic_script_config** conf = Languages ; *conf != NULL; conf++ ) { tic_script_config* ln = *conf;
|
||||
#define FOR_EACH_LANG_END }
|
||||
|
||||
|
||||
typedef struct { u8 index; tic_flip flip; tic_rotate rotate; } RemapResult;
|
||||
typedef void(*RemapFunc)(void*, s32 x, s32 y, RemapResult* result);
|
||||
|
||||
typedef void(*TraceOutput)(void*, const char*, u8 color);
|
||||
typedef void(*ErrorOutput)(void*, const char*);
|
||||
typedef void(*ExitCallback)(void*);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TraceOutput trace;
|
||||
ErrorOutput error;
|
||||
ExitCallback exit;
|
||||
|
||||
clock_t start;
|
||||
|
||||
void* data;
|
||||
} tic_tick_data;
|
||||
|
||||
typedef struct tic_mem tic_mem;
|
||||
typedef void(*tic_tick)(tic_mem* memory);
|
||||
typedef void(*tic_boot)(tic_mem* memory);
|
||||
typedef void(*tic_scanline)(tic_mem* memory, s32 row, void* data);
|
||||
typedef void(*tic_border)(tic_mem* memory, s32 row, void* data);
|
||||
typedef void(*tic_gamemenu)(tic_mem* memory, s32 index, void* data);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* pos;
|
||||
s32 size;
|
||||
} tic_outline_item;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_scanline scanline;
|
||||
tic_border border;
|
||||
tic_gamemenu menu;
|
||||
void* data;
|
||||
} tic_blit_callback;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 id;
|
||||
const char* name;
|
||||
const char* fileExtension;
|
||||
const char* projectComment;
|
||||
struct
|
||||
{
|
||||
bool(*init)(tic_mem* memory, const char* code);
|
||||
void(*close)(tic_mem* memory);
|
||||
|
||||
tic_tick tick;
|
||||
tic_boot boot;
|
||||
tic_blit_callback callback;
|
||||
};
|
||||
|
||||
const tic_outline_item* (*getOutline)(const char* code, s32* size);
|
||||
void (*eval)(tic_mem* tic, const char* code);
|
||||
|
||||
const char* blockCommentStart;
|
||||
const char* blockCommentEnd;
|
||||
const char* blockCommentStart2;
|
||||
const char* blockCommentEnd2;
|
||||
const char* blockStringStart;
|
||||
const char* blockStringEnd;
|
||||
const char* singleComment;
|
||||
const char* blockEnd;
|
||||
|
||||
const char* const * keywords;
|
||||
s32 keywordsCount;
|
||||
} tic_script_config;
|
||||
|
||||
extern tic_script_config* Languages[];
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_tiles_texture,
|
||||
tic_map_texture,
|
||||
tic_vbank_texture,
|
||||
} tic_texture_src;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 x, y;
|
||||
} tic_point;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 x, y, w, h;
|
||||
} tic_rect;
|
||||
|
||||
// SYNC DEFINITION TABLE
|
||||
// .--------------------------------- - - -
|
||||
// | CART | RAM | INDEX
|
||||
// |---------+---------------+------- - - -
|
||||
// | | |
|
||||
#define TIC_SYNC_LIST(macro) \
|
||||
macro(tiles, tiles, 0) \
|
||||
macro(sprites, sprites, 1) \
|
||||
macro(map, map, 2) \
|
||||
macro(sfx, sfx, 3) \
|
||||
macro(music, music, 4) \
|
||||
macro(palette, vram.palette, 5) \
|
||||
macro(flags, flags, 6) \
|
||||
macro(screen, vram.screen, 7)
|
||||
|
||||
enum
|
||||
{
|
||||
#define TIC_SYNC_DEF(NAME, _, INDEX) tic_sync_##NAME = 1 << INDEX,
|
||||
TIC_SYNC_LIST(TIC_SYNC_DEF)
|
||||
#undef TIC_SYNC_DEF
|
||||
};
|
||||
|
||||
#define TIC_FN "TIC"
|
||||
#define BOOT_FN "BOOT"
|
||||
#define SCN_FN "SCN"
|
||||
#define OVR_FN "OVR" // deprecated since v1.0
|
||||
#define BDR_FN "BDR"
|
||||
#define MENU_FN "MENU"
|
||||
|
||||
#define TIC_CALLBACK_LIST(macro) \
|
||||
macro(TIC, TIC_FN "()", "Main function. It's called at " DEF2STR(TIC80_FRAMERATE) \
|
||||
"fps (" DEF2STR(TIC80_FRAMERATE) " times every second).") \
|
||||
macro(BOOT, BOOT_FN, "Startup function.") \
|
||||
macro(MENU, MENU_FN "(index)", "Game Menu handler.") \
|
||||
macro(SCN, SCN_FN "(row)", "Allows you to execute code between the drawing of each scanline, " \
|
||||
"for example, to manipulate the palette.") \
|
||||
macro(BDR, BDR_FN "(row)", "Allows you to execute code between the drawing of each fullscreen scanline, " \
|
||||
"for example, to manipulate the palette.")
|
||||
|
||||
// API DEFINITION TABLE
|
||||
// macro
|
||||
// (
|
||||
// definition
|
||||
// help
|
||||
// parameters count
|
||||
// required parameters count
|
||||
// callback?
|
||||
// return type
|
||||
// function parameters
|
||||
// )
|
||||
|
||||
#define TIC_API_LIST(macro) \
|
||||
macro(print, \
|
||||
"print(text x=0 y=0 color=15 fixed=false scale=1 smallfont=false) -> width", \
|
||||
\
|
||||
"This will simply print text to the screen using the font defined in config.\n" \
|
||||
"When set to true, the fixed width option ensures that each character " \
|
||||
"will be printed in a `box` of the same size, " \
|
||||
"so the character `i` will occupy the same width as the character `w` for example.\n" \
|
||||
"When fixed width is false, there will be a single space between each character.\n" \
|
||||
"\nTips:\n" \
|
||||
"- To use a custom rastered font, check out `font()`.\n" \
|
||||
"- To print to the console, check out `trace()`.", \
|
||||
7, \
|
||||
1, \
|
||||
0, \
|
||||
s32, \
|
||||
tic_mem*, const char* text, s32 x, s32 y, u8 color, bool fixed, s32 scale, bool alt) \
|
||||
\
|
||||
\
|
||||
macro(cls, \
|
||||
"cls(color=0)", \
|
||||
\
|
||||
"Clear the screen.\n" \
|
||||
"When called this function clear all the screen using the color passed as argument.\n" \
|
||||
"If no parameter is passed first color (0) is used.", \
|
||||
1, \
|
||||
0, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(pix, \
|
||||
"pix(x y color)\npix(x y) -> color", \
|
||||
\
|
||||
"This function can read or write pixel color values.\n" \
|
||||
"When called with a color parameter, the pixel at the specified coordinates is set to that color.\n" \
|
||||
"Calling the function without a color parameter returns the color of the pixel at the specified position.", \
|
||||
3, \
|
||||
2, \
|
||||
0, \
|
||||
u8, \
|
||||
tic_mem*, s32 x, s32 y, u8 color, bool get) \
|
||||
\
|
||||
\
|
||||
macro(line, \
|
||||
"line(x0 y0 x1 y1 color)", \
|
||||
\
|
||||
"Draws a straight line from point (x0,y0) to point (x1,y1) in the specified color.", \
|
||||
5, \
|
||||
5, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, float x1, float y1, float x2, float y2, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(rect, \
|
||||
"rect(x y w h color)", \
|
||||
\
|
||||
"This function draws a filled rectangle of the desired size and color at the specified position.\n" \
|
||||
"If you only need to draw the the border or outline of a rectangle (ie not filled) see `rectb()`.", \
|
||||
5, \
|
||||
5, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 width, s32 height, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(rectb, \
|
||||
"rectb(x y w h color)", \
|
||||
\
|
||||
"This function draws a one pixel thick rectangle border at the position requested.\n" \
|
||||
"If you need to fill the rectangle with a color, see `rect()` instead.", \
|
||||
5, \
|
||||
5, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 width, s32 height, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(spr, \
|
||||
"spr(id x y colorkey=-1 scale=1 flip=0 rotate=0 w=1 h=1)", \
|
||||
\
|
||||
"Draws the sprite number index at the x and y coordinate.\n" \
|
||||
"You can specify a colorkey in the palette which will be used as the transparent color " \
|
||||
"or use a value of -1 for an opaque sprite.\n" \
|
||||
"The sprite can be scaled up by a desired factor. For example, " \
|
||||
"a scale factor of 2 means an 8x8 pixel sprite is drawn to a 16x16 area of the screen.\n" \
|
||||
"You can flip the sprite where:\n" \
|
||||
"- 0 = No Flip\n" \
|
||||
"- 1 = Flip horizontally\n" \
|
||||
"- 2 = Flip vertically\n" \
|
||||
"- 3 = Flip both vertically and horizontally\n" \
|
||||
"When you rotate the sprite, it's rotated clockwise in 90 steps:\n" \
|
||||
"- 0 = No rotation\n" \
|
||||
"- 1 = 90 rotation\n" \
|
||||
"- 2 = 180 rotation\n" \
|
||||
"- 3 = 270 rotation\n" \
|
||||
"You can draw a composite sprite (consisting of a rectangular region of sprites from the sprite sheet) " \
|
||||
"by specifying the `w` and `h` parameters (which default to 1).", \
|
||||
9, \
|
||||
3, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 index, s32 x, s32 y, s32 w, s32 h, \
|
||||
u8* trans_colors, u8 trans_count, s32 scale, tic_flip flip, tic_rotate rotate) \
|
||||
\
|
||||
\
|
||||
macro(btn, \
|
||||
"btn(id) -> pressed", \
|
||||
\
|
||||
"This function allows you to read the status of one of the buttons attached to TIC.\n" \
|
||||
"The function returns true if the key with the supplied id is currently in the pressed state.\n" \
|
||||
"It remains true for as long as the key is held down.\n" \
|
||||
"If you want to test if a key was just pressed, use `btnp()` instead.", \
|
||||
1, \
|
||||
1, \
|
||||
0, \
|
||||
u32, \
|
||||
tic_mem*, s32 id) \
|
||||
\
|
||||
\
|
||||
macro(btnp, \
|
||||
"btnp(id hold=-1 period=-1) -> pressed", \
|
||||
\
|
||||
"This function allows you to read the status of one of TIC's buttons.\n" \
|
||||
"It returns true only if the key has been pressed since the last frame.\n" \
|
||||
"You can also use the optional hold and period parameters " \
|
||||
"which allow you to check if a button is being held down.\n" \
|
||||
"After the time specified by hold has elapsed, " \
|
||||
"btnp will return true each time period is passed if the key is still down.\n" \
|
||||
"For example, to re-examine the state of button `0` after 2 seconds " \
|
||||
"and continue to check its state every 1/10th of a second, you would use btnp(0, 120, 6).\n" \
|
||||
"Since time is expressed in ticks and TIC runs at 60 frames per second, " \
|
||||
"we use the value of 120 to wait 2 seconds and 6 ticks (ie 60/10) as the interval for re-checking.", \
|
||||
3, \
|
||||
1, \
|
||||
0, \
|
||||
u32, \
|
||||
tic_mem*, s32 id, s32 hold, s32 period) \
|
||||
\
|
||||
\
|
||||
macro(sfx, \
|
||||
"sfx(id note=-1 duration=-1 channel=0 volume=15 speed=0)", \
|
||||
\
|
||||
"This function will play the sound with `id` created in the sfx editor.\n" \
|
||||
"Calling the function with id set to -1 will stop playing the channel.\n" \
|
||||
"The note can be supplied as an integer between 0 and 95 (representing 8 octaves of 12 notes each) " \
|
||||
"or as a string giving the note name and octave.\n" \
|
||||
"For example, a note value of `14` will play the note `D` in the second octave.\n" \
|
||||
"The same note could be specified by the string `D-2`.\n" \
|
||||
"Note names consist of two characters, " \
|
||||
"the note itself (in upper case) followed by `-` to represent the natural note or `#` to represent a sharp.\n" \
|
||||
"There is no option to indicate flat values.\n" \
|
||||
"The available note names are therefore: C-, C#, D-, D#, E-, F-, F#, G-, G#, A-, A#, B-.\n" \
|
||||
"The `octave` is specified using a single digit in the range 0 to 8.\n" \
|
||||
"The `duration` specifies how many ticks to play the sound for since TIC-80 runs at 60 frames per second, " \
|
||||
"a value of 30 represents half a second.\n" \
|
||||
"A value of -1 will play the sound continuously.\n" \
|
||||
"The `channel` parameter indicates which of the four channels to use. Allowed values are 0 to 3.\n" \
|
||||
"The `volume` can be between 0 and 15.\n" \
|
||||
"The `speed` in the range -4 to 3 can be specified and means how many `ticks+1` to play each step, " \
|
||||
"so speed==0 means 1 tick per step.", \
|
||||
6, \
|
||||
1, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 index, s32 note, s32 octave, \
|
||||
s32 duration, s32 channel, s32 left, s32 right, s32 speed) \
|
||||
\
|
||||
\
|
||||
macro(map, \
|
||||
"map(x=0 y=0 w=30 h=17 sx=0 sy=0 colorkey=-1 scale=1 remap=nil)", \
|
||||
\
|
||||
"The map consists of cells of 8x8 pixels, each of which can be filled with a sprite using the map editor.\n" \
|
||||
"The map can be up to 240 cells wide by 136 deep.\n" \
|
||||
"This function will draw the desired area of the map to a specified screen position.\n" \
|
||||
"For example, map(5,5,12,10,0,0) will draw a 12x10 section of the map, " \
|
||||
"starting from map co-ordinates (5,5) to screen position (0,0).\n" \
|
||||
"The map function's last parameter is a powerful callback function " \
|
||||
"for changing how map cells (sprites) are drawn when map is called.\n" \
|
||||
"It can be used to rotate, flip and replace sprites while the game is running.\n" \
|
||||
"Unlike mset, which saves changes to the map, this special function can be used to create " \
|
||||
"animated tiles or replace them completely.\n" \
|
||||
"Some examples include changing sprites to open doorways, " \
|
||||
"hiding sprites used to spawn objects in your game and even to emit the objects themselves.\n" \
|
||||
"The tilemap is laid out sequentially in RAM - writing 1 to 0x08000 " \
|
||||
"will cause tile(sprite) #1 to appear at top left when map() is called.\n" \
|
||||
"To set the tile immediately below this we need to write to 0x08000 + 240, ie 0x080F0.", \
|
||||
9, \
|
||||
0, \
|
||||
1, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, \
|
||||
u8* trans_colors, u8 trans_count, s32 scale, RemapFunc remap, void* data) \
|
||||
\
|
||||
\
|
||||
macro(mget, \
|
||||
"mget(x y) -> tile_id", \
|
||||
\
|
||||
"Gets the sprite id at the given x and y map coordinate.", \
|
||||
2, \
|
||||
2, \
|
||||
0, \
|
||||
u8, \
|
||||
tic_mem*, s32 x, s32 y) \
|
||||
\
|
||||
\
|
||||
macro(mset, \
|
||||
"mset(x y tile_id)", \
|
||||
\
|
||||
"This function will change the tile at the specified map coordinates.\n" \
|
||||
"By default, changes made are only kept while the current game is running.\n" \
|
||||
"To make permanent changes to the map, see `sync()`.\n" \
|
||||
"Related: `map()` `mget()` `sync()`.", \
|
||||
3, \
|
||||
3, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, u8 value) \
|
||||
\
|
||||
\
|
||||
macro(peek, \
|
||||
"peek(addr bits=8) -> value", \
|
||||
\
|
||||
"This function allows to read the memory from TIC.\n" \
|
||||
"It's useful to access resources created with the integrated tools like sprite, maps, sounds, " \
|
||||
"cartridges data?\n" \
|
||||
"Never dream to sound a sprite?\n" \
|
||||
"Address are in hexadecimal format but values are decimal.\n" \
|
||||
"To write to a memory address, use `poke()`.\n" \
|
||||
"`bits` allowed to be 1,2,4,8.", \
|
||||
2, \
|
||||
1, \
|
||||
0, \
|
||||
u8, \
|
||||
tic_mem*, s32 address, s32 bits) \
|
||||
\
|
||||
\
|
||||
macro(poke, \
|
||||
"poke(addr value bits=8)", \
|
||||
\
|
||||
"This function allows you to write a single byte to any address in TIC's RAM.\n" \
|
||||
"The address should be specified in hexadecimal format, the value in decimal.\n" \
|
||||
"`bits` allowed to be 1,2,4,8.", \
|
||||
3, \
|
||||
2, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 address, u8 value, s32 bits) \
|
||||
\
|
||||
\
|
||||
macro(peek1, \
|
||||
"peek1(addr) -> value", \
|
||||
\
|
||||
"This function enables you to read single bit values from TIC's RAM.\n" \
|
||||
"The address is often specified in hexadecimal format.", \
|
||||
1, \
|
||||
1, \
|
||||
0, \
|
||||
u8, \
|
||||
tic_mem*, s32 address) \
|
||||
\
|
||||
\
|
||||
macro(poke1, \
|
||||
"poke1(addr value)", \
|
||||
\
|
||||
"This function allows you to write single bit values directly to RAM.\n" \
|
||||
"The address is often specified in hexadecimal format.", \
|
||||
2, \
|
||||
2, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 address, u8 value) \
|
||||
\
|
||||
\
|
||||
macro(peek2, \
|
||||
"peek2(addr) -> value", \
|
||||
\
|
||||
"This function enables you to read two bits values from TIC's RAM.\n" \
|
||||
"The address is often specified in hexadecimal format.", \
|
||||
1, \
|
||||
1, \
|
||||
0, \
|
||||
u8, \
|
||||
tic_mem*, s32 address) \
|
||||
\
|
||||
\
|
||||
macro(poke2, \
|
||||
"poke2(addr value)", \
|
||||
\
|
||||
"This function allows you to write two bits values directly to RAM.\n" \
|
||||
"The address is often specified in hexadecimal format.", \
|
||||
2, \
|
||||
2, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 address, u8 value) \
|
||||
\
|
||||
\
|
||||
macro(peek4, \
|
||||
"peek4(addr) -> value", \
|
||||
\
|
||||
"This function enables you to read values from TIC's RAM.\n" \
|
||||
"The address is often specified in hexadecimal format.\n" \
|
||||
"See 'poke4()' for detailed information on how nibble addressing compares with byte addressing.", \
|
||||
1, \
|
||||
1, \
|
||||
0, \
|
||||
u8, \
|
||||
tic_mem*, s32 address) \
|
||||
\
|
||||
\
|
||||
macro(poke4, \
|
||||
"poke4(addr value)", \
|
||||
\
|
||||
"This function allows you to write directly to RAM.\n" \
|
||||
"The address is often specified in hexadecimal format.\n" \
|
||||
"For both peek4 and poke4 RAM is addressed in 4 bit segments (nibbles).\n" \
|
||||
"Therefore, to access the the RAM at byte address 0x4000\n" \
|
||||
"you would need to access both the 0x8000 and 0x8001 nibble addresses.", \
|
||||
2, \
|
||||
2, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 address, u8 value) \
|
||||
\
|
||||
\
|
||||
macro(memcpy, \
|
||||
"memcpy(dest source size)", \
|
||||
\
|
||||
"This function allows you to copy a continuous block of TIC's 96K RAM from one address to another.\n" \
|
||||
"Addresses are specified are in hexadecimal format, values are decimal.", \
|
||||
3, \
|
||||
3, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 dst, s32 src, s32 size) \
|
||||
\
|
||||
\
|
||||
macro(memset, \
|
||||
"memset(dest value size)", \
|
||||
\
|
||||
"This function allows you to set a continuous block of any part of TIC's RAM to the same value.\n" \
|
||||
"The address is specified in hexadecimal format, the value in decimal.", \
|
||||
3, \
|
||||
3, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 dst, u8 val, s32 size) \
|
||||
\
|
||||
\
|
||||
macro(trace, \
|
||||
"trace(message color=15)", \
|
||||
\
|
||||
"This is a service function, useful for debugging your code.\n" \
|
||||
"It prints the message parameter to the console in the (optional) color specified.\n" \
|
||||
"\nTips:\n" \
|
||||
"- The Lua concatenator for strings is .. (two points).\n" \
|
||||
"- Use console cls command to clear the output from trace.", \
|
||||
2, \
|
||||
1, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, const char* text, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(pmem, \
|
||||
"pmem(index value)\npmem(index) -> value", \
|
||||
\
|
||||
"This function allows you to save and retrieve data in one of the 256 individual 32-bit slots " \
|
||||
"available in the cartridge's persistent memory.\n" \
|
||||
"This is useful for saving high-scores, level advancement or achievements.\n" \
|
||||
"The data is stored as unsigned 32-bit integers (from 0 to 4294967295).\n" \
|
||||
"\nTips:\n" \
|
||||
"- pmem depends on the cartridge hash (md5), so don't change your lua script if you want to keep the data.\n" \
|
||||
"- Use `saveid:` with a personalized string in the header metadata to override the default MD5 calculation.\n" \
|
||||
"This allows the user to update a cart without losing their saved data.", \
|
||||
2, \
|
||||
1, \
|
||||
0, \
|
||||
u32, \
|
||||
tic_mem*, s32 index, u32 value, bool get) \
|
||||
\
|
||||
\
|
||||
macro(time, \
|
||||
"time() -> ticks", \
|
||||
\
|
||||
"This function returns the number of milliseconds elapsed since the cartridge began execution.\n" \
|
||||
"Useful for keeping track of time, animating items and triggering events.", \
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
double, \
|
||||
tic_mem*) \
|
||||
\
|
||||
\
|
||||
macro(tstamp, \
|
||||
"tstamp() -> timestamp", \
|
||||
\
|
||||
"This function returns the number of seconds elapsed since January 1st, 1970.\n" \
|
||||
"Useful for creating persistent games which evolve over time between plays.", \
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
s32, \
|
||||
tic_mem*) \
|
||||
\
|
||||
\
|
||||
macro(exit, \
|
||||
"exit()", \
|
||||
\
|
||||
"Interrupts program execution and returns to the console when the TIC function ends.", \
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*) \
|
||||
\
|
||||
\
|
||||
macro(font, \
|
||||
"font(text x y chromakey char_width char_height fixed=false scale=1) -> width", \
|
||||
\
|
||||
"Print string with font defined in foreground sprites.\n" \
|
||||
"To simply print to the screen, check out `print()`.\n" \
|
||||
"To print to the console, check out `trace()`.", \
|
||||
8, \
|
||||
6, \
|
||||
0, \
|
||||
s32, \
|
||||
tic_mem*, const char* text, s32 x, s32 y, \
|
||||
u8* trans_colors, u8 trans_count, s32 w, s32 h, bool fixed, s32 scale, bool alt) \
|
||||
\
|
||||
\
|
||||
macro(mouse, \
|
||||
"mouse() -> x y left middle right scrollx scrolly", \
|
||||
\
|
||||
"This function returns the mouse coordinates and a boolean value for the state of each mouse button," \
|
||||
"with true indicating that a button is pressed.", \
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
tic_point, \
|
||||
tic_mem*) \
|
||||
\
|
||||
\
|
||||
macro(circ, \
|
||||
"circ(x y radius color)", \
|
||||
\
|
||||
"This function draws a filled circle of the desired radius and color with its center at x, y.\n" \
|
||||
"It uses the Bresenham algorithm.", \
|
||||
4, \
|
||||
4, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 radius, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(circb, \
|
||||
"circb(x y radius color)", \
|
||||
\
|
||||
"Draws the circumference of a circle with its center at x, y using the radius and color requested.\n" \
|
||||
"It uses the Bresenham algorithm.", \
|
||||
4, \
|
||||
4, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 radius, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(elli, \
|
||||
"elli(x y a b color)", \
|
||||
\
|
||||
"This function draws a filled ellipse of the desired a, b radiuses and color with its center at x, y.\n" \
|
||||
"It uses the Bresenham algorithm.", \
|
||||
5, \
|
||||
5, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 a, s32 b, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(ellib, \
|
||||
"ellib(x y a b color)", \
|
||||
\
|
||||
"This function draws an ellipse border with the desired radiuses a b and color with its center at x, y.\n" \
|
||||
"It uses the Bresenham algorithm.", \
|
||||
5, \
|
||||
5, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 a, s32 b, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(tri, \
|
||||
"tri(x1 y1 x2 y2 x3 y3 color)", \
|
||||
\
|
||||
"This function draws a triangle filled with color, using the supplied vertices.", \
|
||||
7, \
|
||||
7, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, float x1, float y1, float x2, float y2, float x3, float y3, u8 color) \
|
||||
\
|
||||
macro(trib, \
|
||||
"trib(x1 y1 x2 y2 x3 y3 color)", \
|
||||
\
|
||||
"This function draws a triangle border with color, using the supplied vertices.", \
|
||||
7, \
|
||||
7, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, float x1, float y1, float x2, float y2, float x3, float y3, u8 color) \
|
||||
\
|
||||
\
|
||||
macro(ttri, \
|
||||
"ttri(x1 y1 x2 y2 x3 y3 u1 v1 u2 v2 u3 v3 texsrc=0 chromakey=-1 z1=0 z2=0 z3=0)", \
|
||||
\
|
||||
"It renders a triangle filled with texture from image ram, map ram or vbank.\n" \
|
||||
"Use in 3D graphics.\n" \
|
||||
"In particular, if the vertices in the triangle have different 3D depth, you may see some distortion.\n" \
|
||||
"These can be thought of as the window inside image ram (sprite sheet), map ram or another vbank.\n" \
|
||||
"Note that the sprite sheet or map in this case is treated as a single large image, " \
|
||||
"with U and V addressing its pixels directly, rather than by sprite ID.\n" \
|
||||
"So for example the top left corner of sprite #2 would be located at u=16, v=0.", \
|
||||
17, \
|
||||
12, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, float x1, float y1, float x2, float y2, float x3, float y3, \
|
||||
float u1, float v1, float u2, float v2, float u3, float v3, tic_texture_src texsrc, u8* colors, s32 count, \
|
||||
float z1, float z2, float z3, bool depth) \
|
||||
\
|
||||
\
|
||||
macro(clip, \
|
||||
"clip(x y width height)\nclip()", \
|
||||
\
|
||||
"This function limits drawing to a clipping region or `viewport` defined by x,y,w,h.\n" \
|
||||
"Things drawn outside of this area will not be visible.\n" \
|
||||
"Calling clip() with no parameters will reset the drawing area to the entire screen.", \
|
||||
4, \
|
||||
4, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 x, s32 y, s32 width, s32 height) \
|
||||
\
|
||||
\
|
||||
macro(music, \
|
||||
"music(track=-1 frame=-1 row=-1 loop=true sustain=false tempo=-1 speed=-1)", \
|
||||
\
|
||||
"This function starts playing a track created in the Music Editor.\n" \
|
||||
"Call without arguments to stop the music.", \
|
||||
7, \
|
||||
0, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 track, s32 frame, s32 row, bool loop, bool sustain, s32 tempo, s32 speed) \
|
||||
\
|
||||
\
|
||||
macro(sync, \
|
||||
"sync(mask=0 bank=0 tocart=false)", \
|
||||
\
|
||||
"The pro version of TIC-80 contains 8 memory banks.\n" \
|
||||
"To switch between these banks, sync can be used to either load contents from a memory bank to runtime, " \
|
||||
"or save contents from the active runtime to a bank.\n" \
|
||||
"The function can only be called once per frame." \
|
||||
"If you have manipulated the runtime memory (e.g. by using mset), " \
|
||||
"you can reset the active state by calling sync(0,0,false).\n" \
|
||||
"This resets the whole runtime memory to the contents of bank 0." \
|
||||
"Note that sync is not used to load code from banks; this is done automatically.", \
|
||||
3, \
|
||||
0, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, u32 mask, s32 bank, bool toCart) \
|
||||
\
|
||||
\
|
||||
macro(vbank, \
|
||||
"vbank(bank) -> prev\nvbank() -> prev", \
|
||||
\
|
||||
"VRAM contains 2x16K memory chips, use vbank(0) or vbank(1) to switch between them.", \
|
||||
1, \
|
||||
1, \
|
||||
0, \
|
||||
s32, \
|
||||
tic_mem*, s32 bank) \
|
||||
\
|
||||
\
|
||||
macro(reset, \
|
||||
"reset()", \
|
||||
\
|
||||
"Resets the cartridge. To return to the console, see the `exit()`.", \
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*) \
|
||||
\
|
||||
\
|
||||
macro(key, \
|
||||
"key(code=-1) -> pressed", \
|
||||
\
|
||||
"The function returns true if the key denoted by keycode is pressed.", \
|
||||
1, \
|
||||
0, \
|
||||
0, \
|
||||
bool, \
|
||||
tic_mem*, tic_key key) \
|
||||
\
|
||||
\
|
||||
macro(keyp, \
|
||||
"keyp(code=-1 hold=-1 period=-1) -> pressed", \
|
||||
\
|
||||
"This function returns true if the given key is pressed but wasn't pressed in the previous frame.\n" \
|
||||
"Refer to `btnp()` for an explanation of the optional hold and period parameters.", \
|
||||
3, \
|
||||
0, \
|
||||
0, \
|
||||
bool, \
|
||||
tic_mem*, tic_key key, s32 hold, s32 period) \
|
||||
\
|
||||
\
|
||||
macro(fget, \
|
||||
"fget(sprite_id flag) -> bool", \
|
||||
\
|
||||
"Returns true if the specified flag of the sprite is set. See `fset()` for more details.", \
|
||||
2, \
|
||||
2, \
|
||||
0, \
|
||||
bool, \
|
||||
tic_mem*, s32 index, u8 flag) \
|
||||
\
|
||||
\
|
||||
macro(fset, \
|
||||
"fset(sprite_id flag bool)", \
|
||||
\
|
||||
"Each sprite has eight flags which can be used to store information or signal different conditions.\n" \
|
||||
"For example, flag 0 might be used to indicate that the sprite is invisible, " \
|
||||
"flag 6 might indicate that the flag should be draw scaled etc.\n" \
|
||||
"See algo `fget()`.", \
|
||||
3, \
|
||||
3, \
|
||||
0, \
|
||||
void, \
|
||||
tic_mem*, s32 index, u8 flag, bool value)
|
||||
|
||||
#define TIC_API_DEF(name, _, __, ___, ____, _____, ret, ...) ret tic_api_##name(__VA_ARGS__);
|
||||
TIC_API_LIST(TIC_API_DEF)
|
||||
#undef TIC_API_DEF
|
||||
|
||||
struct tic_mem
|
||||
{
|
||||
tic80 product;
|
||||
tic_ram* ram;
|
||||
tic_cartridge cart;
|
||||
|
||||
tic_ram* base_ram;
|
||||
|
||||
char saveid[TIC_SAVEID_SIZE];
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 gamepad:1;
|
||||
u8 mouse:1;
|
||||
u8 keyboard:1;
|
||||
};
|
||||
|
||||
u8 data;
|
||||
} input;
|
||||
};
|
||||
|
||||
tic_mem* tic_core_create(s32 samplerate, tic80_pixel_color_format format);
|
||||
void tic_core_close(tic_mem* memory);
|
||||
void tic_core_pause(tic_mem* memory);
|
||||
void tic_core_resume(tic_mem* memory);
|
||||
void tic_core_tick_start(tic_mem* memory);
|
||||
void tic_core_tick(tic_mem* memory, tic_tick_data* data);
|
||||
void tic_core_tick_end(tic_mem* memory);
|
||||
void tic_core_synth_sound(tic_mem* tic);
|
||||
void tic_core_blit(tic_mem* tic);
|
||||
void tic_core_blit_ex(tic_mem* tic, tic_blit_callback clb);
|
||||
const tic_script_config* tic_core_script_config(tic_mem* memory);
|
||||
|
||||
#define VBANK(tic, bank) \
|
||||
bool MACROVAR(_bank_) = tic_api_vbank(tic, bank); \
|
||||
SCOPE(tic_api_vbank(tic, MACROVAR(_bank_)))
|
|
@ -0,0 +1,215 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "core/core.h"
|
||||
|
||||
// Fennel requires Lua
|
||||
#if defined(TIC_BUILD_WITH_LUA)
|
||||
|
||||
#include "lua_api.h"
|
||||
|
||||
#if defined(TIC_BUILD_WITH_FENNEL)
|
||||
|
||||
#include "fennel.h"
|
||||
|
||||
#define FENNEL_CODE(...) #__VA_ARGS__
|
||||
|
||||
static const char* execute_fennel_src = FENNEL_CODE(
|
||||
local fennel = require("fennel")
|
||||
debug.traceback = fennel.traceback
|
||||
local opts = {filename="game", allowedGlobals = false}
|
||||
local src = ...
|
||||
if(src:find("\n;; strict: true")) then opts.allowedGlobals = nil end
|
||||
local ok, msg = pcall(fennel.eval, src, opts)
|
||||
if(not ok) then return msg end
|
||||
);
|
||||
|
||||
static bool initFennel(tic_mem* tic, const char* code)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
closeLua(tic);
|
||||
|
||||
lua_State* lua = core->currentVM = luaL_newstate();
|
||||
lua_open_builtins(lua);
|
||||
|
||||
initLuaAPI(core);
|
||||
|
||||
{
|
||||
lua_State* fennel = core->currentVM;
|
||||
|
||||
lua_settop(fennel, 0);
|
||||
|
||||
if (luaL_loadbuffer(fennel, (const char *)loadfennel_lua,
|
||||
loadfennel_lua_len, "fennel.lua") != LUA_OK)
|
||||
{
|
||||
core->data->error(core->data->data, "failed to load fennel compiler");
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_call(fennel, 0, 0);
|
||||
|
||||
if (luaL_loadbuffer(fennel, execute_fennel_src, strlen(execute_fennel_src), "execute_fennel") != LUA_OK)
|
||||
{
|
||||
core->data->error(core->data->data, "failed to load fennel compiler");
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pushstring(fennel, code);
|
||||
lua_call(fennel, 1, 1);
|
||||
const char* err = lua_tostring(fennel, -1);
|
||||
|
||||
if (err)
|
||||
{
|
||||
core->data->error(core->data->data, err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* const FennelKeywords [] =
|
||||
{
|
||||
"lua", "hashfn","macro", "macros", "macroexpand", "macrodebug",
|
||||
"do", "values", "if", "when", "each", "for", "fn", "lambda", "partial",
|
||||
"while", "set", "global", "var", "local", "let", "tset", "doto", "match",
|
||||
"or", "and", "true", "false", "nil", "not", "not=", "length", "set-forcibly!",
|
||||
"rshift", "lshift", "bor", "band", "bnot", "bxor", "pick-values", "pick-args",
|
||||
".", "..", "#", "...", ":", "->", "->>", "-?>", "-?>>", "$", "with-open"
|
||||
};
|
||||
|
||||
static const tic_outline_item* getFennelOutline(const char* code, s32* size)
|
||||
{
|
||||
enum{Size = sizeof(tic_outline_item)};
|
||||
|
||||
*size = 0;
|
||||
|
||||
static tic_outline_item* items = NULL;
|
||||
|
||||
if(items)
|
||||
{
|
||||
free(items);
|
||||
items = NULL;
|
||||
}
|
||||
|
||||
const char* ptr = code;
|
||||
|
||||
while(true)
|
||||
{
|
||||
static const char FuncString[] = "(fn ";
|
||||
|
||||
ptr = strstr(ptr, FuncString);
|
||||
|
||||
if(ptr)
|
||||
{
|
||||
ptr += sizeof FuncString - 1;
|
||||
|
||||
const char* start = ptr;
|
||||
const char* end = start;
|
||||
|
||||
while(*ptr)
|
||||
{
|
||||
char c = *ptr;
|
||||
|
||||
if(c == ' ' || c == '\t' || c == '\n' || c == '[')
|
||||
{
|
||||
end = ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if(end > start)
|
||||
{
|
||||
items = realloc(items, (*size + 1) * Size);
|
||||
|
||||
items[*size].pos = start;
|
||||
items[*size].size = (s32)(end - start);
|
||||
|
||||
(*size)++;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
static void evalFennel(tic_mem* tic, const char* code) {
|
||||
tic_core* core = (tic_core*)tic;
|
||||
lua_State* fennel = core->currentVM;
|
||||
|
||||
lua_settop(fennel, 0);
|
||||
|
||||
if (luaL_loadbuffer(fennel, execute_fennel_src, strlen(execute_fennel_src), "execute_fennel") != LUA_OK)
|
||||
{
|
||||
core->data->error(core->data->data, "failed to load fennel compiler");
|
||||
}
|
||||
|
||||
lua_pushstring(fennel, code);
|
||||
lua_call(fennel, 1, 1);
|
||||
const char* err = lua_tostring(fennel, -1);
|
||||
|
||||
if (err)
|
||||
{
|
||||
core->data->error(core->data->data, err);
|
||||
}
|
||||
}
|
||||
|
||||
tic_script_config FennelSyntaxConfig =
|
||||
{
|
||||
.id = 14,
|
||||
.name = "fennel",
|
||||
.fileExtension = ".fnl",
|
||||
.projectComment = ";;",
|
||||
.init = initFennel,
|
||||
.close = closeLua,
|
||||
.tick = callLuaTick,
|
||||
.boot = callLuaBoot,
|
||||
|
||||
.callback =
|
||||
{
|
||||
.scanline = callLuaScanline,
|
||||
.border = callLuaBorder,
|
||||
.menu = callLuaMenu,
|
||||
},
|
||||
|
||||
.getOutline = getFennelOutline,
|
||||
.eval = evalFennel,
|
||||
|
||||
.blockCommentStart = NULL,
|
||||
.blockCommentEnd = NULL,
|
||||
.blockCommentStart2 = NULL,
|
||||
.blockCommentEnd2 = NULL,
|
||||
.blockStringStart = NULL,
|
||||
.blockStringEnd = NULL,
|
||||
.singleComment = ";",
|
||||
.blockEnd = NULL,
|
||||
|
||||
.keywords = FennelKeywords,
|
||||
.keywordsCount = COUNT_OF(FennelKeywords),
|
||||
};
|
||||
|
||||
#endif /* defined(TIC_BUILD_WITH_FENNEL) */
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,44 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
s32 luaopen_lpeg(lua_State *lua);
|
||||
|
||||
extern void initLuaAPI(tic_core* core);
|
||||
extern void callLuaTick(tic_mem* tic);
|
||||
extern void callLuaBoot(tic_mem* tic);
|
||||
extern void callLuaScanlineName(tic_mem* tic, s32 row, void* data, const char* name);
|
||||
extern void callLuaScanline(tic_mem* tic, s32 row, void* data);
|
||||
extern void callLuaBorder(tic_mem* tic, s32 row, void* data);
|
||||
extern void callLuaOverline(tic_mem* tic, void* data);
|
||||
extern void callLuaMenu(tic_mem* tic, s32 index, void* data);
|
||||
extern void closeLua(tic_mem* tic);
|
||||
extern void callLuaTick(tic_mem* tic);
|
||||
extern void lua_open_builtins(lua_State *lua);
|
|
@ -0,0 +1,229 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "core/core.h"
|
||||
|
||||
// Moonscript requires Lua
|
||||
#if defined(TIC_BUILD_WITH_LUA)
|
||||
|
||||
#include "lua_api.h"
|
||||
|
||||
#if defined(TIC_BUILD_WITH_MOON)
|
||||
|
||||
static const char _ms_loadstring[] = "_ms_loadstring";
|
||||
|
||||
static inline bool isalnum_(char c) {return isalnum(c) || c == '_';}
|
||||
|
||||
#include "moonscript.h"
|
||||
|
||||
#define MOON_CODE(...) #__VA_ARGS__
|
||||
|
||||
static const char* execute_moonscript_src = MOON_CODE(
|
||||
local fn, err = require('moonscript.base').loadstring(...)
|
||||
|
||||
if not fn then
|
||||
error(err)
|
||||
end
|
||||
return fn()
|
||||
);
|
||||
|
||||
static void setloaded(lua_State* l, char* name)
|
||||
{
|
||||
s32 top = lua_gettop(l);
|
||||
lua_getglobal(l, "package");
|
||||
lua_getfield(l, -1, "loaded");
|
||||
lua_getfield(l, -1, name);
|
||||
if (lua_isnil(l, -1)) {
|
||||
lua_pop(l, 1);
|
||||
lua_pushvalue(l, top);
|
||||
lua_setfield(l, -2, name);
|
||||
}
|
||||
|
||||
lua_settop(l, top);
|
||||
}
|
||||
|
||||
static void evalMoonscript(tic_mem* tic, const char* code) {
|
||||
tic_core* core = (tic_core*)tic;
|
||||
lua_State* lua = core->currentVM;
|
||||
|
||||
lua_getglobal(lua, _ms_loadstring);
|
||||
|
||||
lua_pushstring(lua, code);
|
||||
if (lua_pcall(lua, 1, 1, 0) != LUA_OK)
|
||||
{
|
||||
const char* msg = lua_tostring(lua, -1);
|
||||
if (msg)
|
||||
{
|
||||
core->data->error(core->data->data, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool initMoonscript(tic_mem* tic, const char* code)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
closeLua(tic);
|
||||
|
||||
lua_State* lua = core->currentVM = luaL_newstate();
|
||||
lua_open_builtins(lua);
|
||||
|
||||
luaopen_lpeg(lua);
|
||||
setloaded(lua, "lpeg");
|
||||
|
||||
initLuaAPI(core);
|
||||
|
||||
{
|
||||
lua_State* moon = lua;
|
||||
|
||||
lua_settop(moon, 0);
|
||||
|
||||
if (luaL_loadbuffer(moon, (const char *)moonscript_lua, moonscript_lua_len, "moonscript.lua") != LUA_OK)
|
||||
{
|
||||
core->data->error(core->data->data, "failed to load moonscript.lua");
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_call(moon, 0, 0);
|
||||
|
||||
if (luaL_loadbuffer(moon, execute_moonscript_src, strlen(execute_moonscript_src), "execute_moonscript") != LUA_OK)
|
||||
{
|
||||
core->data->error(core->data->data, "failed to load moonscript compiler");
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_setglobal(lua, _ms_loadstring);
|
||||
lua_getglobal(lua, _ms_loadstring);
|
||||
|
||||
lua_pushstring(moon, code);
|
||||
if (lua_pcall(moon, 1, 1, 0) != LUA_OK)
|
||||
{
|
||||
const char* msg = lua_tostring(moon, -1);
|
||||
|
||||
if (msg)
|
||||
{
|
||||
core->data->error(core->data->data, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* const MoonKeywords [] =
|
||||
{
|
||||
"false", "true", "nil", "local", "return",
|
||||
"break", "continue", "for", "while",
|
||||
"if", "else", "elseif", "unless", "switch",
|
||||
"when", "and", "or", "in", "do",
|
||||
"not", "super", "try", "catch",
|
||||
"with", "export", "import", "then",
|
||||
"from", "class", "extends", "new", "using",
|
||||
};
|
||||
|
||||
static const tic_outline_item* getMoonOutline(const char* code, s32* size)
|
||||
{
|
||||
enum{Size = sizeof(tic_outline_item)};
|
||||
|
||||
*size = 0;
|
||||
|
||||
static tic_outline_item* items = NULL;
|
||||
|
||||
if(items)
|
||||
{
|
||||
free(items);
|
||||
items = NULL;
|
||||
}
|
||||
|
||||
const char* ptr = code;
|
||||
|
||||
while(true)
|
||||
{
|
||||
static const char FuncString[] = "=->";
|
||||
|
||||
ptr = strstr(ptr, FuncString);
|
||||
|
||||
if(ptr)
|
||||
{
|
||||
const char* end = ptr;
|
||||
|
||||
ptr += sizeof FuncString - 1;
|
||||
|
||||
while(end >= code && !isalnum_(*end)) end--;
|
||||
|
||||
const char* start = end;
|
||||
|
||||
for (const char* val = start-1; val >= code && (isalnum_(*val)); val--, start--);
|
||||
|
||||
if(end > start)
|
||||
{
|
||||
items = realloc(items, (*size + 1) * Size);
|
||||
|
||||
items[*size].pos = start;
|
||||
items[*size].size = (s32)(end - start + 1);
|
||||
|
||||
(*size)++;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
tic_script_config MoonSyntaxConfig =
|
||||
{
|
||||
.id = 13,
|
||||
.name = "moon",
|
||||
.fileExtension = ".moon",
|
||||
.projectComment = "--",
|
||||
.init = initMoonscript,
|
||||
.close = closeLua,
|
||||
.tick = callLuaTick,
|
||||
.boot = callLuaBoot,
|
||||
|
||||
.callback =
|
||||
{
|
||||
.scanline = callLuaScanline,
|
||||
.border = callLuaBorder,
|
||||
.menu = callLuaMenu,
|
||||
},
|
||||
|
||||
.getOutline = getMoonOutline,
|
||||
.eval = evalMoonscript,
|
||||
|
||||
.blockCommentStart = NULL,
|
||||
.blockCommentEnd = NULL,
|
||||
.blockCommentStart2 = NULL,
|
||||
.blockCommentEnd2 = NULL,
|
||||
.blockStringStart = NULL,
|
||||
.blockStringEnd = NULL,
|
||||
.singleComment = "--",
|
||||
.blockEnd = NULL,
|
||||
|
||||
.keywords = MoonKeywords,
|
||||
.keywordsCount = COUNT_OF(MoonKeywords),
|
||||
};
|
||||
|
||||
#endif /* defined(TIC_BUILD_WITH_MOON) */
|
||||
|
||||
#endif /* defined(TIC_BUILD_WITH_LUA) */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,320 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "cart.h"
|
||||
|
||||
#if defined(BUILD_DEPRECATED)
|
||||
#include "tools.h"
|
||||
#include "ext/gif.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(DINGUX) && !defined(static_assert)
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CHUNK_DUMMY, // 0
|
||||
CHUNK_TILES, // 1
|
||||
CHUNK_SPRITES, // 2
|
||||
CHUNK_COVER_DEP, // 3 - deprecated chunk
|
||||
CHUNK_MAP, // 4
|
||||
CHUNK_CODE, // 5
|
||||
CHUNK_FLAGS, // 6
|
||||
CHUNK_TEMP2, // 7
|
||||
CHUNK_TEMP3, // 8
|
||||
CHUNK_SAMPLES, // 9
|
||||
CHUNK_WAVEFORM, // 10
|
||||
CHUNK_TEMP4, // 11
|
||||
CHUNK_PALETTE, // 12
|
||||
CHUNK_PATTERNS_DEP, // 13 - deprecated chunk
|
||||
CHUNK_MUSIC, // 14
|
||||
CHUNK_PATTERNS, // 15
|
||||
CHUNK_CODE_ZIP, // 16
|
||||
CHUNK_DEFAULT, // 17
|
||||
CHUNK_SCREEN, // 18
|
||||
CHUNK_BINARY, // 19
|
||||
CHUNK_LANG, // 20
|
||||
} ChunkType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 type:5; // ChunkType
|
||||
u32 bank:TIC_BANK_BITS;
|
||||
u32 size:TIC_BANKSIZE_BITS; // max chunk size is 64K
|
||||
u32 temp:8;
|
||||
} Chunk;
|
||||
|
||||
static_assert(sizeof(Chunk) == 4, "tic_chunk_size");
|
||||
|
||||
static const u8 Sweetie16[] = {0x1a, 0x1c, 0x2c, 0x5d, 0x27, 0x5d, 0xb1, 0x3e, 0x53, 0xef, 0x7d, 0x57, 0xff, 0xcd, 0x75, 0xa7, 0xf0, 0x70, 0x38, 0xb7, 0x64, 0x25, 0x71, 0x79, 0x29, 0x36, 0x6f, 0x3b, 0x5d, 0xc9, 0x41, 0xa6, 0xf6, 0x73, 0xef, 0xf7, 0xf4, 0xf4, 0xf4, 0x94, 0xb0, 0xc2, 0x56, 0x6c, 0x86, 0x33, 0x3c, 0x57};
|
||||
static const u8 Waveforms[] = {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe};
|
||||
|
||||
static s32 chunkSize(const Chunk* chunk)
|
||||
{
|
||||
return chunk->size == 0 && chunk->type == CHUNK_CODE ? TIC_BANK_SIZE : chunk->size;
|
||||
}
|
||||
|
||||
void tic_cart_load(tic_cartridge* cart, const u8* buffer, s32 size)
|
||||
{
|
||||
memset(cart, 0, sizeof(tic_cartridge));
|
||||
const u8* end = buffer + size;
|
||||
|
||||
#define LOAD_CHUNK(to) memcpy(&to, ptr, MIN(sizeof(to), chunk->size ? chunk->size : TIC_BANK_SIZE))
|
||||
|
||||
// load palette chunk first
|
||||
{
|
||||
const u8* ptr = buffer;
|
||||
while (ptr < end)
|
||||
{
|
||||
const Chunk* chunk = (Chunk*)ptr;
|
||||
ptr += sizeof(Chunk);
|
||||
|
||||
switch (chunk->type)
|
||||
{
|
||||
case CHUNK_PALETTE:
|
||||
LOAD_CHUNK(cart->banks[chunk->bank].palette);
|
||||
break;
|
||||
case CHUNK_DEFAULT:
|
||||
memcpy(&cart->banks[chunk->bank].palette, Sweetie16, sizeof Sweetie16);
|
||||
memcpy(&cart->banks[chunk->bank].sfx.waveforms, Waveforms, sizeof Waveforms);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
ptr += chunkSize(chunk);
|
||||
}
|
||||
|
||||
#if defined(BUILD_DEPRECATED)
|
||||
// workaround to support ancient carts without palette
|
||||
// load DB16 palette if it not exists
|
||||
if (EMPTY(cart->bank0.palette.vbank0.data))
|
||||
{
|
||||
static const u8 DB16[] = { 0x14, 0x0c, 0x1c, 0x44, 0x24, 0x34, 0x30, 0x34, 0x6d, 0x4e, 0x4a, 0x4e, 0x85, 0x4c, 0x30, 0x34, 0x65, 0x24, 0xd0, 0x46, 0x48, 0x75, 0x71, 0x61, 0x59, 0x7d, 0xce, 0xd2, 0x7d, 0x2c, 0x85, 0x95, 0xa1, 0x6d, 0xaa, 0x2c, 0xd2, 0xaa, 0x99, 0x6d, 0xc2, 0xca, 0xda, 0xd4, 0x5e, 0xde, 0xee, 0xd6 };
|
||||
memcpy(cart->bank0.palette.vbank0.data, DB16, sizeof DB16);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct CodeChunk {s32 size; const char* data;} code[TIC_BANKS] = {0};
|
||||
struct BinaryChunk {s32 size; const char* data;} binary[TIC_BINARY_BANKS] = {0};
|
||||
|
||||
{
|
||||
const u8* ptr = buffer;
|
||||
while(ptr < end)
|
||||
{
|
||||
const Chunk* chunk = (Chunk*)ptr;
|
||||
ptr += sizeof(Chunk);
|
||||
|
||||
switch(chunk->type)
|
||||
{
|
||||
case CHUNK_TILES: LOAD_CHUNK(cart->banks[chunk->bank].tiles); break;
|
||||
case CHUNK_SPRITES: LOAD_CHUNK(cart->banks[chunk->bank].sprites); break;
|
||||
case CHUNK_MAP: LOAD_CHUNK(cart->banks[chunk->bank].map); break;
|
||||
case CHUNK_SAMPLES: LOAD_CHUNK(cart->banks[chunk->bank].sfx.samples); break;
|
||||
case CHUNK_WAVEFORM: LOAD_CHUNK(cart->banks[chunk->bank].sfx.waveforms); break;
|
||||
case CHUNK_MUSIC: LOAD_CHUNK(cart->banks[chunk->bank].music.tracks); break;
|
||||
case CHUNK_PATTERNS: LOAD_CHUNK(cart->banks[chunk->bank].music.patterns); break;
|
||||
case CHUNK_FLAGS: LOAD_CHUNK(cart->banks[chunk->bank].flags); break;
|
||||
case CHUNK_SCREEN: LOAD_CHUNK(cart->banks[chunk->bank].screen); break;
|
||||
case CHUNK_LANG: LOAD_CHUNK(cart->lang); break;
|
||||
case CHUNK_BINARY:
|
||||
binary[chunk->bank] = (struct BinaryChunk){chunkSize(chunk), ptr};
|
||||
break;
|
||||
case CHUNK_CODE:
|
||||
code[chunk->bank] = (struct CodeChunk){chunkSize(chunk), ptr};
|
||||
break;
|
||||
#if defined(BUILD_DEPRECATED)
|
||||
case CHUNK_CODE_ZIP:
|
||||
tic_tool_unzip(cart->code.data, TIC_CODE_SIZE, ptr, chunk->size);
|
||||
break;
|
||||
case CHUNK_COVER_DEP:
|
||||
{
|
||||
// workaround to load deprecated cover section
|
||||
gif_image* image = gif_read_data(ptr, chunk->size);
|
||||
|
||||
if (image)
|
||||
{
|
||||
if(image->width == TIC80_WIDTH && image->height == TIC80_HEIGHT)
|
||||
for (s32 i = 0; i < TIC80_WIDTH * TIC80_HEIGHT; i++)
|
||||
tic_tool_poke4(cart->bank0.screen.data, i,
|
||||
tic_nearest_color(cart->bank0.palette.vbank0.colors, (const tic_rgb*)&image->palette[image->buffer[i]], TIC_PALETTE_SIZE));
|
||||
|
||||
gif_close(image);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CHUNK_PATTERNS_DEP:
|
||||
{
|
||||
// workaround to load deprecated music patterns section
|
||||
// and automatically convert volume value to a command
|
||||
tic_patterns* ptrns = &cart->banks[chunk->bank].music.patterns;
|
||||
LOAD_CHUNK(*ptrns);
|
||||
for(s32 i = 0; i < MUSIC_PATTERNS; i++)
|
||||
for(s32 r = 0; r < MUSIC_PATTERN_ROWS; r++)
|
||||
{
|
||||
tic_track_row* row = &ptrns->data[i].rows[r];
|
||||
if(row->note >= NoteStart && row->command == tic_music_cmd_empty)
|
||||
{
|
||||
row->command = tic_music_cmd_volume;
|
||||
row->param2 = row->param1 = MAX_VOLUME - row->param1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
|
||||
ptr += chunkSize(chunk);
|
||||
}
|
||||
#undef LOAD_CHUNK
|
||||
|
||||
{
|
||||
u32 total_size = 0;
|
||||
char* ptr = cart->binary.data;
|
||||
RFOR(const struct BinaryChunk*, chunk, binary)
|
||||
if (chunk->size)
|
||||
{
|
||||
memcpy(ptr, chunk->data, chunk->size);
|
||||
ptr += chunk->size;
|
||||
total_size += chunk->size;
|
||||
}
|
||||
cart->binary.size = total_size;
|
||||
}
|
||||
|
||||
if (!*cart->code.data)
|
||||
{
|
||||
char* ptr = cart->code.data;
|
||||
RFOR(const struct CodeChunk*, chunk, code)
|
||||
if (chunk->data)
|
||||
{
|
||||
memcpy(ptr, chunk->data, chunk->size);
|
||||
ptr += chunk->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static s32 calcBufferSize(const void* buffer, s32 size)
|
||||
{
|
||||
const u8* ptr = (u8*)buffer + size - 1;
|
||||
const u8* end = (u8*)buffer;
|
||||
|
||||
while(ptr >= end)
|
||||
{
|
||||
if(*ptr) break;
|
||||
|
||||
ptr--;
|
||||
size--;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static u8* saveFixedChunk(u8* buffer, ChunkType type, const void* from, s32 size, s32 bank)
|
||||
{
|
||||
if(size)
|
||||
{
|
||||
Chunk chunk = {.type = type, .bank = bank, .size = size, .temp = 0};
|
||||
memcpy(buffer, &chunk, sizeof(Chunk));
|
||||
buffer += sizeof(Chunk);
|
||||
|
||||
memcpy(buffer, from, size);
|
||||
buffer += size;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static u8* saveChunk(u8* buffer, ChunkType type, const void* from, s32 size, s32 bank)
|
||||
{
|
||||
s32 chunkSize = calcBufferSize(from, size);
|
||||
|
||||
return saveFixedChunk(buffer, type, from, chunkSize, bank);
|
||||
}
|
||||
|
||||
s32 tic_cart_save(const tic_cartridge* cart, u8* buffer)
|
||||
{
|
||||
u8* start = buffer;
|
||||
|
||||
#define SAVE_CHUNK(ID, FROM, BANK) saveChunk(buffer, ID, &FROM, sizeof(FROM), BANK)
|
||||
|
||||
tic_waveforms defaultWaveforms = {0};
|
||||
tic_palettes defaultPalettes = {0};
|
||||
|
||||
memcpy(&defaultWaveforms, Waveforms, sizeof Waveforms);
|
||||
memcpy(&defaultPalettes, Sweetie16, sizeof Sweetie16);
|
||||
|
||||
for(s32 i = 0; i < TIC_BANKS; i++)
|
||||
{
|
||||
if(memcmp(&cart->banks[i].sfx.waveforms, &defaultWaveforms, sizeof defaultWaveforms) == 0
|
||||
&& memcmp(&cart->banks[i].palette, &defaultPalettes, sizeof defaultPalettes) == 0)
|
||||
{
|
||||
Chunk chunk = {CHUNK_DEFAULT, i, 0, 0};
|
||||
memcpy(buffer, &chunk, sizeof chunk);
|
||||
buffer += sizeof chunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = SAVE_CHUNK(CHUNK_PALETTE, cart->banks[i].palette, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_WAVEFORM, cart->banks[i].sfx.waveforms, i);
|
||||
}
|
||||
|
||||
buffer = SAVE_CHUNK(CHUNK_TILES, cart->banks[i].tiles, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_SPRITES, cart->banks[i].sprites, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_MAP, cart->banks[i].map, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_SAMPLES, cart->banks[i].sfx.samples, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_PATTERNS, cart->banks[i].music.patterns, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_MUSIC, cart->banks[i].music.tracks, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_FLAGS, cart->banks[i].flags, i);
|
||||
buffer = SAVE_CHUNK(CHUNK_SCREEN, cart->banks[i].screen, i);
|
||||
}
|
||||
|
||||
const char* ptr;
|
||||
if (cart->binary.size)
|
||||
{
|
||||
ptr = cart->binary.data;
|
||||
s32 remaining = cart->binary.size;
|
||||
for (s32 i = cart->binary.size / TIC_BANK_SIZE; i >= 0; --i, ptr += TIC_BANK_SIZE)
|
||||
{
|
||||
buffer = saveFixedChunk(buffer, CHUNK_BINARY, ptr, MIN(remaining, TIC_BANK_SIZE), i);
|
||||
remaining -= TIC_BANK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
ptr = cart->code.data;
|
||||
for(s32 i = strlen(ptr) / TIC_BANK_SIZE; i >= 0; --i, ptr += TIC_BANK_SIZE)
|
||||
buffer = saveFixedChunk(buffer, CHUNK_CODE, ptr, MIN(strlen(ptr), TIC_BANK_SIZE), i);
|
||||
|
||||
if(cart->lang)
|
||||
SAVE_CHUNK(CHUNK_LANG, cart->lang, 0);
|
||||
|
||||
#undef SAVE_CHUNK
|
||||
|
||||
return (s32)(buffer - start);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic.h"
|
||||
|
||||
void tic_cart_load(tic_cartridge* rom, const u8* buffer, s32 size);
|
||||
s32 tic_cart_save(const tic_cartridge* rom, u8* buffer);
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,740 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "api.h"
|
||||
#include "core.h"
|
||||
#include "tilesheet.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(DINGUX) && !defined(static_assert)
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
#ifdef _3DS
|
||||
#include <3ds.h>
|
||||
#endif
|
||||
|
||||
static_assert(TIC_BANK_BITS == 3, "tic_bank_bits");
|
||||
static_assert(sizeof(tic_map) < 1024 * 32, "tic_map");
|
||||
static_assert(sizeof(tic_vram) == TIC_VRAM_SIZE, "tic_vram");
|
||||
static_assert(sizeof(tic_ram) == TIC_RAM_SIZE, "tic_ram");
|
||||
|
||||
u8 tic_api_peek(tic_mem* memory, s32 address, s32 bits)
|
||||
{
|
||||
if (address < 0)
|
||||
return 0;
|
||||
|
||||
const u8* ram = (u8*)memory->ram;
|
||||
enum{RamBits = sizeof(tic_ram) * BITS_IN_BYTE};
|
||||
|
||||
switch(bits)
|
||||
{
|
||||
case 1: if(address < RamBits / 1) return tic_tool_peek1(ram, address);
|
||||
case 2: if(address < RamBits / 2) return tic_tool_peek2(ram, address);
|
||||
case 4: if(address < RamBits / 4) return tic_tool_peek4(ram, address);
|
||||
case 8: if(address < RamBits / 8) return ram[address];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tic_api_poke(tic_mem* memory, s32 address, u8 value, s32 bits)
|
||||
{
|
||||
if (address < 0)
|
||||
return;
|
||||
|
||||
tic_core* core = (tic_core*)memory;
|
||||
u8* ram = (u8*)memory->ram;
|
||||
enum{RamBits = sizeof(tic_ram) * BITS_IN_BYTE};
|
||||
|
||||
switch(bits)
|
||||
{
|
||||
case 1: if(address < RamBits / 1) tic_tool_poke1(ram, address, value); break;
|
||||
case 2: if(address < RamBits / 2) tic_tool_poke2(ram, address, value); break;
|
||||
case 4: if(address < RamBits / 4) tic_tool_poke4(ram, address, value); break;
|
||||
case 8: if(address < RamBits / 8) ram[address] = value; break;
|
||||
}
|
||||
}
|
||||
|
||||
u8 tic_api_peek4(tic_mem* memory, s32 address)
|
||||
{
|
||||
return tic_api_peek(memory, address, 4);
|
||||
}
|
||||
|
||||
u8 tic_api_peek1(tic_mem* memory, s32 address)
|
||||
{
|
||||
return tic_api_peek(memory, address, 1);
|
||||
}
|
||||
|
||||
void tic_api_poke1(tic_mem* memory, s32 address, u8 value)
|
||||
{
|
||||
tic_api_poke(memory, address, value, 1);
|
||||
}
|
||||
|
||||
u8 tic_api_peek2(tic_mem* memory, s32 address)
|
||||
{
|
||||
return tic_api_peek(memory, address, 2);
|
||||
}
|
||||
|
||||
void tic_api_poke2(tic_mem* memory, s32 address, u8 value)
|
||||
{
|
||||
tic_api_poke(memory, address, value, 2);
|
||||
}
|
||||
|
||||
void tic_api_poke4(tic_mem* memory, s32 address, u8 value)
|
||||
{
|
||||
tic_api_poke(memory, address, value, 4);
|
||||
}
|
||||
|
||||
void tic_api_memcpy(tic_mem* memory, s32 dst, s32 src, s32 size)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
s32 bound = sizeof(tic_ram) - size;
|
||||
|
||||
if (size >= 0
|
||||
&& size <= sizeof(tic_ram)
|
||||
&& dst >= 0
|
||||
&& src >= 0
|
||||
&& dst <= bound
|
||||
&& src <= bound)
|
||||
{
|
||||
u8* base = (u8*)memory->ram;
|
||||
memcpy(base + dst, base + src, size);
|
||||
}
|
||||
}
|
||||
|
||||
void tic_api_memset(tic_mem* memory, s32 dst, u8 val, s32 size)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
s32 bound = sizeof(tic_ram) - size;
|
||||
|
||||
if (size >= 0
|
||||
&& size <= sizeof(tic_ram)
|
||||
&& dst >= 0
|
||||
&& dst <= bound)
|
||||
{
|
||||
u8* base = (u8*)memory->ram;
|
||||
memset(base + dst, val, size);
|
||||
}
|
||||
}
|
||||
|
||||
void tic_api_trace(tic_mem* memory, const char* text, u8 color)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
core->data->trace(core->data->data, text ? text : "nil", color);
|
||||
}
|
||||
|
||||
u32 tic_api_pmem(tic_mem* tic, s32 index, u32 value, bool set)
|
||||
{
|
||||
u32 old = tic->ram->persistent.data[index];
|
||||
|
||||
if (set)
|
||||
tic->ram->persistent.data[index] = value;
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
void tic_api_exit(tic_mem* tic)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
core->data->exit(core->data->data);
|
||||
}
|
||||
|
||||
static inline void sync(void* dst, void* src, s32 size, bool rev)
|
||||
{
|
||||
if(rev)
|
||||
SWAP(dst, src, void*);
|
||||
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
void tic_api_sync(tic_mem* tic, u32 mask, s32 bank, bool toCart)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
static const struct { s32 bank; s32 ram; s32 size; u8 mask; } Sections[] =
|
||||
{
|
||||
#define TIC_SYNC_DEF(CART, RAM, ...) { offsetof(tic_bank, CART), offsetof(tic_ram, RAM), sizeof(tic_##CART), tic_sync_##CART },
|
||||
TIC_SYNC_LIST(TIC_SYNC_DEF)
|
||||
#undef TIC_SYNC_DEF
|
||||
};
|
||||
|
||||
enum { Count = COUNT_OF(Sections), Mask = (1 << Count) - 1 };
|
||||
|
||||
if (mask == 0) mask = Mask;
|
||||
|
||||
mask &= ~core->state.synced & Mask;
|
||||
|
||||
assert(bank >= 0 && bank < TIC_BANKS);
|
||||
|
||||
for (s32 i = 0; i < Count; i++)
|
||||
if(mask & Sections[i].mask)
|
||||
sync((u8*)tic->ram + Sections[i].ram, (u8*)&tic->cart.banks[bank] + Sections[i].bank, Sections[i].size, toCart);
|
||||
|
||||
core->state.synced |= mask;
|
||||
}
|
||||
|
||||
double tic_api_time(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
return (clock() - core->data->start) * 1000.0 / CLOCKS_PER_SEC;
|
||||
}
|
||||
|
||||
s32 tic_api_tstamp(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
return (s32)time(NULL);
|
||||
}
|
||||
|
||||
static bool compareMetatag(const char* code, const char* tag, const char* value, const char* comment)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
char* str = tic_tool_metatag(code, tag, comment);
|
||||
|
||||
if (str)
|
||||
{
|
||||
result = strcmp(str, value) == 0;
|
||||
free(str);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const tic_script_config* tic_core_script_config(tic_mem* memory)
|
||||
{
|
||||
FOR_EACH_LANG(it)
|
||||
{
|
||||
if(it->id == memory->cart.lang || compareMetatag(memory->cart.code.data, "script", it->name, it->singleComment))
|
||||
return it;
|
||||
}
|
||||
FOR_EACH_LANG_END
|
||||
|
||||
return Languages[0];
|
||||
}
|
||||
|
||||
static void updateSaveid(tic_mem* memory)
|
||||
{
|
||||
memset(memory->saveid, 0, sizeof memory->saveid);
|
||||
char* saveid = tic_tool_metatag(memory->cart.code.data, "saveid", tic_core_script_config(memory)->singleComment);
|
||||
if (saveid)
|
||||
{
|
||||
strncpy(memory->saveid, saveid, TIC_SAVEID_SIZE - 1);
|
||||
free(saveid);
|
||||
}
|
||||
}
|
||||
|
||||
static void soundClear(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
for (s32 i = 0; i < TIC_SOUND_CHANNELS; i++)
|
||||
{
|
||||
static const tic_channel_data EmptyChannel =
|
||||
{
|
||||
.tick = -1,
|
||||
.pos = NULL,
|
||||
.index = -1,
|
||||
.note = 0,
|
||||
.volume = {0, 0},
|
||||
.speed = 0,
|
||||
.duration = -1,
|
||||
};
|
||||
|
||||
memcpy(&core->state.music.channels[i], &EmptyChannel, sizeof EmptyChannel);
|
||||
memcpy(&core->state.sfx.channels[i], &EmptyChannel, sizeof EmptyChannel);
|
||||
|
||||
memset(core->state.sfx.channels[i].pos = &memory->ram->sfxpos[i], -1, sizeof(tic_sfx_pos));
|
||||
memset(core->state.music.channels[i].pos = &core->state.music.sfxpos[i], -1, sizeof(tic_sfx_pos));
|
||||
}
|
||||
|
||||
memset(&memory->ram->registers, 0, sizeof memory->ram->registers);
|
||||
memset(memory->product.samples.buffer, 0, memory->product.samples.count * TIC80_SAMPLESIZE);
|
||||
|
||||
tic_api_music(memory, -1, 0, 0, false, false, -1, -1);
|
||||
}
|
||||
|
||||
static void resetVbank(tic_mem* memory)
|
||||
{
|
||||
ZEROMEM(memory->ram->vram.vars);
|
||||
|
||||
static const u8 DefaultMapping[] = { 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe };
|
||||
memcpy(memory->ram->vram.mapping, DefaultMapping, sizeof DefaultMapping);
|
||||
memory->ram->vram.palette = memory->cart.bank0.palette.vbank0;
|
||||
memory->ram->vram.blit.segment = TIC_DEFAULT_BLIT_MODE;
|
||||
}
|
||||
|
||||
static void font2ram(tic_mem* memory)
|
||||
{
|
||||
memory->ram->font = (tic_font)
|
||||
{
|
||||
.regular =
|
||||
{
|
||||
.data =
|
||||
{
|
||||
#include "font.inl"
|
||||
},
|
||||
.width = TIC_FONT_WIDTH,
|
||||
.height = TIC_FONT_HEIGHT,
|
||||
},
|
||||
|
||||
.alt =
|
||||
{
|
||||
.data =
|
||||
{
|
||||
#include "altfont.inl"
|
||||
},
|
||||
.width = TIC_ALTFONT_WIDTH,
|
||||
.height = TIC_FONT_HEIGHT,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
void tic_api_reset(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
// keyboard state is critical and must be preserved across API resets.
|
||||
// Often `tic_api_reset` is called to effect transitions between modes
|
||||
// yet we still need to know when the key WAS pressed after the
|
||||
// transition - to prevent it from counting as a second keypress.
|
||||
//
|
||||
// So why presev `now` not `previous`? this is most often called in
|
||||
// the middle of a tick... so we preserve now, which during `tick_end`
|
||||
// is copied to previous. This duplicates the prior behavior of
|
||||
// `ram.input.keyboard` (which existing outside `state`).
|
||||
u32 kb_now = core->state.keyboard.now.data;
|
||||
ZEROMEM(core->state);
|
||||
core->state.keyboard.now.data = kb_now;
|
||||
tic_api_clip(memory, 0, 0, TIC80_WIDTH, TIC80_HEIGHT);
|
||||
|
||||
resetVbank(memory);
|
||||
|
||||
VBANK(memory, 1)
|
||||
{
|
||||
resetVbank(memory);
|
||||
|
||||
// init VBANK1 palette with VBANK0 palette if it's empty
|
||||
// for backward compatibility
|
||||
if(!EMPTY(memory->cart.bank0.palette.vbank1.data))
|
||||
memcpy(&memory->ram->vram.palette, &memory->cart.bank0.palette.vbank1, sizeof(tic_palette));
|
||||
}
|
||||
|
||||
memory->ram->input.mouse.relative = 0;
|
||||
|
||||
soundClear(memory);
|
||||
updateSaveid(memory);
|
||||
font2ram(memory);
|
||||
}
|
||||
|
||||
static void cart2ram(tic_mem* memory)
|
||||
{
|
||||
font2ram(memory);
|
||||
|
||||
enum
|
||||
{
|
||||
#define TIC_SYNC_DEF(NAME, _, INDEX) sync_##NAME = INDEX,
|
||||
TIC_SYNC_LIST(TIC_SYNC_DEF)
|
||||
#undef TIC_SYNC_DEF
|
||||
count,
|
||||
all = (1 << count) - 1,
|
||||
noscreen = BITCLEAR(all, sync_screen)
|
||||
};
|
||||
|
||||
// don't sync empty screen
|
||||
tic_api_sync(memory, EMPTY(memory->cart.bank0.screen.data) ? noscreen : all, 0, false);
|
||||
}
|
||||
|
||||
static void tic_close_current_vm(tic_core* core)
|
||||
{
|
||||
// close previous VM if any
|
||||
if(core->currentVM)
|
||||
{
|
||||
// printf("Closing VM of %s, %d\n", core->currentScript->name, core->currentVM);
|
||||
core->currentScript->close( (tic_mem*)core );
|
||||
core->currentVM = NULL;
|
||||
}
|
||||
if (core->memory.ram == NULL) {
|
||||
core->memory.ram = core->memory.base_ram;
|
||||
}
|
||||
}
|
||||
|
||||
static bool tic_init_vm(tic_core* core, const char* code, const tic_script_config* config)
|
||||
{
|
||||
tic_close_current_vm(core);
|
||||
// set current script config and init
|
||||
core->currentScript = config;
|
||||
bool done = config->init( (tic_mem*) core , code);
|
||||
if(!done)
|
||||
{
|
||||
// if it couldn't init, make sure the VM is not left dirty by the implementation
|
||||
core->currentVM = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf("Initialized VM of %s, %d\n", core->currentScript->name, core->currentVM);
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
s32 tic_api_vbank(tic_mem* tic, s32 bank)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
s32 prev = core->state.vbank.id;
|
||||
|
||||
switch(bank)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
if(core->state.vbank.id != bank)
|
||||
{
|
||||
SWAP(tic->ram->vram, core->state.vbank.mem, tic_vram);
|
||||
core->state.vbank.id = bank;
|
||||
}
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
void tic_core_tick(tic_mem* tic, tic_tick_data* data)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
core->data = data;
|
||||
|
||||
if (!core->state.initialized)
|
||||
{
|
||||
const char* code = tic->cart.code.data;
|
||||
|
||||
bool done = false;
|
||||
const tic_script_config* config = tic_core_script_config(tic);
|
||||
|
||||
if (strlen(code))
|
||||
{
|
||||
cart2ram(tic);
|
||||
|
||||
core->state.synced = 0;
|
||||
tic->input.data = 0;
|
||||
|
||||
if (compareMetatag(code, "input", "mouse", config->singleComment))
|
||||
tic->input.mouse = 1;
|
||||
else if (compareMetatag(code, "input", "gamepad", config->singleComment))
|
||||
tic->input.gamepad = 1;
|
||||
else if (compareMetatag(code, "input", "keyboard", config->singleComment))
|
||||
tic->input.keyboard = 1;
|
||||
else tic->input.data = -1; // default is all enabled
|
||||
|
||||
data->start = clock();
|
||||
|
||||
// TODO: does where to fetch code from need to be a config option so this isn't hard
|
||||
// coded for just a single langage? perhaps change it later when we have a second script
|
||||
// engine that uses BINARY?
|
||||
if (strcmp(config->name,"wasm")==0) {
|
||||
code = tic->cart.binary.data;
|
||||
}
|
||||
|
||||
done = tic_init_vm(core, code, config);
|
||||
}
|
||||
else
|
||||
{
|
||||
core->data->error(core->data->data, "the code is empty");
|
||||
}
|
||||
|
||||
if (done)
|
||||
{
|
||||
config->boot(tic);
|
||||
core->state.tick = config->tick;
|
||||
core->state.callback = config->callback;
|
||||
core->state.initialized = true;
|
||||
}
|
||||
else return;
|
||||
}
|
||||
|
||||
core->state.tick(tic);
|
||||
}
|
||||
|
||||
void tic_core_pause(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
memcpy(&core->pause.state, &core->state, sizeof(tic_core_state_data));
|
||||
memcpy(&core->pause.ram, memory->ram, sizeof(tic_ram));
|
||||
core->pause.input = memory->input.data;
|
||||
|
||||
if (core->data)
|
||||
{
|
||||
core->pause.time.start = core->data->start;
|
||||
core->pause.time.paused = clock();
|
||||
}
|
||||
}
|
||||
|
||||
void tic_core_resume(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
if (core->data)
|
||||
{
|
||||
memcpy(&core->state, &core->pause.state, sizeof(tic_core_state_data));
|
||||
memcpy(memory->ram, &core->pause.ram, sizeof(tic_ram));
|
||||
core->data->start = core->pause.time.start + clock() - core->pause.time.paused;
|
||||
memory->input.data = core->pause.input;
|
||||
}
|
||||
}
|
||||
|
||||
void tic_core_close(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
core->state.initialized = false;
|
||||
|
||||
tic_close_current_vm(core);
|
||||
|
||||
blip_delete(core->blip.left);
|
||||
blip_delete(core->blip.right);
|
||||
|
||||
free(memory->product.screen);
|
||||
free(memory->product.samples.buffer);
|
||||
free(core);
|
||||
}
|
||||
|
||||
void tic_core_tick_start(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
tic_core_sound_tick_start(memory);
|
||||
tic_core_tick_io(memory);
|
||||
|
||||
// SECURITY: preserve the system keyboard/game controller input state
|
||||
// (and restore it post-tick, see below) to prevent user cartridges
|
||||
// from being able to corrupt and take control of the inputs in
|
||||
// nefarious ways.
|
||||
//
|
||||
// Related: https://github.com/nesbox/TIC-80/issues/1785
|
||||
core->state.keyboard.now.data = core->memory.ram->input.keyboard.data;
|
||||
core->state.gamepads.now.data = core->memory.ram->input.gamepads.data;
|
||||
|
||||
core->state.synced = 0;
|
||||
}
|
||||
|
||||
void tic_core_tick_end(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
tic80_input* input = &core->memory.ram->input;
|
||||
|
||||
core->state.gamepads.previous.data = input->gamepads.data;
|
||||
// SECURITY: we do not use `memory.ram.input` here because it is
|
||||
// untrustworthy since the cartridge could have modified it to
|
||||
// inject artificial keyboard/gamepad events.
|
||||
core->state.keyboard.previous.data = core->state.keyboard.now.data;
|
||||
core->state.gamepads.previous.data = core->state.gamepads.now.data;
|
||||
|
||||
tic_core_sound_tick_end(memory);
|
||||
}
|
||||
|
||||
// copied from SDL2
|
||||
static inline void memset4(void* dst, u32 val, u32 dwords)
|
||||
{
|
||||
#if defined(__GNUC__) && defined(i386)
|
||||
s32 u0, u1, u2;
|
||||
__asm__ __volatile__(
|
||||
"cld \n\t"
|
||||
"rep ; stosl \n\t"
|
||||
: "=&D" (u0), "=&a" (u1), "=&c" (u2)
|
||||
: "0" (dst), "1" (val), "2" (dwords)
|
||||
: "memory"
|
||||
);
|
||||
#else
|
||||
u32 _n = (dwords + 3) / 4;
|
||||
u32* _p = (u32*)dst;
|
||||
u32 _val = (val);
|
||||
if (dwords == 0)
|
||||
return;
|
||||
switch (dwords % 4)
|
||||
{
|
||||
case 0: do {
|
||||
*_p++ = _val;
|
||||
case 3: *_p++ = _val;
|
||||
case 2: *_p++ = _val;
|
||||
case 1: *_p++ = _val;
|
||||
} while (--_n);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline tic_vram* vbank0(tic_core* core)
|
||||
{
|
||||
return core->state.vbank.id ? &core->state.vbank.mem : &core->memory.ram->vram;
|
||||
}
|
||||
|
||||
static inline tic_vram* vbank1(tic_core* core)
|
||||
{
|
||||
return core->state.vbank.id ? &core->memory.ram->vram : &core->state.vbank.mem;
|
||||
}
|
||||
|
||||
static inline void updpal(tic_mem* tic, tic_blitpal* pal0, tic_blitpal* pal1)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
*pal0 = tic_tool_palette_blit(&vbank0(core)->palette, core->screen_format);
|
||||
*pal1 = tic_tool_palette_blit(&vbank1(core)->palette, core->screen_format);
|
||||
}
|
||||
|
||||
static inline void updbdr(tic_mem* tic, s32 row, u32* ptr, tic_blit_callback clb, tic_blitpal* pal0, tic_blitpal* pal1)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
if(clb.border) clb.border(tic, row, clb.data);
|
||||
|
||||
if(clb.scanline)
|
||||
{
|
||||
if(row == 0) clb.scanline(tic, 0, clb.data);
|
||||
else if(row > TIC80_MARGIN_TOP && row < (TIC80_HEIGHT + TIC80_MARGIN_TOP))
|
||||
clb.scanline(tic, row - TIC80_MARGIN_TOP, clb.data);
|
||||
}
|
||||
|
||||
if(clb.border || clb.scanline)
|
||||
updpal(tic, pal0, pal1);
|
||||
|
||||
memset4(ptr, pal0->data[vbank0(core)->vars.border], TIC80_FULLWIDTH);
|
||||
}
|
||||
|
||||
static inline u32 blitpix(tic_mem* tic, s32 offset0, s32 offset1, const tic_blitpal* pal0, const tic_blitpal* pal1)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
u32 pix = tic_tool_peek4(vbank1(core)->screen.data, offset1);
|
||||
|
||||
return pix != vbank1(core)->vars.clear
|
||||
? pal1->data[pix]
|
||||
: pal0->data[tic_tool_peek4(vbank0(core)->screen.data, offset0)];
|
||||
}
|
||||
|
||||
void tic_core_blit_ex(tic_mem* tic, tic_blit_callback clb)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
tic_blitpal pal0, pal1;
|
||||
updpal(tic, &pal0, &pal1);
|
||||
|
||||
s32 row = 0;
|
||||
u32* rowPtr = tic->product.screen;
|
||||
|
||||
#define UPDBDR() updbdr(tic, row, rowPtr, clb, &pal0, &pal1)
|
||||
|
||||
for(; row != TIC80_MARGIN_TOP; ++row, rowPtr += TIC80_FULLWIDTH)
|
||||
UPDBDR();
|
||||
|
||||
for(; row != TIC80_FULLHEIGHT - TIC80_MARGIN_BOTTOM; ++row)
|
||||
{
|
||||
UPDBDR();
|
||||
rowPtr += TIC80_MARGIN_LEFT;
|
||||
|
||||
if(*(u16*)&vbank0(core)->vars.offset == 0 && *(u16*)&vbank1(core)->vars.offset == 0)
|
||||
{
|
||||
// render line without XY offsets
|
||||
for(s32 x = (row - TIC80_MARGIN_TOP) * TIC80_WIDTH, end = x + TIC80_WIDTH; x != end; ++x)
|
||||
*rowPtr++ = blitpix(tic, x, x, &pal0, &pal1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// render line with XY offsets
|
||||
enum{OffsetY = TIC80_HEIGHT - TIC80_MARGIN_TOP};
|
||||
s32 start0 = (row - vbank0(core)->vars.offset.y + OffsetY) % TIC80_HEIGHT * TIC80_WIDTH;
|
||||
s32 start1 = (row - vbank1(core)->vars.offset.y + OffsetY) % TIC80_HEIGHT * TIC80_WIDTH;
|
||||
s32 offsetX0 = vbank0(core)->vars.offset.x;
|
||||
s32 offsetX1 = vbank1(core)->vars.offset.x;
|
||||
|
||||
for(s32 x = TIC80_WIDTH; x != 2 * TIC80_WIDTH; ++x)
|
||||
*rowPtr++ = blitpix(tic, (x - offsetX0) % TIC80_WIDTH + start0,
|
||||
(x - offsetX1) % TIC80_WIDTH + start1, &pal0, &pal1);
|
||||
}
|
||||
|
||||
rowPtr += TIC80_MARGIN_RIGHT;
|
||||
}
|
||||
|
||||
for(; row != TIC80_FULLHEIGHT; ++row, rowPtr += TIC80_FULLWIDTH)
|
||||
UPDBDR();
|
||||
|
||||
#undef UPDBDR
|
||||
}
|
||||
|
||||
static inline void scanline(tic_mem* memory, s32 row, void* data)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
if (core->state.initialized)
|
||||
core->state.callback.scanline(memory, row, data);
|
||||
}
|
||||
|
||||
static inline void border(tic_mem* memory, s32 row, void* data)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
if (core->state.initialized)
|
||||
core->state.callback.border(memory, row, data);
|
||||
}
|
||||
|
||||
void tic_core_blit(tic_mem* tic)
|
||||
{
|
||||
tic_core_blit_ex(tic, (tic_blit_callback){scanline, border, NULL});
|
||||
}
|
||||
|
||||
tic_mem* tic_core_create(s32 samplerate, tic80_pixel_color_format format)
|
||||
{
|
||||
tic_core* core = (tic_core*)malloc(sizeof(tic_core));
|
||||
memset(core, 0, sizeof(tic_core));
|
||||
|
||||
tic80* product = &core->memory.product;
|
||||
|
||||
core->screen_format = format;
|
||||
core->memory.ram = (tic_ram*)malloc(TIC_RAM_SIZE);
|
||||
core->memory.base_ram = core->memory.ram;
|
||||
core->samplerate = samplerate;
|
||||
|
||||
memset(core->memory.ram, 0, sizeof(tic_ram));
|
||||
#ifdef _3DS
|
||||
// To feed texture data directly to the 3DS GPU, linearly allocated memory is required, which is
|
||||
// not guaranteed by malloc.
|
||||
// Additionally, allocate TIC80_FULLHEIGHT + 1 lines to minimize glitches in linear scaling mode.
|
||||
product->screen = linearAlloc(TIC80_FULLWIDTH * (TIC80_FULLHEIGHT + 1) * sizeof(u32));
|
||||
#else
|
||||
product->screen = malloc(TIC80_FULLWIDTH * TIC80_FULLHEIGHT * sizeof product->screen[0]);
|
||||
#endif
|
||||
product->samples.count = samplerate * TIC80_SAMPLE_CHANNELS / TIC80_FRAMERATE;
|
||||
product->samples.buffer = malloc(product->samples.count * TIC80_SAMPLESIZE);
|
||||
|
||||
core->blip.left = blip_new(samplerate / 10);
|
||||
core->blip.right = blip_new(samplerate / 10);
|
||||
|
||||
blip_set_rates(core->blip.left, CLOCKRATE, samplerate);
|
||||
blip_set_rates(core->blip.right, CLOCKRATE, samplerate);
|
||||
|
||||
tic_api_reset(&core->memory);
|
||||
|
||||
return &core->memory;
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api.h"
|
||||
#include "tools.h"
|
||||
#include "blip_buf.h"
|
||||
|
||||
#define CLOCKRATE (255<<13)
|
||||
#define TIC_DEFAULT_COLOR 15
|
||||
#define TIC_SOUND_RINGBUF_LEN 12 // in worst case, this induces ~ 12 tick delay i.e. 200 ms
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 time; /* clock time of next delta */
|
||||
s32 phase; /* position within waveform */
|
||||
s32 amp; /* current amplitude in delta buffer */
|
||||
}tic_sound_register_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
s32 tick;
|
||||
tic_sfx_pos* pos;
|
||||
s32 index;
|
||||
s32 note;
|
||||
struct
|
||||
{
|
||||
u8 left:4;
|
||||
u8 right:4;
|
||||
} volume;
|
||||
s8 speed:SFX_SPEED_BITS;
|
||||
s32 duration;
|
||||
} tic_channel_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
s32 tick;
|
||||
u8 note1:4;
|
||||
u8 note2:4;
|
||||
} chord;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 tick;
|
||||
u8 period:4;
|
||||
u8 depth:4;
|
||||
} vibrato;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 tick;
|
||||
u8 note;
|
||||
s32 duration;
|
||||
} slide;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 value;
|
||||
} finepitch;
|
||||
|
||||
struct
|
||||
{
|
||||
const tic_track_row* row;
|
||||
s32 ticks;
|
||||
} delay;
|
||||
|
||||
} tic_command_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool active;
|
||||
s32 frame;
|
||||
s32 beat;
|
||||
} tic_jump_command;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
tic80_gamepads previous;
|
||||
tic80_gamepads now;
|
||||
|
||||
u32 holds[sizeof(tic80_gamepads) * BITS_IN_BYTE];
|
||||
} gamepads;
|
||||
|
||||
struct
|
||||
{
|
||||
tic80_keyboard previous;
|
||||
tic80_keyboard now;
|
||||
|
||||
u32 holds[tic_keys_count];
|
||||
} keyboard;
|
||||
|
||||
struct
|
||||
{
|
||||
tic_sound_register_data left[TIC_SOUND_CHANNELS];
|
||||
tic_sound_register_data right[TIC_SOUND_CHANNELS];
|
||||
} registers;
|
||||
|
||||
struct
|
||||
{
|
||||
tic_sound_register registers[TIC_SOUND_CHANNELS];
|
||||
tic_stereo_volume stereo;
|
||||
} sound_ringbuf[TIC_SOUND_RINGBUF_LEN];
|
||||
|
||||
u32 sound_ringbuf_head;
|
||||
u32 sound_ringbuf_tail;
|
||||
|
||||
struct
|
||||
{
|
||||
tic_channel_data channels[TIC_SOUND_CHANNELS];
|
||||
} sfx;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 ticks;
|
||||
tic_channel_data channels[TIC_SOUND_CHANNELS];
|
||||
tic_command_data commands[TIC_SOUND_CHANNELS];
|
||||
tic_sfx_pos sfxpos[TIC_SOUND_CHANNELS];
|
||||
tic_jump_command jump;
|
||||
s32 tempo;
|
||||
s32 speed;
|
||||
} music;
|
||||
|
||||
tic_tick tick;
|
||||
tic_blit_callback callback;
|
||||
|
||||
u32 synced;
|
||||
|
||||
struct
|
||||
{
|
||||
s32 id;
|
||||
tic_vram mem;
|
||||
} vbank;
|
||||
|
||||
struct ClipRect
|
||||
{
|
||||
s32 l, t, r, b;
|
||||
} clip;
|
||||
|
||||
bool initialized;
|
||||
} tic_core_state_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_mem memory; // it should be first
|
||||
tic80_pixel_color_format screen_format;
|
||||
|
||||
void* currentVM;
|
||||
const tic_script_config* currentScript;
|
||||
|
||||
struct
|
||||
{
|
||||
blip_buffer_t* left;
|
||||
blip_buffer_t* right;
|
||||
} blip;
|
||||
|
||||
s32 samplerate;
|
||||
tic_tick_data* data;
|
||||
tic_core_state_data state;
|
||||
|
||||
struct
|
||||
{
|
||||
tic_core_state_data state;
|
||||
tic_ram ram;
|
||||
u8 input;
|
||||
|
||||
struct
|
||||
{
|
||||
clock_t start;
|
||||
clock_t paused;
|
||||
} time;
|
||||
} pause;
|
||||
|
||||
} tic_core;
|
||||
|
||||
void tic_core_tick_io(tic_mem* memory);
|
||||
void tic_core_sound_tick_start(tic_mem* memory);
|
||||
void tic_core_sound_tick_end(tic_mem* memory);
|
||||
|
||||
// mouse cursor is the same in both modes
|
||||
// for backward compatibility
|
||||
#define OVR_COMPAT(CORE, BANK) \
|
||||
tic_api_vbank(&CORE->memory, BANK), \
|
||||
CORE->memory.ram->vram.vars.cursor = CORE->state.vbank.mem.vars.cursor
|
||||
|
||||
#define OVR(CORE) \
|
||||
s32 MACROVAR(_bank_) = CORE->state.vbank.id; \
|
||||
OVR_COMPAT(CORE, 1); \
|
||||
tic_api_cls(&CORE->memory, 0); \
|
||||
SCOPE(OVR_COMPAT(CORE, MACROVAR(_bank_)))
|
|
@ -0,0 +1,909 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "api.h"
|
||||
#include "core.h"
|
||||
#include "tilesheet.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#define TRANSPARENT_COLOR 255
|
||||
|
||||
typedef void(*PixelFunc)(tic_mem* memory, s32 x, s32 y, u8 color);
|
||||
|
||||
static tic_tilesheet getTileSheetFromSegment(tic_mem* memory, u8 segment)
|
||||
{
|
||||
u8* src;
|
||||
switch (segment) {
|
||||
case 0:
|
||||
case 1:
|
||||
src = (u8*)&memory->ram->font; break;
|
||||
default:
|
||||
src = (u8*)&memory->ram->tiles.data; break;
|
||||
}
|
||||
|
||||
return tic_tilesheet_get(segment, src);
|
||||
}
|
||||
|
||||
static u8* getPalette(tic_mem* tic, u8* colors, u8 count)
|
||||
{
|
||||
static u8 mapping[TIC_PALETTE_SIZE];
|
||||
for (s32 i = 0; i < TIC_PALETTE_SIZE; i++) mapping[i] = tic_tool_peek4(tic->ram->vram.mapping, i);
|
||||
for (s32 i = 0; i < count; i++) mapping[colors[i]] = TRANSPARENT_COLOR;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static inline u8 mapColor(tic_mem* tic, u8 color)
|
||||
{
|
||||
return tic_tool_peek4(tic->ram->vram.mapping, color & 0xf);
|
||||
}
|
||||
|
||||
static inline void setPixel(tic_core* core, s32 x, s32 y, u8 color)
|
||||
{
|
||||
const tic_vram* vram = &core->memory.ram->vram;
|
||||
|
||||
if (x < core->state.clip.l || y < core->state.clip.t || x >= core->state.clip.r || y >= core->state.clip.b) return;
|
||||
|
||||
tic_api_poke4((tic_mem*)core, y * TIC80_WIDTH + x, color);
|
||||
}
|
||||
|
||||
static inline void setPixelFast(tic_core* core, s32 x, s32 y, u8 color)
|
||||
{
|
||||
// does not do any CLIP checking, the caller needs to do that first
|
||||
tic_api_poke4((tic_mem*)core, y * TIC80_WIDTH + x, color);
|
||||
}
|
||||
|
||||
static u8 getPixel(tic_core* core, s32 x, s32 y)
|
||||
{
|
||||
return tic_api_peek4((tic_mem*)core, y * TIC80_WIDTH + x);
|
||||
}
|
||||
|
||||
#define EARLY_CLIP(x, y, width, height) \
|
||||
( \
|
||||
(((y)+(height)-1) < core->state.clip.t) \
|
||||
|| (((x)+(width)-1) < core->state.clip.l) \
|
||||
|| ((y) >= core->state.clip.b) \
|
||||
|| ((x) >= core->state.clip.r) \
|
||||
)
|
||||
|
||||
static void drawHLine(tic_core* core, s32 x, s32 y, s32 width, u8 color)
|
||||
{
|
||||
const tic_vram* vram = &core->memory.ram->vram;
|
||||
|
||||
if (y < core->state.clip.t || core->state.clip.b <= y) return;
|
||||
|
||||
s32 xl = MAX(x, core->state.clip.l);
|
||||
s32 xr = MIN(x + width, core->state.clip.r);
|
||||
s32 start = y * TIC80_WIDTH;
|
||||
|
||||
for(s32 i = start + xl, end = start + xr; i < end; ++i)
|
||||
tic_api_poke4((tic_mem*)core, i, color);
|
||||
}
|
||||
|
||||
static void drawVLine(tic_core* core, s32 x, s32 y, s32 height, u8 color)
|
||||
{
|
||||
const tic_vram* vram = &core->memory.ram->vram;
|
||||
|
||||
if (x < core->state.clip.l || core->state.clip.r <= x) return;
|
||||
|
||||
s32 yl = y < 0 ? 0 : y;
|
||||
s32 yr = y + height >= TIC80_HEIGHT ? TIC80_HEIGHT : y + height;
|
||||
|
||||
for (s32 i = yl; i < yr; ++i)
|
||||
setPixel(core, x, i, color);
|
||||
}
|
||||
|
||||
static void drawRect(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color)
|
||||
{
|
||||
for (s32 i = y; i < y + height; ++i)
|
||||
drawHLine(core, x, i, width, color);
|
||||
}
|
||||
|
||||
static void drawRectBorder(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color)
|
||||
{
|
||||
drawHLine(core, x, y, width, color);
|
||||
drawHLine(core, x, y + height - 1, width, color);
|
||||
|
||||
drawVLine(core, x, y, height, color);
|
||||
drawVLine(core, x + width - 1, y, height, color);
|
||||
}
|
||||
|
||||
#define DRAW_TILE_BODY(X, Y) do {\
|
||||
for(s32 py=sy; py < ey; py++, y++) \
|
||||
{ \
|
||||
s32 xx = x; \
|
||||
for(s32 px=sx; px < ex; px++, xx++) \
|
||||
{ \
|
||||
u8 color = mapping[tic_tilesheet_gettilepix(tile, (X), (Y))];\
|
||||
if(color != TRANSPARENT_COLOR) setPixelFast(core, xx, y, color); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define REVERT(X) (TIC_SPRITESIZE - 1 - (X))
|
||||
|
||||
static void drawTile(tic_core* core, tic_tileptr* tile, s32 x, s32 y, u8* colors, s32 count, s32 scale, tic_flip flip, tic_rotate rotate)
|
||||
{
|
||||
const tic_vram* vram = &core->memory.ram->vram;
|
||||
u8* mapping = getPalette(&core->memory, colors, count);
|
||||
|
||||
rotate &= 0b11;
|
||||
u32 orientation = flip & 0b11;
|
||||
|
||||
if (rotate == tic_90_rotate) orientation ^= 0b001;
|
||||
else if (rotate == tic_180_rotate) orientation ^= 0b011;
|
||||
else if (rotate == tic_270_rotate) orientation ^= 0b010;
|
||||
if (rotate == tic_90_rotate || rotate == tic_270_rotate) orientation |= 0b100;
|
||||
|
||||
if (scale == 1) {
|
||||
// the most common path
|
||||
s32 sx, sy, ex, ey;
|
||||
sx = core->state.clip.l - x; if (sx < 0) sx = 0;
|
||||
sy = core->state.clip.t - y; if (sy < 0) sy = 0;
|
||||
ex = core->state.clip.r - x; if (ex > TIC_SPRITESIZE) ex = TIC_SPRITESIZE;
|
||||
ey = core->state.clip.b - y; if (ey > TIC_SPRITESIZE) ey = TIC_SPRITESIZE;
|
||||
y += sy;
|
||||
x += sx;
|
||||
switch (orientation) {
|
||||
case 0b100: DRAW_TILE_BODY(py, px); break;
|
||||
case 0b110: DRAW_TILE_BODY(REVERT(py), px); break;
|
||||
case 0b101: DRAW_TILE_BODY(py, REVERT(px)); break;
|
||||
case 0b111: DRAW_TILE_BODY(REVERT(py), REVERT(px)); break;
|
||||
case 0b000: DRAW_TILE_BODY(px, py); break;
|
||||
case 0b010: DRAW_TILE_BODY(px, REVERT(py)); break;
|
||||
case 0b001: DRAW_TILE_BODY(REVERT(px), py); break;
|
||||
case 0b011: DRAW_TILE_BODY(REVERT(px), REVERT(py)); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (EARLY_CLIP(x, y, TIC_SPRITESIZE * scale, TIC_SPRITESIZE * scale)) return;
|
||||
|
||||
for (s32 py = 0; py < TIC_SPRITESIZE; py++, y += scale)
|
||||
{
|
||||
s32 xx = x;
|
||||
for (s32 px = 0; px < TIC_SPRITESIZE; px++, xx += scale)
|
||||
{
|
||||
s32 ix = orientation & 0b001 ? TIC_SPRITESIZE - px - 1 : px;
|
||||
s32 iy = orientation & 0b010 ? TIC_SPRITESIZE - py - 1 : py;
|
||||
if (orientation & 0b100) {
|
||||
s32 tmp = ix; ix = iy; iy = tmp;
|
||||
}
|
||||
u8 color = mapping[tic_tilesheet_gettilepix(tile, ix, iy)];
|
||||
if (color != TRANSPARENT_COLOR) drawRect(core, xx, y, scale, scale, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef DRAW_TILE_BODY
|
||||
#undef REVERT
|
||||
|
||||
static void drawSprite(tic_core* core, s32 index, s32 x, s32 y, s32 w, s32 h, u8* colors, s32 count, s32 scale, tic_flip flip, tic_rotate rotate)
|
||||
{
|
||||
const tic_vram* vram = &core->memory.ram->vram;
|
||||
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
rotate &= 0b11;
|
||||
flip &= 0b11;
|
||||
|
||||
tic_tilesheet sheet = getTileSheetFromSegment(&core->memory, core->memory.ram->vram.blit.segment);
|
||||
if (w == 1 && h == 1) {
|
||||
tic_tileptr tile = tic_tilesheet_gettile(&sheet, index, false);
|
||||
drawTile(core, &tile, x, y, colors, count, scale, flip, rotate);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 step = TIC_SPRITESIZE * scale;
|
||||
s32 cols = sheet.segment->sheet_width;
|
||||
|
||||
const tic_flip vert_horz_flip = tic_horz_flip | tic_vert_flip;
|
||||
|
||||
if (EARLY_CLIP(x, y, w * step, h * step)) return;
|
||||
|
||||
for (s32 i = 0; i < w; i++)
|
||||
{
|
||||
for (s32 j = 0; j < h; j++)
|
||||
{
|
||||
s32 mx = i;
|
||||
s32 my = j;
|
||||
|
||||
if (flip == tic_horz_flip || flip == vert_horz_flip) mx = w - 1 - i;
|
||||
if (flip == tic_vert_flip || flip == vert_horz_flip) my = h - 1 - j;
|
||||
|
||||
if (rotate == tic_180_rotate)
|
||||
{
|
||||
mx = w - 1 - mx;
|
||||
my = h - 1 - my;
|
||||
}
|
||||
else if (rotate == tic_90_rotate)
|
||||
{
|
||||
if (flip == tic_no_flip || flip == vert_horz_flip) my = h - 1 - my;
|
||||
else mx = w - 1 - mx;
|
||||
}
|
||||
else if (rotate == tic_270_rotate)
|
||||
{
|
||||
if (flip == tic_no_flip || flip == vert_horz_flip) mx = w - 1 - mx;
|
||||
else my = h - 1 - my;
|
||||
}
|
||||
|
||||
enum { Cols = TIC_SPRITESHEET_SIZE / TIC_SPRITESIZE };
|
||||
|
||||
|
||||
tic_tileptr tile = tic_tilesheet_gettile(&sheet, index + mx + my * cols, false);
|
||||
if (rotate == 0 || rotate == 2)
|
||||
drawTile(core, &tile, x + i * step, y + j * step, colors, count, scale, flip, rotate);
|
||||
else
|
||||
drawTile(core, &tile, x + j * step, y + i * step, colors, count, scale, flip, rotate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawMap(tic_core* core, const tic_map* src, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8* colors, s32 count, s32 scale, RemapFunc remap, void* data)
|
||||
{
|
||||
const s32 size = TIC_SPRITESIZE * scale;
|
||||
|
||||
tic_tilesheet sheet = getTileSheetFromSegment(&core->memory, core->memory.ram->vram.blit.segment);
|
||||
|
||||
for (s32 j = y, jj = sy; j < y + height; j++, jj += size)
|
||||
for (s32 i = x, ii = sx; i < x + width; i++, ii += size)
|
||||
{
|
||||
s32 mi = i;
|
||||
s32 mj = j;
|
||||
|
||||
while (mi < 0) mi += TIC_MAP_WIDTH;
|
||||
while (mj < 0) mj += TIC_MAP_HEIGHT;
|
||||
while (mi >= TIC_MAP_WIDTH) mi -= TIC_MAP_WIDTH;
|
||||
while (mj >= TIC_MAP_HEIGHT) mj -= TIC_MAP_HEIGHT;
|
||||
|
||||
s32 index = mi + mj * TIC_MAP_WIDTH;
|
||||
RemapResult retile = { *(src->data + index), tic_no_flip, tic_no_rotate };
|
||||
|
||||
if (remap)
|
||||
remap(data, mi, mj, &retile);
|
||||
|
||||
tic_tileptr tile = tic_tilesheet_gettile(&sheet, retile.index, true);
|
||||
drawTile(core, &tile, ii, jj, colors, count, scale, retile.flip, retile.rotate);
|
||||
}
|
||||
}
|
||||
|
||||
static s32 drawChar(tic_core* core, tic_tileptr* font_char, s32 x, s32 y, s32 scale, bool fixed, u8* mapping)
|
||||
{
|
||||
const tic_vram* vram = &core->memory.ram->vram;
|
||||
|
||||
enum { Size = TIC_SPRITESIZE };
|
||||
|
||||
s32 j = 0, start = 0, end = Size;
|
||||
|
||||
if (!fixed) {
|
||||
for (s32 i = 0; i < Size; i++) {
|
||||
for (j = 0; j < Size; j++)
|
||||
if (mapping[tic_tilesheet_gettilepix(font_char, i, j)] != TRANSPARENT_COLOR) break;
|
||||
if (j < Size) break; else start++;
|
||||
}
|
||||
for (s32 i = Size - 1; i >= start; i--) {
|
||||
for (j = 0; j < Size; j++)
|
||||
if (mapping[tic_tilesheet_gettilepix(font_char, i, j)] != TRANSPARENT_COLOR) break;
|
||||
if (j < Size) break; else end--;
|
||||
}
|
||||
}
|
||||
s32 width = end - start;
|
||||
|
||||
if (EARLY_CLIP(x, y, Size * scale, Size * scale)) return width;
|
||||
|
||||
s32 colStart = start, colStep = 1, rowStart = 0, rowStep = 1;
|
||||
|
||||
for (s32 i = 0, col = colStart, xs = x; i < width; i++, col += colStep, xs += scale)
|
||||
{
|
||||
for (s32 j = 0, row = rowStart, ys = y; j < Size; j++, row += rowStep, ys += scale)
|
||||
{
|
||||
u8 color = tic_tilesheet_gettilepix(font_char, col, row);
|
||||
if (mapping[color] != TRANSPARENT_COLOR)
|
||||
drawRect(core, xs, ys, scale, scale, mapping[color]);
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
static s32 drawText(tic_core* core, tic_tilesheet* font_face, const char* text, s32 x, s32 y, s32 width, s32 height, bool fixed, u8* mapping, s32 scale, bool alt)
|
||||
{
|
||||
s32 pos = x;
|
||||
s32 MAX = x;
|
||||
char sym = 0;
|
||||
|
||||
while ((sym = *text++))
|
||||
{
|
||||
if (sym == '\n')
|
||||
{
|
||||
if (pos > MAX)
|
||||
MAX = pos;
|
||||
|
||||
pos = x;
|
||||
y += height * scale;
|
||||
}
|
||||
else {
|
||||
tic_tileptr font_char = tic_tilesheet_gettile(font_face, alt * TIC_FONT_CHARS + sym, true);
|
||||
s32 size = drawChar(core, &font_char, pos, y, scale, fixed, mapping);
|
||||
pos += ((!fixed && size) ? size + 1 : width) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
return pos > MAX ? pos - x : MAX - x;
|
||||
}
|
||||
|
||||
void tic_api_clip(tic_mem* memory, s32 x, s32 y, s32 width, s32 height)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
tic_vram* vram = &memory->ram->vram;
|
||||
|
||||
core->state.clip.l = x;
|
||||
core->state.clip.t = y;
|
||||
core->state.clip.r = x + width;
|
||||
core->state.clip.b = y + height;
|
||||
|
||||
if (core->state.clip.l < 0) core->state.clip.l = 0;
|
||||
if (core->state.clip.t < 0) core->state.clip.t = 0;
|
||||
if (core->state.clip.r > TIC80_WIDTH) core->state.clip.r = TIC80_WIDTH;
|
||||
if (core->state.clip.b > TIC80_HEIGHT) core->state.clip.b = TIC80_HEIGHT;
|
||||
}
|
||||
|
||||
void tic_api_rect(tic_mem* memory, s32 x, s32 y, s32 width, s32 height, u8 color)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
drawRect(core, x, y, width, height, mapColor(memory, color));
|
||||
}
|
||||
|
||||
static double ZBuffer[TIC80_WIDTH * TIC80_HEIGHT];
|
||||
|
||||
void tic_api_cls(tic_mem* tic, u8 color)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
tic_vram* vram = &tic->ram->vram;
|
||||
|
||||
static const struct ClipRect EmptyClip = { 0, 0, TIC80_WIDTH, TIC80_HEIGHT };
|
||||
|
||||
if (MEMCMP(core->state.clip, EmptyClip))
|
||||
{
|
||||
memset(&vram->screen, (color & 0xf) | (color << TIC_PALETTE_BPP), sizeof(tic_screen));
|
||||
ZEROMEM(ZBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(s32 y = core->state.clip.t, start = y * TIC80_WIDTH; y < core->state.clip.b; ++y, start += TIC80_WIDTH)
|
||||
for(s32 x = core->state.clip.l, pixel = start + x; x < core->state.clip.r; ++x, ++pixel)
|
||||
{
|
||||
tic_api_poke4(tic, pixel, color);
|
||||
ZBuffer[pixel] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 tic_api_font(tic_mem* memory, const char* text, s32 x, s32 y, u8* trans_colors, u8 trans_count, s32 w, s32 h, bool fixed, s32 scale, bool alt)
|
||||
{
|
||||
u8* mapping = getPalette(memory, trans_colors, trans_count);
|
||||
|
||||
// Compatibility : flip top and bottom of the spritesheet
|
||||
// to preserve tic_api_font's default target
|
||||
u8 segment = memory->ram->vram.blit.segment >> 1;
|
||||
u8 flipmask = 1; while (segment >>= 1) flipmask <<= 1;
|
||||
|
||||
tic_tilesheet font_face = getTileSheetFromSegment(memory, memory->ram->vram.blit.segment ^ flipmask);
|
||||
return drawText((tic_core*)memory, &font_face, text, x, y, w, h, fixed, mapping, scale, alt);
|
||||
}
|
||||
|
||||
s32 tic_api_print(tic_mem* memory, const char* text, s32 x, s32 y, u8 color, bool fixed, s32 scale, bool alt)
|
||||
{
|
||||
u8 mapping[] = { 255, color };
|
||||
tic_tilesheet font_face = getTileSheetFromSegment(memory, 1);
|
||||
|
||||
const tic_font_data* font = alt ? &memory->ram->font.alt : &memory->ram->font.regular;
|
||||
s32 width = font->width;
|
||||
|
||||
// Compatibility : print uses reduced width for non-fixed space
|
||||
if (!fixed) width -= 2;
|
||||
return drawText((tic_core*)memory, &font_face, text, x, y, width, font->height, fixed, mapping, scale, alt);
|
||||
}
|
||||
|
||||
void tic_api_spr(tic_mem* memory, s32 index, s32 x, s32 y, s32 w, s32 h, u8* trans_colors, u8 trans_count, s32 scale, tic_flip flip, tic_rotate rotate)
|
||||
{
|
||||
drawSprite((tic_core*)memory, index, x, y, w, h, trans_colors, trans_count, scale, flip, rotate);
|
||||
}
|
||||
|
||||
static inline u8* getFlag(tic_mem* memory, s32 index, u8 flag)
|
||||
{
|
||||
static u8 stub = 0;
|
||||
if (index >= TIC_FLAGS || flag >= BITS_IN_BYTE)
|
||||
return &stub;
|
||||
|
||||
return memory->ram->flags.data + index;
|
||||
}
|
||||
|
||||
bool tic_api_fget(tic_mem* memory, s32 index, u8 flag)
|
||||
{
|
||||
return *getFlag(memory, index, flag) & (1 << flag);
|
||||
}
|
||||
|
||||
void tic_api_fset(tic_mem* memory, s32 index, u8 flag, bool value)
|
||||
{
|
||||
if (value)
|
||||
*getFlag(memory, index, flag) |= (1 << flag);
|
||||
else
|
||||
*getFlag(memory, index, flag) &= ~(1 << flag);
|
||||
}
|
||||
|
||||
u8 tic_api_pix(tic_mem* memory, s32 x, s32 y, u8 color, bool get)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
if (get) return getPixel(core, x, y);
|
||||
|
||||
setPixel(core, x, y, mapColor(memory, color));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tic_api_rectb(tic_mem* memory, s32 x, s32 y, s32 width, s32 height, u8 color)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
drawRectBorder(core, x, y, width, height, mapColor(memory, color));
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
s16 Left[TIC80_HEIGHT];
|
||||
s16 Right[TIC80_HEIGHT];
|
||||
} SidesBuffer;
|
||||
|
||||
static void initSidesBuffer()
|
||||
{
|
||||
for (s32 i = 0; i < COUNT_OF(SidesBuffer.Left); i++)
|
||||
SidesBuffer.Left[i] = TIC80_WIDTH, SidesBuffer.Right[i] = -1;
|
||||
}
|
||||
|
||||
static void setSidePixel(s32 x, s32 y)
|
||||
{
|
||||
if (y >= 0 && y < TIC80_HEIGHT)
|
||||
{
|
||||
if (x < SidesBuffer.Left[y]) SidesBuffer.Left[y] = x;
|
||||
if (x > SidesBuffer.Right[y]) SidesBuffer.Right[y] = x;
|
||||
}
|
||||
}
|
||||
|
||||
static void drawEllipse(tic_mem* memory, s32 x0, s32 y0, s32 x1, s32 y1, u8 color, PixelFunc pix)
|
||||
{
|
||||
s64 a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1; /* values of diameter */
|
||||
s64 dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */
|
||||
s64 err = dx + dy + b1 * a * a, e2; /* error of 1.step */
|
||||
|
||||
if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped pos32s */
|
||||
if (y0 > y1) y0 = y1; /* .. exchange them */
|
||||
y0 += (b + 1) / 2; y1 = y0 - b1; /* starting pixel */
|
||||
a *= 8 * a; b1 = 8 * b * b;
|
||||
|
||||
do
|
||||
{
|
||||
pix(memory, x1, y0, color); /* I. Quadrant */
|
||||
pix(memory, x0, y0, color); /* II. Quadrant */
|
||||
pix(memory, x0, y1, color); /* III. Quadrant */
|
||||
pix(memory, x1, y1, color); /* IV. Quadrant */
|
||||
e2 = 2 * err;
|
||||
if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
|
||||
if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */
|
||||
} while (x0 <= x1);
|
||||
|
||||
while (y0-y1 < b)
|
||||
{ /* too early stop of flat ellipses a=1 */
|
||||
pix(memory, x0 - 1, y0, color); /* -> finish tip of ellipse */
|
||||
pix(memory, x1 + 1, y0++, color);
|
||||
pix(memory, x0 - 1, y1, color);
|
||||
pix(memory, x1 + 1, y1--, color);
|
||||
}
|
||||
}
|
||||
|
||||
static void setElliPixel(tic_mem* tic, s32 x, s32 y, u8 color)
|
||||
{
|
||||
setPixel((tic_core*)tic, x, y, color);
|
||||
}
|
||||
|
||||
static void setElliSide(tic_mem* tic, s32 x, s32 y, u8 color)
|
||||
{
|
||||
setSidePixel(x, y);
|
||||
}
|
||||
|
||||
static void drawSidesBuffer(tic_mem* memory, s32 y0, s32 y1, u8 color)
|
||||
{
|
||||
tic_vram* vram = &memory->ram->vram;
|
||||
|
||||
tic_core* core = (tic_core*)memory;
|
||||
s32 yt = MAX(core->state.clip.t, y0);
|
||||
s32 yb = MIN(core->state.clip.b, y1 + 1);
|
||||
u8 final_color = mapColor(&core->memory, color);
|
||||
for (s32 y = yt; y < yb; y++)
|
||||
{
|
||||
s32 xl = MAX(SidesBuffer.Left[y], core->state.clip.l);
|
||||
s32 xr = MIN(SidesBuffer.Right[y] + 1, core->state.clip.r);
|
||||
s32 start = y * TIC80_WIDTH;
|
||||
|
||||
for(s32 i = start + xl, end = start + xr; i < end; ++i)
|
||||
tic_api_poke4(memory, i, color);
|
||||
}
|
||||
}
|
||||
|
||||
void tic_api_circ(tic_mem* memory, s32 x, s32 y, s32 r, u8 color)
|
||||
{
|
||||
initSidesBuffer();
|
||||
drawEllipse(memory, x - r, y - r, x + r, y + r, 0, setElliSide);
|
||||
drawSidesBuffer(memory, y - r, y + r + 1, color);
|
||||
}
|
||||
|
||||
void tic_api_circb(tic_mem* memory, s32 x, s32 y, s32 r, u8 color)
|
||||
{
|
||||
drawEllipse(memory, x - r, y - r, x + r, y + r, mapColor(memory, color), setElliPixel);
|
||||
}
|
||||
|
||||
void tic_api_elli(tic_mem* memory, s32 x, s32 y, s32 a, s32 b, u8 color)
|
||||
{
|
||||
initSidesBuffer();
|
||||
drawEllipse(memory, x - a, y - b, x + a, y + b, 0, setElliSide);
|
||||
drawSidesBuffer(memory, y - b, y + b + 1, color);
|
||||
}
|
||||
|
||||
void tic_api_ellib(tic_mem* memory, s32 x, s32 y, s32 a, s32 b, u8 color)
|
||||
{
|
||||
drawEllipse(memory, x - a, y - b, x + a, y + b, mapColor(memory, color), setElliPixel);
|
||||
}
|
||||
|
||||
static inline float initLine(float *x0, float *x1, float *y0, float *y1)
|
||||
{
|
||||
if (*y0 > *y1)
|
||||
{
|
||||
SWAP(*x0, *x1, float);
|
||||
SWAP(*y0, *y1, float);
|
||||
}
|
||||
|
||||
float t = (*x1 - *x0) / (*y1 - *y0);
|
||||
|
||||
if(*y0 < 0) *x0 -= *y0 * t, *y0 = 0;
|
||||
if(*y1 > TIC80_WIDTH) *x1 += (TIC80_WIDTH - *y0) * t, *y1 = TIC80_WIDTH;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void drawLine(tic_mem* tic, float x0, float y0, float x1, float y1, u8 color)
|
||||
{
|
||||
if(fabs(x0 - x1) < fabs(y0 - y1))
|
||||
for (float t = initLine(&x0, &x1, &y0, &y1); y0 < y1; y0++, x0 += t)
|
||||
setPixel((tic_core*)tic, x0, y0, color);
|
||||
else
|
||||
for (float t = initLine(&y0, &y1, &x0, &x1); x0 < x1; x0++, y0 += t)
|
||||
setPixel((tic_core*)tic, x0, y0, color);
|
||||
|
||||
setPixel((tic_core*)tic, x1, y1, color);
|
||||
}
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
double x, y;
|
||||
};
|
||||
|
||||
double d[2];
|
||||
} Vec2;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
double x, y, z;
|
||||
};
|
||||
|
||||
double d[3];
|
||||
} Vec3;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void* data;
|
||||
const Vec2* v[3];
|
||||
Vec3 w;
|
||||
} ShaderAttr;
|
||||
|
||||
typedef tic_color(*PixelShader)(const ShaderAttr* a, s32 pixel);
|
||||
|
||||
static inline double edgeFn(const Vec2* a, const Vec2* b, const Vec2* c)
|
||||
{
|
||||
return (b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x);
|
||||
}
|
||||
|
||||
static void drawTri(tic_mem* tic, const Vec2* v0, const Vec2* v1, const Vec2* v2, PixelShader shader, void* data)
|
||||
{
|
||||
ShaderAttr a = {data, v0, v1, v2};
|
||||
|
||||
tic_core* core = (tic_core*)tic;
|
||||
const struct ClipRect* clip = &core->state.clip;
|
||||
|
||||
tic_point min = {floor(MIN3(a.v[0]->x, a.v[1]->x, a.v[2]->x)), floor(MIN3(a.v[0]->y, a.v[1]->y, a.v[2]->y))};
|
||||
tic_point max = {ceil(MAX3(a.v[0]->x, a.v[1]->x, a.v[2]->x)), ceil(MAX3(a.v[0]->y, a.v[1]->y, a.v[2]->y))};
|
||||
|
||||
min.x = MAX(min.x, clip->l);
|
||||
min.y = MAX(min.y, clip->t);
|
||||
max.x = MIN(max.x, clip->r);
|
||||
max.y = MIN(max.y, clip->b);
|
||||
|
||||
if(min.x >= max.x || min.y >= max.y) return;
|
||||
|
||||
double area = edgeFn(a.v[0], a.v[1], a.v[2]);
|
||||
if((s32)floor(area) == 0) return;
|
||||
if(area < 0.0)
|
||||
{
|
||||
SWAP(a.v[1], a.v[2], const Vec2*);
|
||||
area = -area;
|
||||
}
|
||||
|
||||
Vec2 d[3];
|
||||
Vec3 s;
|
||||
|
||||
for(s32 i = 0; i != COUNT_OF(s.d); ++i)
|
||||
{
|
||||
// pixel center
|
||||
const double Center = 0.5 - FLT_EPSILON;
|
||||
Vec2 p = {min.x + Center, min.y + Center};
|
||||
|
||||
s32 c = (i + 1) % 3, n = (i + 2) % 3;
|
||||
|
||||
d[i].x = (a.v[c]->y - a.v[n]->y) / area;
|
||||
d[i].y = (a.v[n]->x - a.v[c]->x) / area;
|
||||
s.d[i] = edgeFn(a.v[c], a.v[n], &p) / area;
|
||||
}
|
||||
|
||||
for(s32 y = min.y, start = min.y * TIC80_WIDTH + min.x; y < max.y; ++y, start += TIC80_WIDTH)
|
||||
{
|
||||
for(s32 i = 0; i != COUNT_OF(a.w.d); ++i)
|
||||
a.w.d[i] = s.d[i];
|
||||
|
||||
for(s32 x = min.x, pixel = start; x < max.x; ++x, ++pixel)
|
||||
{
|
||||
if(a.w.x > -DBL_EPSILON && a.w.y > -DBL_EPSILON && a.w.z > -DBL_EPSILON)
|
||||
{
|
||||
u8 color = shader(&a, pixel);
|
||||
if(color != TRANSPARENT_COLOR)
|
||||
tic_api_poke4(tic, pixel, color);
|
||||
}
|
||||
|
||||
for(s32 i = 0; i != COUNT_OF(a.w.d); ++i)
|
||||
a.w.d[i] += d[i].x;
|
||||
}
|
||||
|
||||
for(s32 i = 0; i != COUNT_OF(s.d); ++i)
|
||||
s.d[i] += d[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
static tic_color triColorShader(const ShaderAttr* a, s32 pixel){return *(u8*)a->data;}
|
||||
|
||||
void tic_api_tri(tic_mem* tic, float x1, float y1, float x2, float y2, float x3, float y3, u8 color)
|
||||
{
|
||||
color = mapColor(tic, color);
|
||||
drawTri(tic,
|
||||
&(Vec2){x1, y1},
|
||||
&(Vec2){x2, y2},
|
||||
&(Vec2){x3, y3},
|
||||
triColorShader, &color);
|
||||
}
|
||||
|
||||
void tic_api_trib(tic_mem* tic, float x1, float y1, float x2, float y2, float x3, float y3, u8 color)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
u8 finalColor = mapColor(tic, color);
|
||||
|
||||
drawLine(tic, x1, y1, x2, y2, finalColor);
|
||||
drawLine(tic, x2, y2, x3, y3, finalColor);
|
||||
drawLine(tic, x3, y3, x1, y1, finalColor);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Vec2 _;
|
||||
Vec3 d;
|
||||
}TexVert;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_tilesheet sheet;
|
||||
u8* mapping;
|
||||
const u8* map;
|
||||
const tic_vram* vram;
|
||||
bool depth;
|
||||
} TexData;
|
||||
|
||||
static inline bool shaderStart(const ShaderAttr* a, Vec3* vars, s32 pixel)
|
||||
{
|
||||
TexData* data = a->data;
|
||||
|
||||
if(data->depth)
|
||||
{
|
||||
vars->z = 0;
|
||||
for(s32 i = 0; i != COUNT_OF(a->v); ++i)
|
||||
{
|
||||
const TexVert* t = (TexVert*)a->v[i];
|
||||
vars->z += a->w.d[i] * t->d.z;
|
||||
}
|
||||
|
||||
if(ZBuffer[pixel] < vars->z);
|
||||
else return false;
|
||||
}
|
||||
|
||||
vars->x = vars->y = 0;
|
||||
for(s32 i = 0; i != COUNT_OF(a->v); ++i)
|
||||
{
|
||||
const TexVert* t = (TexVert*)a->v[i];
|
||||
vars->x += a->w.d[i] * t->d.x;
|
||||
vars->y += a->w.d[i] * t->d.y;
|
||||
}
|
||||
|
||||
if(data->depth)
|
||||
vars->x /= vars->z,
|
||||
vars->y /= vars->z;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline tic_color shaderEnd(const ShaderAttr* a, const Vec3* vars, s32 pixel, tic_color color)
|
||||
{
|
||||
TexData* data = a->data;
|
||||
|
||||
if(data->depth && color != TRANSPARENT_COLOR)
|
||||
ZBuffer[pixel] = vars->z;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
static tic_color triTexMapShader(const ShaderAttr* a, s32 pixel)
|
||||
{
|
||||
TexData* data = a->data;
|
||||
|
||||
Vec3 vars;
|
||||
if(!shaderStart(a, &vars, pixel))
|
||||
return TRANSPARENT_COLOR;
|
||||
|
||||
enum { MapWidth = TIC_MAP_WIDTH * TIC_SPRITESIZE, MapHeight = TIC_MAP_HEIGHT * TIC_SPRITESIZE,
|
||||
WMask = TIC_SPRITESIZE - 1, HMask = TIC_SPRITESIZE - 1 };
|
||||
|
||||
s32 iu = tic_modulo(vars.x, MapWidth);
|
||||
s32 iv = tic_modulo(vars.y, MapHeight);
|
||||
|
||||
u8 idx = data->map[(iv >> 3) * TIC_MAP_WIDTH + (iu >> 3)];
|
||||
tic_tileptr tile = tic_tilesheet_gettile(&data->sheet, idx, true);
|
||||
|
||||
return shaderEnd(a, &vars, pixel, data->mapping[tic_tilesheet_gettilepix(&tile, iu & WMask, iv & HMask)]);
|
||||
}
|
||||
|
||||
static tic_color triTexTileShader(const ShaderAttr* a, s32 pixel)
|
||||
{
|
||||
TexData* data = a->data;
|
||||
|
||||
Vec3 vars;
|
||||
if(!shaderStart(a, &vars, pixel))
|
||||
return TRANSPARENT_COLOR;
|
||||
|
||||
enum { WMask = TIC_SPRITESHEET_SIZE - 1, HMask = TIC_SPRITESHEET_SIZE * TIC_SPRITE_BANKS - 1 };
|
||||
|
||||
return shaderEnd(a, &vars, pixel, data->mapping[tic_tilesheet_getpix(&data->sheet, (s32)vars.x & WMask, (s32)vars.y & HMask)]);
|
||||
}
|
||||
|
||||
static tic_color triTexVbankShader(const ShaderAttr* a, s32 pixel)
|
||||
{
|
||||
TexData* data = a->data;
|
||||
|
||||
Vec3 vars;
|
||||
if(!shaderStart(a, &vars, pixel))
|
||||
return TRANSPARENT_COLOR;
|
||||
|
||||
s32 iu = tic_modulo(vars.x, TIC80_WIDTH);
|
||||
s32 iv = tic_modulo(vars.y, TIC80_HEIGHT);
|
||||
|
||||
return shaderEnd(a, &vars, pixel, data->mapping[tic_tool_peek4(data->vram->data, iv * TIC80_WIDTH + iu)]);
|
||||
}
|
||||
|
||||
void tic_api_ttri(tic_mem* tic,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3,
|
||||
float u1, float v1,
|
||||
float u2, float v2,
|
||||
float u3, float v3,
|
||||
tic_texture_src texsrc, u8* colors, s32 count,
|
||||
float z1, float z2, float z3, bool depth)
|
||||
{
|
||||
TexData texData =
|
||||
{
|
||||
.sheet = getTileSheetFromSegment(tic, tic->ram->vram.blit.segment),
|
||||
.mapping = getPalette(tic, colors, count),
|
||||
.map = tic->ram->map.data,
|
||||
.vram = &((tic_core*)tic)->state.vbank.mem,
|
||||
.depth = depth,
|
||||
};
|
||||
|
||||
TexVert t[] =
|
||||
{
|
||||
{x1, y1, u1, v1, z1},
|
||||
{x2, y2, u2, v2, z2},
|
||||
{x3, y3, u3, v3, z3},
|
||||
};
|
||||
|
||||
if(depth)
|
||||
for(s32 i = 0; i != COUNT_OF(t); ++i)
|
||||
t[i].d.x /= t[i].d.z,
|
||||
t[i].d.y /= t[i].d.z,
|
||||
t[i].d.z = 1.0 / t[i].d.z;
|
||||
|
||||
static const PixelShader Shaders[] =
|
||||
{
|
||||
[tic_tiles_texture] = triTexTileShader,
|
||||
[tic_map_texture] = triTexMapShader,
|
||||
[tic_vbank_texture] = triTexVbankShader,
|
||||
};
|
||||
|
||||
if(texsrc >= 0 && texsrc < COUNT_OF(Shaders))
|
||||
drawTri(tic,
|
||||
(const Vec2*)&t[0],
|
||||
(const Vec2*)&t[1],
|
||||
(const Vec2*)&t[2],
|
||||
Shaders[texsrc], &texData);
|
||||
}
|
||||
|
||||
void tic_api_map(tic_mem* memory, s32 x, s32 y, s32 width, s32 height, s32 sx, s32 sy, u8* colors, u8 count, s32 scale, RemapFunc remap, void* data)
|
||||
{
|
||||
drawMap((tic_core*)memory, &memory->ram->map, x, y, width, height, sx, sy, colors, count, scale, remap, data);
|
||||
}
|
||||
|
||||
void tic_api_mset(tic_mem* memory, s32 x, s32 y, u8 value)
|
||||
{
|
||||
if (x < 0 || x >= TIC_MAP_WIDTH || y < 0 || y >= TIC_MAP_HEIGHT) return;
|
||||
|
||||
tic_map* src = &memory->ram->map;
|
||||
*(src->data + y * TIC_MAP_WIDTH + x) = value;
|
||||
}
|
||||
|
||||
u8 tic_api_mget(tic_mem* memory, s32 x, s32 y)
|
||||
{
|
||||
if (x < 0 || x >= TIC_MAP_WIDTH || y < 0 || y >= TIC_MAP_HEIGHT) return 0;
|
||||
|
||||
const tic_map* src = &memory->ram->map;
|
||||
return *(src->data + y * TIC_MAP_WIDTH + x);
|
||||
}
|
||||
|
||||
void tic_api_line(tic_mem* memory, float x0, float y0, float x1, float y1, u8 color)
|
||||
{
|
||||
drawLine(memory, x0, y0, x1, y1, mapColor(memory, color));
|
||||
}
|
||||
|
||||
#if defined(BUILD_DEPRECATED)
|
||||
#include "draw_dep.h"
|
||||
#endif
|
|
@ -0,0 +1,209 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x, y, u, v;
|
||||
} TexVertDep;
|
||||
|
||||
static struct
|
||||
{
|
||||
s16 Left[TIC80_HEIGHT];
|
||||
s16 Right[TIC80_HEIGHT];
|
||||
s32 ULeft[TIC80_HEIGHT];
|
||||
s32 VLeft[TIC80_HEIGHT];
|
||||
} SidesBufferDep;
|
||||
|
||||
static void setSideTexPixel(s32 x, s32 y, float u, float v)
|
||||
{
|
||||
s32 yy = y;
|
||||
if (yy >= 0 && yy < TIC80_HEIGHT)
|
||||
{
|
||||
if (x < SidesBufferDep.Left[yy])
|
||||
{
|
||||
SidesBufferDep.Left[yy] = x;
|
||||
SidesBufferDep.ULeft[yy] = (s32)(u * 65536.0f);
|
||||
SidesBufferDep.VLeft[yy] = (s32)(v * 65536.0f);
|
||||
}
|
||||
if (x > SidesBufferDep.Right[yy])
|
||||
{
|
||||
SidesBufferDep.Right[yy] = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ticTexLine(tic_mem* memory, TexVertDep* v0, TexVertDep* v1)
|
||||
{
|
||||
TexVertDep* top = v0;
|
||||
TexVertDep* bot = v1;
|
||||
|
||||
if (bot->y < top->y)
|
||||
{
|
||||
top = v1;
|
||||
bot = v0;
|
||||
}
|
||||
|
||||
float dy = bot->y - top->y;
|
||||
float step_x = (bot->x - top->x);
|
||||
float step_u = (bot->u - top->u);
|
||||
float step_v = (bot->v - top->v);
|
||||
|
||||
if ((s32)dy != 0)
|
||||
{
|
||||
step_x /= dy;
|
||||
step_u /= dy;
|
||||
step_v /= dy;
|
||||
}
|
||||
|
||||
float x = top->x;
|
||||
float y = top->y;
|
||||
float u = top->u;
|
||||
float v = top->v;
|
||||
|
||||
if (y < .0f)
|
||||
{
|
||||
y = .0f - y;
|
||||
|
||||
x += step_x * y;
|
||||
u += step_u * y;
|
||||
v += step_v * y;
|
||||
|
||||
y = .0f;
|
||||
}
|
||||
|
||||
s32 botY = (s32)bot->y;
|
||||
if (botY > TIC80_HEIGHT)
|
||||
botY = TIC80_HEIGHT;
|
||||
|
||||
for (; y < botY; ++y)
|
||||
{
|
||||
setSideTexPixel((s32)x, (s32)y, u, v);
|
||||
x += step_x;
|
||||
u += step_u;
|
||||
v += step_v;
|
||||
}
|
||||
}
|
||||
|
||||
void drawTexturedTriangleDep(tic_core* core, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8* colors, s32 count)
|
||||
{
|
||||
tic_mem* memory = &core->memory;
|
||||
tic_vram* vram = &memory->ram->vram;
|
||||
|
||||
u8* mapping = getPalette(memory, colors, count);
|
||||
TexVertDep V0, V1, V2;
|
||||
|
||||
const u8* map = memory->ram->map.data;
|
||||
tic_tilesheet sheet = getTileSheetFromSegment(memory, memory->ram->vram.blit.segment);
|
||||
|
||||
V0.x = x1; V0.y = y1; V0.u = u1; V0.v = v1;
|
||||
V1.x = x2; V1.y = y2; V1.u = u2; V1.v = v2;
|
||||
V2.x = x3; V2.y = y3; V2.u = u3; V2.v = v3;
|
||||
|
||||
// calculate the slope of the surface
|
||||
// use floats here
|
||||
float denom = (V0.x - V2.x) * (V1.y - V2.y) - (V1.x - V2.x) * (V0.y - V2.y);
|
||||
if (denom == 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
float id = 1.0f / denom;
|
||||
float dudx, dvdx;
|
||||
// this is the UV slope across the surface
|
||||
dudx = ((V0.u - V2.u) * (V1.y - V2.y) - (V1.u - V2.u) * (V0.y - V2.y)) * id;
|
||||
dvdx = ((V0.v - V2.v) * (V1.y - V2.y) - (V1.v - V2.v) * (V0.y - V2.y)) * id;
|
||||
// convert to fixed
|
||||
s32 dudxs = (s32)(dudx * 65536.0f);
|
||||
s32 dvdxs = (s32)(dvdx * 65536.0f);
|
||||
// fill the buffer
|
||||
for (s32 i = 0; i < COUNT_OF(SidesBuffer.Left); i++)
|
||||
SidesBufferDep.Left[i] = TIC80_WIDTH, SidesBufferDep.Right[i] = -1;
|
||||
|
||||
// parse each line and decide where in the buffer to store them ( left or right )
|
||||
ticTexLine(memory, &V0, &V1);
|
||||
ticTexLine(memory, &V1, &V2);
|
||||
ticTexLine(memory, &V2, &V0);
|
||||
|
||||
for (s32 y = 0; y < TIC80_HEIGHT; y++)
|
||||
{
|
||||
// if it's backwards skip it
|
||||
s32 width = SidesBufferDep.Right[y] - SidesBufferDep.Left[y];
|
||||
// if it's off top or bottom , skip this line
|
||||
if ((y < core->state.clip.t) || (y > core->state.clip.b))
|
||||
width = 0;
|
||||
if (width > 0)
|
||||
{
|
||||
s32 u = SidesBufferDep.ULeft[y];
|
||||
s32 v = SidesBufferDep.VLeft[y];
|
||||
s32 left = SidesBufferDep.Left[y];
|
||||
s32 right = SidesBufferDep.Right[y];
|
||||
// check right edge, and CLAMP it
|
||||
if (right > core->state.clip.r)
|
||||
right = core->state.clip.r;
|
||||
// check left edge and offset UV's if we are off the left
|
||||
if (left < core->state.clip.l)
|
||||
{
|
||||
s32 dist = core->state.clip.l - SidesBufferDep.Left[y];
|
||||
u += dudxs * dist;
|
||||
v += dvdxs * dist;
|
||||
left = core->state.clip.l;
|
||||
}
|
||||
// are we drawing from the map . ok then at least check before the inner loop
|
||||
if (use_map == true)
|
||||
{
|
||||
for (s32 x = left; x < right; ++x)
|
||||
{
|
||||
enum { MapWidth = TIC_MAP_WIDTH * TIC_SPRITESIZE, MapHeight = TIC_MAP_HEIGHT * TIC_SPRITESIZE };
|
||||
s32 iu = (u >> 16) % MapWidth;
|
||||
s32 iv = (v >> 16) % MapHeight;
|
||||
|
||||
while (iu < 0) iu += MapWidth;
|
||||
while (iv < 0) iv += MapHeight;
|
||||
|
||||
u8 tileindex = map[(iv >> 3) * TIC_MAP_WIDTH + (iu >> 3)];
|
||||
tic_tileptr tile = tic_tilesheet_gettile(&sheet, tileindex, true);
|
||||
|
||||
u8 color = mapping[tic_tilesheet_gettilepix(&tile, iu & 7, iv & 7)];
|
||||
if (color != TRANSPARENT_COLOR)
|
||||
setPixel(core, x, y, color);
|
||||
u += dudxs;
|
||||
v += dvdxs;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// direct from tile ram
|
||||
for (s32 x = left; x < right; ++x)
|
||||
{
|
||||
enum { SheetWidth = TIC_SPRITESHEET_SIZE, SheetHeight = TIC_SPRITESHEET_SIZE * TIC_SPRITE_BANKS };
|
||||
s32 iu = (u >> 16) & (SheetWidth - 1);
|
||||
s32 iv = (v >> 16) & (SheetHeight - 1);
|
||||
|
||||
u8 color = mapping[tic_tilesheet_getpix(&sheet, iu, iv)];
|
||||
if (color != TRANSPARENT_COLOR)
|
||||
setPixel(core, x, y, color);
|
||||
u += dudxs;
|
||||
v += dvdxs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,179 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "api.h"
|
||||
#include "core.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(DINGUX) && !defined(static_assert)
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(tic80_input) == 12, "tic80_input");
|
||||
|
||||
extern bool lagged;
|
||||
extern void (*inputcb)();
|
||||
#define UNLAG() do { lagged = false; if (__builtin_expect(!!inputcb, false)) inputcb(); } while (0)
|
||||
|
||||
static bool isKeyPressed(const tic80_keyboard* input, tic_key key)
|
||||
{
|
||||
for (s32 i = 0; i < TIC80_KEY_BUFFER; i++)
|
||||
if (input->keys[i] == key)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 tic_api_btnp(tic_mem* tic, s32 index, s32 hold, s32 period)
|
||||
{
|
||||
UNLAG();
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
return (~core->state.gamepads.previous.data) & core->memory.ram->input.gamepads.data;
|
||||
}
|
||||
else if (hold < 0 || period < 0)
|
||||
{
|
||||
return ((~core->state.gamepads.previous.data) & core->memory.ram->input.gamepads.data) & (1 << index);
|
||||
}
|
||||
|
||||
tic80_gamepads previous;
|
||||
|
||||
previous.data = core->state.gamepads.holds[index] >= (u32)hold
|
||||
? period && core->state.gamepads.holds[index] % period ? core->state.gamepads.previous.data : 0
|
||||
: core->state.gamepads.previous.data;
|
||||
|
||||
return ((~previous.data) & core->memory.ram->input.gamepads.data) & (1 << index);
|
||||
}
|
||||
|
||||
u32 tic_api_btn(tic_mem* tic, s32 index)
|
||||
{
|
||||
UNLAG();
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
return core->memory.ram->input.gamepads.data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return core->memory.ram->input.gamepads.data & (1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
bool tic_api_key(tic_mem* tic, tic_key key)
|
||||
{
|
||||
UNLAG();
|
||||
return key > tic_key_unknown
|
||||
? isKeyPressed(&tic->ram->input.keyboard, key)
|
||||
: tic->ram->input.keyboard.data;
|
||||
}
|
||||
|
||||
bool tic_api_keyp(tic_mem* tic, tic_key key, s32 hold, s32 period)
|
||||
{
|
||||
UNLAG();
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
if (key > tic_key_unknown)
|
||||
{
|
||||
bool prevDown = hold >= 0 && period >= 0 && core->state.keyboard.holds[key] >= (u32)hold
|
||||
? period && core->state.keyboard.holds[key] % period
|
||||
? isKeyPressed(&core->state.keyboard.previous, key)
|
||||
: false
|
||||
: isKeyPressed(&core->state.keyboard.previous, key);
|
||||
|
||||
bool down = isKeyPressed(&tic->ram->input.keyboard, key);
|
||||
|
||||
return !prevDown && down;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < TIC80_KEY_BUFFER; i++)
|
||||
{
|
||||
tic_key key = tic->ram->input.keyboard.keys[i];
|
||||
|
||||
if (key)
|
||||
{
|
||||
bool wasPressed = false;
|
||||
|
||||
for (s32 p = 0; p < TIC80_KEY_BUFFER; p++)
|
||||
{
|
||||
if (core->state.keyboard.previous.keys[p] == key)
|
||||
{
|
||||
wasPressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wasPressed)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tic_point tic_api_mouse(tic_mem* memory)
|
||||
{
|
||||
UNLAG();
|
||||
return memory->ram->input.mouse.relative
|
||||
? (tic_point){memory->ram->input.mouse.rx, memory->ram->input.mouse.ry}
|
||||
: (tic_point){memory->ram->input.mouse.x - TIC80_OFFSET_LEFT, memory->ram->input.mouse.y - TIC80_OFFSET_TOP};
|
||||
}
|
||||
|
||||
void tic_core_tick_io(tic_mem* tic)
|
||||
{
|
||||
tic_core* core = (tic_core*)tic;
|
||||
|
||||
// process gamepads mapping
|
||||
u8* keycodes = tic->ram->mapping.data;
|
||||
for(s32 i = 0; i < sizeof(tic_mapping); ++i)
|
||||
if(keycodes[i] && (keycodes[i] > tic_key_unknown
|
||||
? isKeyPressed(&tic->ram->input.keyboard, keycodes[i])
|
||||
: tic->ram->input.keyboard.data))
|
||||
tic->ram->input.gamepads.data |= 1 << i;
|
||||
|
||||
// process gamepad
|
||||
for (s32 i = 0; i < COUNT_OF(core->state.gamepads.holds); i++)
|
||||
{
|
||||
u32 mask = 1 << i;
|
||||
u32 prevDown = core->state.gamepads.previous.data & mask;
|
||||
u32 down = tic->ram->input.gamepads.data & mask;
|
||||
|
||||
u32* hold = &core->state.gamepads.holds[i];
|
||||
if (prevDown && prevDown == down) (*hold)++;
|
||||
else *hold = 0;
|
||||
}
|
||||
|
||||
// process keyboard
|
||||
for (s32 i = 0; i < tic_keys_count; i++)
|
||||
{
|
||||
bool prevDown = isKeyPressed(&core->state.keyboard.previous, i);
|
||||
bool down = isKeyPressed(&tic->ram->input.keyboard, i);
|
||||
|
||||
u32* hold = &core->state.keyboard.holds[i];
|
||||
|
||||
if (prevDown && down) (*hold)++;
|
||||
else *hold = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#include <stddef.h>
|
||||
#include "api.h"
|
||||
|
||||
#if defined(TIC_BUILD_WITH_MRUBY)
|
||||
extern tic_script_config MRubySyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_JS)
|
||||
extern tic_script_config JsSyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined (TIC_BUILD_WITH_LUA)
|
||||
extern tic_script_config LuaSyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_MOON)
|
||||
extern tic_script_config MoonSyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_FENNEL)
|
||||
extern tic_script_config FennelSyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_SQUIRREL)
|
||||
extern tic_script_config SquirrelSyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_WREN)
|
||||
extern tic_script_config WrenSyntaxConfig;
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_WASM)
|
||||
extern tic_script_config WasmSyntaxConfig;
|
||||
#endif
|
||||
|
||||
|
||||
tic_script_config* Languages[] = {
|
||||
|
||||
#if defined (TIC_BUILD_WITH_LUA)
|
||||
&LuaSyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_MRUBY)
|
||||
&MRubySyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_JS)
|
||||
&JsSyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_MOON)
|
||||
&MoonSyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_FENNEL)
|
||||
&FennelSyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_SQUIRREL)
|
||||
&SquirrelSyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_WREN)
|
||||
&WrenSyntaxConfig,
|
||||
#endif
|
||||
|
||||
#if defined(TIC_BUILD_WITH_WASM)
|
||||
&WasmSyntaxConfig,
|
||||
#endif
|
||||
|
||||
NULL};
|
||||
|
|
@ -0,0 +1,583 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
#include "api.h"
|
||||
#include "core.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(DINGUX) && !defined(static_assert)
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
#define ENVELOPE_FREQ_SCALE 2
|
||||
#define SECONDS_PER_MINUTE 60
|
||||
#define NOTES_PER_MUNUTE (TIC80_FRAMERATE / NOTES_PER_BEAT * SECONDS_PER_MINUTE)
|
||||
#define PIANO_START 8
|
||||
|
||||
static const u16 NoteFreqs[] = { 0x10, 0x11, 0x12, 0x13, 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1c, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2c, 0x2e, 0x31, 0x34, 0x37, 0x3a, 0x3e, 0x41, 0x45, 0x49, 0x4e, 0x52, 0x57, 0x5c, 0x62, 0x68, 0x6e, 0x75, 0x7b, 0x83, 0x8b, 0x93, 0x9c, 0xa5, 0xaf, 0xb9, 0xc4, 0xd0, 0xdc, 0xe9, 0xf7, 0x106, 0x115, 0x126, 0x137, 0x14a, 0x15d, 0x172, 0x188, 0x19f, 0x1b8, 0x1d2, 0x1ee, 0x20b, 0x22a, 0x24b, 0x26e, 0x293, 0x2ba, 0x2e4, 0x310, 0x33f, 0x370, 0x3a4, 0x3dc, 0x417, 0x455, 0x497, 0x4dd, 0x527, 0x575, 0x5c8, 0x620, 0x67d, 0x6e0, 0x749, 0x7b8, 0x82d, 0x8a9, 0x92d, 0x9b9, 0xa4d, 0xaea, 0xb90, 0xc40, 0xcfa, 0xdc0, 0xe91, 0xf6f, 0x105a, 0x1153, 0x125b, 0x1372, 0x149a, 0x15d4, 0x1720, 0x1880 };
|
||||
static_assert(COUNT_OF(NoteFreqs) == NOTES * OCTAVES + PIANO_START, "count_of_freqs");
|
||||
static_assert(sizeof(tic_sound_register) == 16 + 2, "tic_sound_register");
|
||||
static_assert(sizeof(tic_sample) == 66, "tic_sample");
|
||||
static_assert(sizeof(tic_track_pattern) == 3 * MUSIC_PATTERN_ROWS, "tic_track_pattern");
|
||||
static_assert(sizeof(tic_track) == 3 * MUSIC_FRAMES + 3, "tic_track");
|
||||
static_assert(tic_music_cmd_count == 1 << MUSIC_CMD_BITS, "tic_music_cmd_count");
|
||||
static_assert(sizeof(tic_music_state) == 4, "tic_music_state_size");
|
||||
|
||||
static s32 getTempo(tic_core* core, const tic_track* track)
|
||||
{
|
||||
return core->state.music.tempo < 0
|
||||
? track->tempo + DEFAULT_TEMPO
|
||||
: core->state.music.tempo;
|
||||
}
|
||||
|
||||
static s32 getSpeed(tic_core* core, const tic_track* track)
|
||||
{
|
||||
return core->state.music.speed < 0
|
||||
? track->speed + DEFAULT_SPEED
|
||||
: core->state.music.speed;
|
||||
}
|
||||
|
||||
static s32 tick2row(tic_core* core, const tic_track* track, s32 tick)
|
||||
{
|
||||
// BPM = tempo * 6 / speed
|
||||
s32 speed = getSpeed(core, track);
|
||||
return speed
|
||||
? tick * getTempo(core, track) * DEFAULT_SPEED / speed / NOTES_PER_MUNUTE
|
||||
: 0;
|
||||
}
|
||||
|
||||
static s32 row2tick(tic_core* core, const tic_track* track, s32 row)
|
||||
{
|
||||
s32 tempo = getTempo(core, track);
|
||||
return tempo
|
||||
? row * getSpeed(core, track) * NOTES_PER_MUNUTE / tempo / DEFAULT_SPEED
|
||||
: 0;
|
||||
}
|
||||
|
||||
static inline s32 param2val(const tic_track_row* row)
|
||||
{
|
||||
return (row->param1 << 4) | row->param2;
|
||||
}
|
||||
|
||||
static void update_amp(blip_buffer_t* blip, tic_sound_register_data* data, s32 new_amp)
|
||||
{
|
||||
s32 delta = new_amp - data->amp;
|
||||
data->amp += delta;
|
||||
blip_add_delta(blip, data->time, delta);
|
||||
}
|
||||
|
||||
static inline s32 freq2period(s32 freq)
|
||||
{
|
||||
enum
|
||||
{
|
||||
MinPeriodValue = 10,
|
||||
MaxPeriodValue = 4096,
|
||||
Rate = CLOCKRATE * ENVELOPE_FREQ_SCALE / WAVE_VALUES
|
||||
};
|
||||
|
||||
if (freq == 0) return MaxPeriodValue;
|
||||
|
||||
return CLAMP(Rate / freq - 1, MinPeriodValue, MaxPeriodValue);
|
||||
}
|
||||
|
||||
static inline s32 getAmp(const tic_sound_register* reg, s32 amp)
|
||||
{
|
||||
enum { AmpMax = (u16)-1 / 2 };
|
||||
return (amp * AmpMax / MAX_VOLUME) * reg->volume / MAX_VOLUME / TIC_SOUND_CHANNELS;
|
||||
}
|
||||
|
||||
static void runEnvelope(blip_buffer_t* blip, const tic_sound_register* reg, tic_sound_register_data* data, s32 end_time, u8 volume)
|
||||
{
|
||||
s32 period = freq2period(reg->freq * ENVELOPE_FREQ_SCALE);
|
||||
|
||||
for (; data->time < end_time; data->time += period)
|
||||
{
|
||||
data->phase = (data->phase + 1) % WAVE_VALUES;
|
||||
|
||||
update_amp(blip, data, getAmp(reg, tic_tool_peek4(reg->waveform.data, data->phase) * volume / MAX_VOLUME));
|
||||
}
|
||||
}
|
||||
|
||||
static void runNoise(blip_buffer_t* blip, const tic_sound_register* reg, tic_sound_register_data* data, s32 end_time, u8 volume)
|
||||
{
|
||||
// phase is noise LFSR, which must never be zero
|
||||
if (data->phase == 0)
|
||||
data->phase = 1;
|
||||
|
||||
s32 period = freq2period(reg->freq);
|
||||
s32 fb = *reg->waveform.data ? 0x14 : 0x12000;
|
||||
|
||||
for (; data->time < end_time; data->time += period)
|
||||
{
|
||||
data->phase = ((data->phase & 1) * fb) ^ (data->phase >> 1);
|
||||
update_amp(blip, data, getAmp(reg, (data->phase & 1) ? volume : 0));
|
||||
}
|
||||
}
|
||||
|
||||
static s32 calcLoopPos(const tic_sound_loop* loop, s32 pos)
|
||||
{
|
||||
s32 offset = 0;
|
||||
|
||||
if (loop->size > 0)
|
||||
{
|
||||
for (s32 i = 0; i < pos; i++)
|
||||
{
|
||||
if (offset < (loop->start + loop->size - 1))
|
||||
offset++;
|
||||
else offset = loop->start;
|
||||
}
|
||||
}
|
||||
else offset = pos >= SFX_TICKS ? SFX_TICKS - 1 : pos;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void resetSfxPos(tic_channel_data* channel)
|
||||
{
|
||||
memset(channel->pos->data, -1, sizeof(tic_sfx_pos));
|
||||
channel->tick = -1;
|
||||
}
|
||||
|
||||
static void sfx(tic_mem* memory, s32 index, s32 note, s32 pitch, tic_channel_data* channel, tic_sound_register* reg, s32 channelIndex)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
if (channel->duration > 0)
|
||||
channel->duration--;
|
||||
|
||||
if (index < 0 || channel->duration == 0)
|
||||
{
|
||||
resetSfxPos(channel);
|
||||
return;
|
||||
}
|
||||
|
||||
const tic_sample* effect = &memory->ram->sfx.samples.data[index];
|
||||
s32 pos = tic_tool_sfx_pos(channel->speed, ++channel->tick);
|
||||
|
||||
for (s32 i = 0; i < sizeof(tic_sfx_pos); i++)
|
||||
*(channel->pos->data + i) = calcLoopPos(effect->loops + i, pos);
|
||||
|
||||
u8 volume = MAX_VOLUME - effect->data[channel->pos->volume].volume;
|
||||
|
||||
if (volume > 0)
|
||||
{
|
||||
s8 arp = effect->data[channel->pos->chord].chord * (effect->reverse ? -1 : 1);
|
||||
if (arp) note += arp;
|
||||
|
||||
note = CLAMP(note, 0, COUNT_OF(NoteFreqs) - 1);
|
||||
|
||||
reg->freq = NoteFreqs[note] + effect->data[channel->pos->pitch].pitch * (effect->pitch16x ? 16 : 1) + pitch;
|
||||
reg->volume = volume;
|
||||
|
||||
u8 wave = effect->data[channel->pos->wave].wave;
|
||||
const tic_waveform* waveform = &memory->ram->sfx.waveforms.items[wave];
|
||||
memcpy(reg->waveform.data, waveform->data, sizeof(tic_waveform));
|
||||
|
||||
tic_tool_poke4(&memory->ram->stereo.data, channelIndex * 2, channel->volume.left * !effect->stereo_left);
|
||||
tic_tool_poke4(&memory->ram->stereo.data, channelIndex * 2 + 1, channel->volume.right * !effect->stereo_right);
|
||||
}
|
||||
}
|
||||
|
||||
static void setChannelData(tic_mem* memory, s32 index, s32 note, s32 octave, s32 duration, tic_channel_data* channel, s32 volumeLeft, s32 volumeRight, s32 speed)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
channel->volume.left = volumeLeft;
|
||||
channel->volume.right = volumeRight;
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
struct { s8 speed : SFX_SPEED_BITS; } temp = { speed };
|
||||
channel->speed = speed == temp.speed ? speed : memory->ram->sfx.samples.data[index].speed;
|
||||
}
|
||||
|
||||
channel->note = note + octave * NOTES;
|
||||
channel->duration = duration;
|
||||
channel->index = index;
|
||||
|
||||
resetSfxPos(channel);
|
||||
}
|
||||
|
||||
|
||||
static void setMusicChannelData(tic_mem* memory, s32 index, s32 note, s32 octave, s32 left, s32 right, s32 channel)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
setChannelData(memory, index, note, octave, -1, &core->state.music.channels[channel], left, right, SFX_DEF_SPEED);
|
||||
}
|
||||
|
||||
static void resetMusicChannels(tic_mem* memory)
|
||||
{
|
||||
for (s32 c = 0; c < TIC_SOUND_CHANNELS; c++)
|
||||
setMusicChannelData(memory, -1, 0, 0, 0, 0, c);
|
||||
|
||||
tic_core* core = (tic_core*)memory;
|
||||
memset(core->state.music.commands, 0, sizeof core->state.music.commands);
|
||||
memset(&core->state.music.jump, 0, sizeof(tic_jump_command));
|
||||
}
|
||||
|
||||
static void stopMusic(tic_mem* memory)
|
||||
{
|
||||
tic_api_music(memory, -1, 0, 0, false, false, -1, -1);
|
||||
}
|
||||
|
||||
static void processMusic(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
tic_music_state* music_state = &memory->ram->music_state;
|
||||
|
||||
if (music_state->flag.music_status == tic_music_stop) return;
|
||||
|
||||
const tic_track* track = &memory->ram->music.tracks.data[music_state->music.track];
|
||||
s32 row = tick2row(core, track, core->state.music.ticks);
|
||||
tic_jump_command* jumpCmd = &core->state.music.jump;
|
||||
|
||||
if (row != music_state->music.row
|
||||
&& jumpCmd->active)
|
||||
{
|
||||
music_state->music.frame = jumpCmd->frame;
|
||||
row = jumpCmd->beat * NOTES_PER_BEAT;
|
||||
core->state.music.ticks = row2tick(core, track, row);
|
||||
memset(jumpCmd, 0, sizeof(tic_jump_command));
|
||||
}
|
||||
|
||||
s32 rows = MUSIC_PATTERN_ROWS - track->rows;
|
||||
if (row >= rows)
|
||||
{
|
||||
row = 0;
|
||||
core->state.music.ticks = 0;
|
||||
|
||||
// If music is in sustain mode, we only reset the channels if the music stopped.
|
||||
// Otherwise, we reset it on every new frame.
|
||||
if (music_state->flag.music_status == tic_music_stop || !music_state->flag.music_sustain)
|
||||
{
|
||||
resetMusicChannels(memory);
|
||||
|
||||
for (s32 c = 0; c < TIC_SOUND_CHANNELS; c++)
|
||||
setMusicChannelData(memory, -1, 0, 0, MAX_VOLUME, MAX_VOLUME, c);
|
||||
}
|
||||
|
||||
if (music_state->flag.music_status == tic_music_play)
|
||||
{
|
||||
music_state->music.frame++;
|
||||
|
||||
if (music_state->music.frame >= MUSIC_FRAMES)
|
||||
{
|
||||
if (music_state->flag.music_loop)
|
||||
music_state->music.frame = 0;
|
||||
else
|
||||
{
|
||||
stopMusic(memory);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 val = 0;
|
||||
for (s32 c = 0; c < TIC_SOUND_CHANNELS; c++)
|
||||
val += tic_tool_get_pattern_id(track, music_state->music.frame, c);
|
||||
|
||||
// empty frame detected
|
||||
if (!val)
|
||||
{
|
||||
if (music_state->flag.music_loop)
|
||||
music_state->music.frame = 0;
|
||||
else
|
||||
{
|
||||
stopMusic(memory);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (music_state->flag.music_status == tic_music_play_frame)
|
||||
{
|
||||
if (!music_state->flag.music_loop)
|
||||
{
|
||||
stopMusic(memory);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (row != music_state->music.row)
|
||||
{
|
||||
music_state->music.row = row;
|
||||
|
||||
for (s32 c = 0; c < TIC_SOUND_CHANNELS; c++)
|
||||
{
|
||||
s32 patternId = tic_tool_get_pattern_id(track, music_state->music.frame, c);
|
||||
if (!patternId) continue;
|
||||
|
||||
const tic_track_pattern* pattern = &memory->ram->music.patterns.data[patternId - PATTERN_START];
|
||||
const tic_track_row* trackRow = &pattern->rows[music_state->music.row];
|
||||
tic_channel_data* channel = &core->state.music.channels[c];
|
||||
tic_command_data* cmdData = &core->state.music.commands[c];
|
||||
|
||||
if (trackRow->command == tic_music_cmd_delay)
|
||||
{
|
||||
cmdData->delay.row = trackRow;
|
||||
cmdData->delay.ticks = param2val(trackRow);
|
||||
trackRow = NULL;
|
||||
}
|
||||
|
||||
if (cmdData->delay.row && cmdData->delay.ticks == 0)
|
||||
{
|
||||
trackRow = cmdData->delay.row;
|
||||
cmdData->delay.row = NULL;
|
||||
}
|
||||
|
||||
if (trackRow)
|
||||
{
|
||||
// reset commands data
|
||||
if (trackRow->note)
|
||||
{
|
||||
cmdData->slide.tick = 0;
|
||||
cmdData->slide.note = channel->note;
|
||||
}
|
||||
|
||||
if (trackRow->note == NoteStop)
|
||||
setMusicChannelData(memory, -1, 0, 0, channel->volume.left, channel->volume.right, c);
|
||||
else if (trackRow->note >= NoteStart)
|
||||
setMusicChannelData(memory, tic_tool_get_track_row_sfx(trackRow), trackRow->note - NoteStart, trackRow->octave,
|
||||
channel->volume.left, channel->volume.right, c);
|
||||
|
||||
switch (trackRow->command)
|
||||
{
|
||||
case tic_music_cmd_volume:
|
||||
channel->volume.left = trackRow->param1;
|
||||
channel->volume.right = trackRow->param2;
|
||||
break;
|
||||
|
||||
case tic_music_cmd_chord:
|
||||
cmdData->chord.tick = 0;
|
||||
cmdData->chord.note1 = trackRow->param1;
|
||||
cmdData->chord.note2 = trackRow->param2;
|
||||
break;
|
||||
|
||||
case tic_music_cmd_jump:
|
||||
core->state.music.jump.active = true;
|
||||
core->state.music.jump.frame = trackRow->param1;
|
||||
core->state.music.jump.beat = trackRow->param2;
|
||||
break;
|
||||
|
||||
case tic_music_cmd_vibrato:
|
||||
cmdData->vibrato.tick = 0;
|
||||
cmdData->vibrato.period = trackRow->param1;
|
||||
cmdData->vibrato.depth = trackRow->param2;
|
||||
break;
|
||||
|
||||
case tic_music_cmd_slide:
|
||||
cmdData->slide.duration = param2val(trackRow);
|
||||
break;
|
||||
|
||||
case tic_music_cmd_pitch:
|
||||
cmdData->finepitch.value = param2val(trackRow) - PITCH_DELTA;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < TIC_SOUND_CHANNELS; ++i)
|
||||
{
|
||||
tic_channel_data* channel = &core->state.music.channels[i];
|
||||
tic_command_data* cmdData = &core->state.music.commands[i];
|
||||
|
||||
if (channel->index >= 0)
|
||||
{
|
||||
s32 note = channel->note;
|
||||
s32 pitch = 0;
|
||||
|
||||
// process chord commmand
|
||||
{
|
||||
s32 chord[] =
|
||||
{
|
||||
0,
|
||||
cmdData->chord.note1,
|
||||
cmdData->chord.note2
|
||||
};
|
||||
|
||||
note += chord[cmdData->chord.tick % (cmdData->chord.note2 == 0 ? 2 : 3)];
|
||||
}
|
||||
|
||||
// process vibrato commmand
|
||||
if (cmdData->vibrato.period && cmdData->vibrato.depth)
|
||||
{
|
||||
static const s32 VibData[] = { 0x0, 0x31f1, 0x61f8, 0x8e3a, 0xb505, 0xd4db, 0xec83, 0xfb15, 0x10000, 0xfb15, 0xec83, 0xd4db, 0xb505, 0x8e3a, 0x61f8, 0x31f1, 0x0, 0xffffce0f, 0xffff9e08, 0xffff71c6, 0xffff4afb, 0xffff2b25, 0xffff137d, 0xffff04eb, 0xffff0000, 0xffff04eb, 0xffff137d, 0xffff2b25, 0xffff4afb, 0xffff71c6, 0xffff9e08, 0xffffce0f };
|
||||
static_assert(COUNT_OF(VibData) == 32, "VibData");
|
||||
|
||||
s32 p = cmdData->vibrato.period << 1;
|
||||
pitch += (VibData[(cmdData->vibrato.tick % p) * COUNT_OF(VibData) / p] * cmdData->vibrato.depth) >> 16;
|
||||
}
|
||||
|
||||
// process slide command
|
||||
if (cmdData->slide.tick < cmdData->slide.duration)
|
||||
pitch += (NoteFreqs[channel->note] - NoteFreqs[note = cmdData->slide.note]) * cmdData->slide.tick / cmdData->slide.duration;
|
||||
|
||||
pitch += cmdData->finepitch.value;
|
||||
|
||||
sfx(memory, channel->index, note, pitch, channel, &memory->ram->registers[i], i);
|
||||
}
|
||||
|
||||
++cmdData->chord.tick;
|
||||
++cmdData->vibrato.tick;
|
||||
++cmdData->slide.tick;
|
||||
|
||||
if (cmdData->delay.ticks)
|
||||
cmdData->delay.ticks--;
|
||||
}
|
||||
|
||||
core->state.music.ticks++;
|
||||
}
|
||||
|
||||
static void setSfxChannelData(tic_mem* memory, s32 index, s32 note, s32 octave, s32 duration, s32 channel, s32 left, s32 right, s32 speed)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
setChannelData(memory, index, note, octave, duration, &core->state.sfx.channels[channel], left, right, speed);
|
||||
}
|
||||
|
||||
static void setMusic(tic_core* core, s32 index, s32 frame, s32 row, bool loop, bool sustain, s32 tempo, s32 speed)
|
||||
{
|
||||
tic_mem* memory = (tic_mem*)core;
|
||||
tic_ram* ram = memory->ram;
|
||||
|
||||
ram->music_state.music.track = index;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
ram->music_state.flag.music_status = tic_music_stop;
|
||||
resetMusicChannels(memory);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (s32 c = 0; c < TIC_SOUND_CHANNELS; c++)
|
||||
setMusicChannelData(memory, -1, 0, 0, MAX_VOLUME, MAX_VOLUME, c);
|
||||
|
||||
ram->music_state.music.row = -1;
|
||||
ram->music_state.music.frame = frame < 0 ? 0 : frame;
|
||||
ram->music_state.flag.music_loop = loop;
|
||||
ram->music_state.flag.music_sustain = sustain;
|
||||
ram->music_state.flag.music_status = tic_music_play;
|
||||
|
||||
const tic_track* track = &ram->music.tracks.data[index];
|
||||
core->state.music.tempo = tempo;
|
||||
core->state.music.speed = speed;
|
||||
core->state.music.ticks = row >= 0 ? row2tick(core, track, row) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void tic_api_music(tic_mem* memory, s32 index, s32 frame, s32 row, bool loop, bool sustain, s32 tempo, s32 speed)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
setMusic(core, index, frame, row, loop, sustain, tempo, speed);
|
||||
|
||||
if (index >= 0)
|
||||
memory->ram->music_state.flag.music_status = tic_music_play;
|
||||
}
|
||||
|
||||
void tic_api_sfx(tic_mem* memory, s32 index, s32 note, s32 octave, s32 duration, s32 channel, s32 left, s32 right, s32 speed)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
setSfxChannelData(memory, index, note, octave, duration, channel, left, right, speed);
|
||||
}
|
||||
|
||||
static void stereo_synthesize(tic_core* core, tic_sound_register_data* registers, blip_buffer_t* blip, u8 stereoRight)
|
||||
{
|
||||
enum { EndTime = CLOCKRATE / TIC80_FRAMERATE };
|
||||
s32 bufpos = (core->state.sound_ringbuf_tail + TIC_SOUND_RINGBUF_LEN - 1) % TIC_SOUND_RINGBUF_LEN;
|
||||
for (s32 i = 0; i < TIC_SOUND_CHANNELS; ++i)
|
||||
{
|
||||
u8 volume = tic_tool_peek4(&core->state.sound_ringbuf[bufpos].stereo, stereoRight + i * 2);
|
||||
|
||||
const tic_sound_register* reg = &core->state.sound_ringbuf[bufpos].registers[i];
|
||||
tic_sound_register_data* data = registers + i;
|
||||
|
||||
tic_tool_noise(®->waveform)
|
||||
? runNoise(blip, reg, data, EndTime, volume)
|
||||
: runEnvelope(blip, reg, data, EndTime, volume);
|
||||
|
||||
data->time -= EndTime;
|
||||
}
|
||||
|
||||
blip_end_frame(blip, EndTime);
|
||||
}
|
||||
|
||||
void tic_core_synth_sound(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
// synthesize sound using the register values found from the tail of the ring buffer
|
||||
stereo_synthesize(core, core->state.registers.left, core->blip.left, 0);
|
||||
stereo_synthesize(core, core->state.registers.right, core->blip.right, 1);
|
||||
|
||||
blip_read_samples(core->blip.left, core->memory.product.samples.buffer, core->samplerate / TIC80_FRAMERATE, TIC80_SAMPLE_CHANNELS);
|
||||
blip_read_samples(core->blip.right, core->memory.product.samples.buffer + 1, core->samplerate / TIC80_FRAMERATE, TIC80_SAMPLE_CHANNELS);
|
||||
|
||||
// if the head has advanced, we can advance the tail too. Otherwise, we just
|
||||
// keep synthesizing audio using the last known register values, so at least we don't get crackles
|
||||
if (core->state.sound_ringbuf_tail != core->state.sound_ringbuf_head) {
|
||||
// note: we assume storing a 32 bit integer is atomic, that should hold on pretty much any modern processor
|
||||
// assuming it is aligned in memory (which it should be)
|
||||
core->state.sound_ringbuf_tail = (core->state.sound_ringbuf_tail + 1) % TIC_SOUND_RINGBUF_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
void tic_core_sound_tick_start(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
for (s32 i = 0; i < TIC_SOUND_CHANNELS; ++i)
|
||||
memset(&memory->ram->registers[i], 0, sizeof(tic_sound_register));
|
||||
|
||||
memory->ram->stereo.data = -1;
|
||||
|
||||
processMusic(memory);
|
||||
|
||||
for (s32 i = 0; i < TIC_SOUND_CHANNELS; ++i)
|
||||
{
|
||||
tic_channel_data* c = &core->state.sfx.channels[i];
|
||||
|
||||
if (c->index >= 0)
|
||||
sfx(memory, c->index, c->note, 0, c, &memory->ram->registers[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
void tic_core_sound_tick_end(tic_mem* memory)
|
||||
{
|
||||
tic_core* core = (tic_core*)memory;
|
||||
|
||||
// instead of synthesizing the sound right away, push the sound registers to the head of a ring buffer
|
||||
core->state.sound_ringbuf[core->state.sound_ringbuf_head].stereo = memory->ram->stereo;
|
||||
memcpy(&core->state.sound_ringbuf[core->state.sound_ringbuf_head], &memory->ram->registers, sizeof(tic_sound_register[4]));
|
||||
|
||||
if (core->state.sound_ringbuf_head != (core->state.sound_ringbuf_tail + TIC_SOUND_RINGBUF_LEN - 2) % TIC_SOUND_RINGBUF_LEN) {
|
||||
// note: we assume storing a 32 bit integer is atomic, that should hold on pretty much any modern processor
|
||||
// assuming it is aligned in memory (which it should be)
|
||||
core->state.sound_ringbuf_head = (core->state.sound_ringbuf_head + 1) % TIC_SOUND_RINGBUF_LEN;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN3(a,b,c) MIN(MIN(a, b), c)
|
||||
#define MAX3(a,b,c) MAX(MAX(a, b), c)
|
||||
#define CLAMP(v,a,b) (MIN(MAX(v,a),b))
|
||||
#define SWAP(a, b, type) do { type temp = a; a = b; b = temp; } while (0)
|
||||
#define MEMCMP(a, b) (sizeof a == sizeof b && memcmp(&a, &b, sizeof a) == 0)
|
||||
#define ZEROMEM(p) memset(&p, 0, sizeof p)
|
||||
#define MOVE(...) memmove(malloc(sizeof __VA_ARGS__), &__VA_ARGS__, sizeof __VA_ARGS__)
|
||||
#define DEF2STR2(x) #x
|
||||
#define DEF2STR(x) DEF2STR2(x)
|
||||
#define STRLEN(str) (sizeof str - 1)
|
||||
#define CONCAT2(a, b) a ## b
|
||||
#define CONCAT(a, b) CONCAT2(a, b)
|
||||
#define MACROVAR(name) CONCAT(name, __LINE__)
|
||||
#define SCOPE(...) for(int MACROVAR(_i_) = 0; !MACROVAR(_i_); ++MACROVAR(_i_), __VA_ARGS__)
|
||||
#define FOR(type,it,list) for(type it = list, *MACROVAR(_end_) = it + COUNT_OF(list); it != MACROVAR(_end_); ++it)
|
||||
#define RFOR(type,it,list) for(type it = list + (COUNT_OF(list) - 1), *MACROVAR(_end_) = list; it >= MACROVAR(_end_); --it)
|
||||
#define NEW(o) (o*)malloc(sizeof(o))
|
||||
#define FREE(ptr) do { if(ptr) free(ptr); } while (0)
|
||||
|
||||
#define BITSET(a,b) ((a) | (1ULL<<(b)))
|
||||
#define BITCLEAR(a,b) ((a) & ~(1ULL<<(b)))
|
||||
#define BITFLIP(a,b) ((a) ^ (1ULL<<(b)))
|
||||
#define BITCHECK(a,b) (!!((a) & (1ULL<<(b))))
|
||||
#define _BITSET(a,b) ((a) |= (1ULL<<(b)))
|
||||
#define _BITCLEAR(a,b) ((a) &= ~(1ULL<<(b)))
|
||||
#define _BITFLIP(a,b) ((a) ^= (1ULL<<(b)))
|
||||
|
||||
#define REP0(...)
|
||||
#define REP1(...) __VA_ARGS__
|
||||
#define REP2(...) REP1(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP3(...) REP2(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP4(...) REP3(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP5(...) REP4(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP6(...) REP5(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP7(...) REP6(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP8(...) REP7(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP9(...) REP8(__VA_ARGS__) __VA_ARGS__
|
||||
#define REP10(...) REP9(__VA_ARGS__) __VA_ARGS__
|
||||
|
||||
#define REP(HUNDREDS,TENS,ONES,...) \
|
||||
REP##HUNDREDS(REP10(REP10(__VA_ARGS__))) \
|
||||
REP##TENS(REP10(__VA_ARGS__)) \
|
||||
REP##ONES(__VA_ARGS__)
|
|
@ -0,0 +1,421 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gif.h"
|
||||
#include "gif_lib.h"
|
||||
|
||||
static gif_image* readGif(GifFileType *gif)
|
||||
{
|
||||
gif_image* image = NULL;
|
||||
|
||||
s32 error = 0;
|
||||
|
||||
if(gif)
|
||||
{
|
||||
if(gif->SHeight > 0 && gif->SWidth > 0)
|
||||
{
|
||||
s32 size = gif->SWidth * gif->SHeight * sizeof(GifPixelType);
|
||||
GifPixelType* screen = (GifPixelType*)malloc(size);
|
||||
|
||||
if(screen)
|
||||
{
|
||||
memset(screen, gif->SBackGroundColor, size);
|
||||
|
||||
GifRecordType record = UNDEFINED_RECORD_TYPE;
|
||||
|
||||
do
|
||||
{
|
||||
if(DGifGetRecordType(gif, &record) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (record)
|
||||
{
|
||||
case IMAGE_DESC_RECORD_TYPE:
|
||||
{
|
||||
if(DGifGetImageDesc(gif) == GIF_ERROR)
|
||||
error = gif->Error;
|
||||
|
||||
s32 row = gif->Image.Top;
|
||||
s32 col = gif->Image.Left;
|
||||
s32 width = gif->Image.Width;
|
||||
s32 height = gif->Image.Height;
|
||||
|
||||
if (gif->Image.Left + gif->Image.Width > gif->SWidth ||
|
||||
gif->Image.Top + gif->Image.Height > gif->SHeight)
|
||||
error = E_GIF_ERR_OPEN_FAILED;
|
||||
|
||||
if (gif->Image.Interlace)
|
||||
{
|
||||
s32 InterlacedOffset[] = { 0, 4, 2, 1 };
|
||||
s32 InterlacedJumps[] = { 8, 8, 4, 2 };
|
||||
|
||||
for (s32 i = 0; i < 4; i++)
|
||||
for (s32 j = row + InterlacedOffset[i]; j < row + height; j += InterlacedJumps[i])
|
||||
{
|
||||
if(DGifGetLine(gif, screen + j * gif->SWidth + col, width) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (s32 i = 0; i < height; i++, row++)
|
||||
{
|
||||
if(DGifGetLine(gif, screen + row * gif->SWidth + col, width) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EXTENSION_RECORD_TYPE:
|
||||
{
|
||||
s32 extCode = 0;
|
||||
GifByteType* extension = NULL;
|
||||
|
||||
if (DGifGetExtension(gif, &extCode, &extension) == GIF_ERROR)
|
||||
error = gif->Error;
|
||||
else
|
||||
{
|
||||
while (extension != NULL)
|
||||
{
|
||||
if(DGifGetExtensionNext(gif, &extension) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TERMINATE_RECORD_TYPE:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(error != E_GIF_SUCCEEDED)
|
||||
break;
|
||||
}
|
||||
while(record != TERMINATE_RECORD_TYPE);
|
||||
|
||||
if(error == E_GIF_SUCCEEDED)
|
||||
{
|
||||
|
||||
image = (gif_image*)malloc(sizeof(gif_image));
|
||||
|
||||
if(image)
|
||||
{
|
||||
memset(image, 0, sizeof(gif_image));
|
||||
image->buffer = screen;
|
||||
image->width = gif->SWidth;
|
||||
image->height = gif->SHeight;
|
||||
|
||||
ColorMapObject* colorMap = gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
|
||||
|
||||
image->colors = colorMap->ColorCount;
|
||||
|
||||
s32 size = image->colors * sizeof(gif_color);
|
||||
image->palette = malloc(size);
|
||||
|
||||
memcpy(image->palette, colorMap->Colors, size);
|
||||
}
|
||||
}
|
||||
else free(screen);
|
||||
}
|
||||
}
|
||||
|
||||
DGifCloseFile(gif, &error);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const void* data;
|
||||
s32 pos;
|
||||
} GifBuffer;
|
||||
|
||||
static s32 readBuffer(GifFileType* gif, GifByteType* data, s32 size)
|
||||
{
|
||||
GifBuffer* buffer = (GifBuffer*)gif->UserData;
|
||||
|
||||
memcpy(data, (const u8*)buffer->data + buffer->pos, size);
|
||||
buffer->pos += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
gif_image* gif_read_data(const void* data, s32 size)
|
||||
{
|
||||
GifBuffer buffer = {data, 0};
|
||||
GifFileType *gif = DGifOpen(&buffer, readBuffer, NULL);
|
||||
|
||||
return readGif(gif);
|
||||
}
|
||||
|
||||
static bool writeGif(GifFileType* gif, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp)
|
||||
{
|
||||
bool result = false;
|
||||
s32 error = 0;
|
||||
|
||||
if(gif)
|
||||
{
|
||||
s32 colors = 1 << bpp;
|
||||
ColorMapObject* colorMap = GifMakeMapObject(colors, NULL);
|
||||
|
||||
memcpy(colorMap->Colors, palette, colors * sizeof(GifColorType));
|
||||
|
||||
if(EGifPutScreenDesc(gif, width, height, bpp, 0, colorMap) != GIF_ERROR)
|
||||
{
|
||||
if(EGifPutImageDesc(gif, 0, 0, width, height, false, NULL) != GIF_ERROR)
|
||||
{
|
||||
GifByteType* ptr = (GifByteType*)data;
|
||||
for (s32 i = 0; i < height; i++, ptr += width)
|
||||
{
|
||||
if (EGifPutLine(gif, ptr, width) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = error == E_GIF_SUCCEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
EGifCloseFile(gif, &error);
|
||||
GifFreeMapObject(colorMap);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static s32 writeBuffer(GifFileType* gif, const GifByteType* data, s32 size)
|
||||
{
|
||||
GifBuffer* buffer = (GifBuffer*)gif->UserData;
|
||||
|
||||
memcpy((u8*)buffer->data + buffer->pos, data, size);
|
||||
buffer->pos += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
bool gif_write_data(const void* buffer, s32* size, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp)
|
||||
{
|
||||
s32 error = 0;
|
||||
GifBuffer output = {buffer, 0};
|
||||
GifFileType* gif = EGifOpen(&output, writeBuffer, &error);
|
||||
|
||||
bool result = writeGif(gif, width, height, data, palette, bpp);
|
||||
|
||||
*size = output.pos;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void gif_close(gif_image* image)
|
||||
{
|
||||
if(image)
|
||||
{
|
||||
if(image->buffer) free(image->buffer);
|
||||
if(image->palette) free(image->palette);
|
||||
|
||||
free(image);
|
||||
}
|
||||
}
|
||||
|
||||
static bool AddLoop(GifFileType *gif)
|
||||
{
|
||||
{
|
||||
const char *nsle = "NETSCAPE2.0";
|
||||
const char subblock[] = {
|
||||
1, // always 1
|
||||
0, // little-endian loop counter:
|
||||
0 // 0 for infinite loop.
|
||||
};
|
||||
|
||||
EGifPutExtensionLeader(gif, APPLICATION_EXT_FUNC_CODE);
|
||||
EGifPutExtensionBlock(gif, 11, nsle);
|
||||
EGifPutExtensionBlock(gif, 3, subblock);
|
||||
EGifPutExtensionTrailer(gif);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const u8* toColor(const u8* ptr, gif_color* color)
|
||||
{
|
||||
color->r = *ptr++;
|
||||
color->g = *ptr++;
|
||||
color->b = *ptr++;
|
||||
ptr++;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool gif_write_animation(const void* buffer, s32* size, s32 width, s32 height, const u8* data, s32 frames, s32 fps, s32 scale)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
s32 swidth = width*scale, sheight = height*scale;
|
||||
s32 frameSize = width * height;
|
||||
|
||||
enum{Bpp = 8, PalSize = 1 << Bpp, PalStructSize = PalSize * sizeof(gif_color)};
|
||||
|
||||
s32 error = 0;
|
||||
GifBuffer output = {buffer, 0};
|
||||
GifFileType* gif = EGifOpen(&output, writeBuffer, &error);
|
||||
|
||||
if(gif)
|
||||
{
|
||||
EGifSetGifVersion(gif, true);
|
||||
|
||||
if(EGifPutScreenDesc(gif, swidth, sheight, Bpp, 0, NULL) != GIF_ERROR)
|
||||
{
|
||||
if(AddLoop(gif))
|
||||
{
|
||||
gif_color* palette = (gif_color*)malloc(PalStructSize);
|
||||
u8* screen = malloc(frameSize);
|
||||
u8* line = malloc(swidth);
|
||||
|
||||
for(s32 f = 0; f < frames; f++)
|
||||
{
|
||||
enum {DelayUnits = 100, MinDelay = 2};
|
||||
|
||||
s32 frame = (f * fps * MinDelay * 2 + 1) / (2 * DelayUnits);
|
||||
|
||||
if(frame >= frames)
|
||||
break;
|
||||
|
||||
s32 colors = 0;
|
||||
const u8* ptr = data + frameSize*frame*sizeof(u32);
|
||||
|
||||
{
|
||||
memset(palette, 0, PalStructSize);
|
||||
memset(screen, 0, frameSize);
|
||||
|
||||
for(s32 i = 0; i < frameSize; i++)
|
||||
{
|
||||
if(colors >= PalSize) break;
|
||||
|
||||
gif_color color;
|
||||
toColor(ptr + i*sizeof(u32), &color);
|
||||
|
||||
bool found = false;
|
||||
for(s32 c = 0; c < colors; c++)
|
||||
{
|
||||
if(memcmp(&palette[c], &color, sizeof(gif_color)) == 0)
|
||||
{
|
||||
found = true;
|
||||
screen[i] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
// TODO: check for last color in palette and try to find closest color
|
||||
screen[i] = colors;
|
||||
memcpy(&palette[colors], &color, sizeof(gif_color));
|
||||
colors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
GraphicsControlBlock gcb =
|
||||
{
|
||||
.DisposalMode = DISPOSE_DO_NOT,
|
||||
.UserInputFlag = false,
|
||||
.DelayTime = MinDelay,
|
||||
.TransparentColor = -1,
|
||||
};
|
||||
|
||||
u8 ext[4];
|
||||
EGifGCBToExtension(&gcb, ext);
|
||||
EGifPutExtension(gif, GRAPHICS_EXT_FUNC_CODE, sizeof ext, ext);
|
||||
}
|
||||
|
||||
ColorMapObject* colorMap = GifMakeMapObject(PalSize, NULL);
|
||||
memset(colorMap->Colors, 0, PalStructSize);
|
||||
memcpy(colorMap->Colors, palette, colors * sizeof(GifColorType));
|
||||
|
||||
if(EGifPutImageDesc(gif, 0, 0, swidth, sheight, false, colorMap) != GIF_ERROR)
|
||||
{
|
||||
for(s32 y = 0; y < height; y++)
|
||||
{
|
||||
for(s32 x = 0, pos = y*width; x < width; x++, pos++)
|
||||
{
|
||||
u8 color = screen[pos];
|
||||
for(s32 s = 0, pos = x*scale; s < scale; s++, pos++)
|
||||
line[pos] = color;
|
||||
}
|
||||
|
||||
for(s32 s = 0; s < scale; s++)
|
||||
{
|
||||
if (EGifPutLine(gif, line, swidth) == GIF_ERROR)
|
||||
{
|
||||
error = gif->Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(error != E_GIF_SUCCEEDED) break;
|
||||
}
|
||||
|
||||
*size = output.pos;
|
||||
|
||||
result = error == E_GIF_SUCCEEDED;
|
||||
}
|
||||
|
||||
GifFreeMapObject(colorMap);
|
||||
|
||||
if(!result)
|
||||
break;
|
||||
}
|
||||
|
||||
free(line);
|
||||
free(screen);
|
||||
free(palette);
|
||||
}
|
||||
}
|
||||
|
||||
EGifCloseFile(gif, &error);
|
||||
|
||||
*size = output.pos;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tic80_types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
}gif_color;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8* buffer;
|
||||
|
||||
gif_color* palette;
|
||||
|
||||
s32 width;
|
||||
s32 height;
|
||||
|
||||
s32 colors;
|
||||
} gif_image;
|
||||
|
||||
gif_image* gif_read_data(const void* buffer, s32 size);
|
||||
bool gif_write_data(const void* buffer, s32* size, s32 width, s32 height, const u8* data, const gif_color* palette, u8 bpp);
|
||||
bool gif_write_animation(const void* buffer, s32* size, s32 width, s32 height, const u8* data, s32 frames, s32 fps, s32 scale);
|
||||
void gif_close(gif_image* image);
|
|
@ -0,0 +1,99 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <tic80.h>
|
||||
#include "api.h"
|
||||
#include "tools.h"
|
||||
#include "cart.h"
|
||||
|
||||
static void onTrace(void* data, const char* text, u8 color)
|
||||
{
|
||||
tic80* tic = (tic80*)data;
|
||||
|
||||
if(tic->callback.trace)
|
||||
tic->callback.trace(text, color);
|
||||
}
|
||||
|
||||
static void onError(void* data, const char* info)
|
||||
{
|
||||
tic80* tic = (tic80*)data;
|
||||
|
||||
if(tic->callback.error)
|
||||
tic->callback.error(info);
|
||||
}
|
||||
|
||||
static void onExit(void* data)
|
||||
{
|
||||
tic80* tic = (tic80*)data;
|
||||
|
||||
if(tic->callback.exit)
|
||||
tic->callback.exit();
|
||||
}
|
||||
|
||||
tic80* tic80_create(s32 samplerate, tic80_pixel_color_format format)
|
||||
{
|
||||
return &tic_core_create(samplerate, format)->product;
|
||||
}
|
||||
|
||||
TIC80_API void tic80_load(tic80* tic, void* cart, s32 size)
|
||||
{
|
||||
tic_mem* mem = (tic_mem*)tic;
|
||||
|
||||
tic_cart_load(&mem->cart, cart, size);
|
||||
tic_api_reset(mem);
|
||||
}
|
||||
|
||||
TIC80_API void tic80_tick(tic80* tic, tic80_input input)
|
||||
{
|
||||
tic_mem* mem = (tic_mem*)tic;
|
||||
|
||||
mem->ram->input = input;
|
||||
|
||||
tic_tick_data tickData = (tic_tick_data)
|
||||
{
|
||||
.error = onError,
|
||||
.trace = onTrace,
|
||||
.exit = onExit,
|
||||
.data = tic,
|
||||
};
|
||||
|
||||
tic_core_tick_start(mem);
|
||||
tic_core_tick(mem, &tickData);
|
||||
tic_core_tick_end(mem);
|
||||
|
||||
tic_core_blit(mem);
|
||||
}
|
||||
|
||||
TIC80_API void tic80_sound(tic80* tic)
|
||||
{
|
||||
tic_mem* mem = (tic_mem*)tic;
|
||||
tic_core_synth_sound(mem);
|
||||
}
|
||||
|
||||
TIC80_API void tic80_delete(tic80* tic)
|
||||
{
|
||||
tic_mem* mem = (tic_mem*)tic;
|
||||
tic_core_close(mem);
|
||||
}
|
|
@ -0,0 +1,660 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tic80.h"
|
||||
#include "defines.h"
|
||||
|
||||
#define TIC_VRAM_SIZE (16*1024) //16K
|
||||
#define TIC_RAM_SIZE (TIC_VRAM_SIZE+80*1024) //16K+80K
|
||||
#define TIC_WASM_PAGE_COUNT 4 // 256K
|
||||
#define TIC_FONT_WIDTH 6
|
||||
#define TIC_FONT_HEIGHT 6
|
||||
#define TIC_ALTFONT_WIDTH 4
|
||||
#define TIC_PALETTE_BPP 4
|
||||
#define TIC_PALETTE_SIZE (1 << TIC_PALETTE_BPP)
|
||||
#define TIC_PALETTES 2
|
||||
#define TIC_SPRITESIZE 8
|
||||
|
||||
#define TIC_DEFAULT_BIT_DEPTH 4
|
||||
#define TIC_DEFAULT_BLIT_MODE 2
|
||||
|
||||
#define TIC80_OFFSET_LEFT ((TIC80_FULLWIDTH-TIC80_WIDTH)/2)
|
||||
#define TIC80_OFFSET_TOP ((TIC80_FULLHEIGHT-TIC80_HEIGHT)/2)
|
||||
|
||||
#define BITS_IN_BYTE 8
|
||||
#define TIC_BANK_SPRITES (1 << BITS_IN_BYTE)
|
||||
#define TIC_SPRITE_BANKS 2
|
||||
#define TIC_FLAGS (TIC_BANK_SPRITES * TIC_SPRITE_BANKS)
|
||||
#define TIC_SPRITES (TIC_BANK_SPRITES * TIC_SPRITE_BANKS)
|
||||
|
||||
#define TIC_SPRITESHEET_SIZE 128
|
||||
#define TIC_SPRITESHEET_COLS (TIC_SPRITESHEET_SIZE / TIC_SPRITESIZE)
|
||||
|
||||
#define TIC_MAP_ROWS (TIC_SPRITESIZE)
|
||||
#define TIC_MAP_COLS (TIC_SPRITESIZE)
|
||||
#define TIC_MAP_SCREEN_WIDTH (TIC80_WIDTH / TIC_SPRITESIZE)
|
||||
#define TIC_MAP_SCREEN_HEIGHT (TIC80_HEIGHT / TIC_SPRITESIZE)
|
||||
#define TIC_MAP_WIDTH (TIC_MAP_SCREEN_WIDTH * TIC_MAP_ROWS)
|
||||
#define TIC_MAP_HEIGHT (TIC_MAP_SCREEN_HEIGHT * TIC_MAP_COLS)
|
||||
|
||||
#define TIC_PERSISTENT_SIZE (1024/sizeof(s32)) // 1K
|
||||
#define TIC_SAVEID_SIZE 64
|
||||
|
||||
#define TIC_SOUND_CHANNELS 4
|
||||
#define SFX_TICKS 30
|
||||
#define SFX_COUNT_BITS 6
|
||||
#define SFX_COUNT (1 << SFX_COUNT_BITS)
|
||||
#define SFX_SPEED_BITS 3
|
||||
#define SFX_DEF_SPEED (1 << SFX_SPEED_BITS)
|
||||
|
||||
#define NOTES 12
|
||||
#define OCTAVES 8
|
||||
#define MAX_VOLUME 15
|
||||
#define MUSIC_PATTERN_ROWS 64
|
||||
#define MUSIC_PATTERNS 60
|
||||
#define MUSIC_CMD_BITS 3
|
||||
#define TRACK_PATTERN_BITS 6
|
||||
#define TRACK_PATTERN_MASK ((1 << TRACK_PATTERN_BITS) - 1)
|
||||
#define TRACK_PATTERNS_SIZE (TRACK_PATTERN_BITS * TIC_SOUND_CHANNELS / BITS_IN_BYTE)
|
||||
#define MUSIC_FRAMES 16
|
||||
#define MUSIC_TRACKS 8
|
||||
#define DEFAULT_TEMPO 150
|
||||
#define DEFAULT_SPEED 6
|
||||
#define PITCH_DELTA 128
|
||||
#define NOTES_PER_BEAT 4
|
||||
#define PATTERN_START 1
|
||||
#define MUSIC_SFXID_LOW_BITS 5
|
||||
#define WAVES_COUNT 16
|
||||
#define WAVE_VALUES 32
|
||||
#define WAVE_VALUE_BITS 4
|
||||
#define WAVE_MAX_VALUE ((1 << WAVE_VALUE_BITS) - 1)
|
||||
#define WAVE_SIZE (WAVE_VALUES * WAVE_VALUE_BITS / BITS_IN_BYTE)
|
||||
|
||||
#define TIC_BANKSIZE_BITS 16
|
||||
#define TIC_BANK_SIZE (1 << TIC_BANKSIZE_BITS) // 64K
|
||||
#define TIC_BANK_BITS 3
|
||||
#define TIC_BANKS (1 << TIC_BANK_BITS)
|
||||
|
||||
#define TIC_CODE_SIZE (TIC_BANK_SIZE * TIC_BANKS)
|
||||
#define TIC_BINARY_BANKS 4
|
||||
#define TIC_BINARY_SIZE (TIC_BINARY_BANKS * TIC_BANK_SIZE) // 4 * 64k = 256K
|
||||
|
||||
|
||||
#define TIC_BUTTONS 8
|
||||
#define TIC_GAMEPADS (sizeof(tic80_gamepads) / sizeof(tic80_gamepad))
|
||||
|
||||
#define SFX_NOTES {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"}
|
||||
#define TIC_FONT_CHARS 128
|
||||
|
||||
#define TIC_UNUSED(x) (void)x
|
||||
|
||||
enum
|
||||
{
|
||||
NoteNone = 0,
|
||||
NoteStop,
|
||||
NoteNone2,
|
||||
NoteNone3,
|
||||
NoteStart,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_color_black,
|
||||
tic_color_purple,
|
||||
tic_color_red,
|
||||
tic_color_orange,
|
||||
tic_color_yellow,
|
||||
tic_color_light_green,
|
||||
tic_color_green,
|
||||
tic_color_dark_green,
|
||||
tic_color_dark_blue,
|
||||
tic_color_blue,
|
||||
tic_color_light_blue,
|
||||
tic_color_cyan,
|
||||
tic_color_white,
|
||||
tic_color_light_grey,
|
||||
tic_color_grey,
|
||||
tic_color_dark_grey,
|
||||
} tic_color;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_no_flip = 0b00,
|
||||
tic_horz_flip = 0b01,
|
||||
tic_vert_flip = 0b10,
|
||||
} tic_flip;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_no_rotate,
|
||||
tic_90_rotate,
|
||||
tic_180_rotate,
|
||||
tic_270_rotate,
|
||||
} tic_rotate;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_bpp_4 = 4,
|
||||
tic_bpp_2 = 2,
|
||||
tic_bpp_1 = 1,
|
||||
} tic_bpp;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 start:4;
|
||||
u8 size:4;
|
||||
} tic_sound_loop;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
struct
|
||||
{
|
||||
u8 volume:4;
|
||||
u8 wave:4;
|
||||
u8 chord:4;
|
||||
s8 pitch:4;
|
||||
} data[SFX_TICKS];
|
||||
|
||||
struct
|
||||
{
|
||||
u8 octave:3;
|
||||
u8 pitch16x:1; // pitch factor
|
||||
s8 speed:SFX_SPEED_BITS;
|
||||
u8 reverse:1; // chord reverse
|
||||
u8 note:4;
|
||||
u8 stereo_left:1;
|
||||
u8 stereo_right:1;
|
||||
u8 temp:2;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic_sound_loop wave;
|
||||
tic_sound_loop volume;
|
||||
tic_sound_loop chord;
|
||||
tic_sound_loop pitch;
|
||||
};
|
||||
|
||||
tic_sound_loop loops[4];
|
||||
};
|
||||
|
||||
} tic_sample;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
s8 wave;
|
||||
s8 volume;
|
||||
s8 chord;
|
||||
s8 pitch;
|
||||
};
|
||||
|
||||
s8 data[4];
|
||||
};
|
||||
} tic_sfx_pos;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[WAVE_SIZE];
|
||||
}tic_waveform;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_waveform items[WAVES_COUNT];
|
||||
} tic_waveforms;
|
||||
|
||||
#define MUSIC_CMD_LIST(macro) \
|
||||
macro(empty, 0, "") \
|
||||
macro(volume, M, "master volume for LEFT=X / RIGHT=Y channel") \
|
||||
macro(chord, C, "play chord, X=3 Y=7 plays +0,+3,+7 notes") \
|
||||
macro(jump, J, "jump to FRAME=X / BEAT=Y") \
|
||||
macro(slide, S, "slide to note (legato) with TICKS=XY") \
|
||||
macro(pitch, P, "finepitch UP/DOWN=XY-" DEF2STR(PITCH_DELTA)) \
|
||||
macro(vibrato, V, "vibrato with PERIOD=X and DEPTH=Y") \
|
||||
macro(delay, D, "delay triggering a note with TICKS=XY")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define ENUM_ITEM(name, ...) tic_music_cmd_##name,
|
||||
MUSIC_CMD_LIST(ENUM_ITEM)
|
||||
#undef ENUM_ITEM
|
||||
|
||||
tic_music_cmd_count
|
||||
} tic_music_command;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 note :4;
|
||||
u8 param1 :4;
|
||||
u8 param2 :4;
|
||||
u8 command :MUSIC_CMD_BITS; // tic_music_command
|
||||
u8 sfxhi :1;
|
||||
u8 sfxlow :MUSIC_SFXID_LOW_BITS;
|
||||
u8 octave :3;
|
||||
|
||||
} tic_track_row;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_track_row rows[MUSIC_PATTERN_ROWS];
|
||||
|
||||
} tic_track_pattern;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[MUSIC_FRAMES * TRACK_PATTERNS_SIZE]; // sfx - 6bits per channel = 24 bit
|
||||
|
||||
s8 tempo; // delta value, rel to 120 bpm * 10 [32-255]
|
||||
u8 rows; // delta value, rel to 64 rows, can be [1-64]
|
||||
s8 speed; // delta value, rel to 6 [1-31]
|
||||
|
||||
} tic_track;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_track_pattern data[MUSIC_PATTERNS];
|
||||
} tic_patterns;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_track data[MUSIC_TRACKS];
|
||||
} tic_tracks;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_sample data[SFX_COUNT];
|
||||
} tic_samples;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_waveforms waveforms;
|
||||
tic_samples samples;
|
||||
}tic_sfx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_patterns patterns;
|
||||
tic_tracks tracks;
|
||||
}tic_music;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_music_stop,
|
||||
tic_music_play_frame,
|
||||
tic_music_play,
|
||||
} tic_music_status;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
s8 track;
|
||||
s8 frame;
|
||||
s8 row;
|
||||
} music;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 music_loop:1;
|
||||
u8 music_status:2; // enum tic_music_status
|
||||
u8 music_sustain:1;
|
||||
u8 unknown:4;
|
||||
} flag;
|
||||
|
||||
} tic_music_state;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 left1:4;
|
||||
u8 right1:4;
|
||||
|
||||
u8 left2:4;
|
||||
u8 right2:4;
|
||||
|
||||
u8 left3:4;
|
||||
u8 right3:4;
|
||||
|
||||
u8 left4:4;
|
||||
u8 right4:4;
|
||||
};
|
||||
|
||||
u32 data;
|
||||
} tic_stereo_volume;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
u16 freq:12;
|
||||
u16 volume:4;
|
||||
};
|
||||
|
||||
tic_waveform waveform;
|
||||
} tic_sound_register;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_MAP_WIDTH * TIC_MAP_HEIGHT];
|
||||
} tic_map;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_SPRITESIZE * TIC_SPRITESIZE * TIC_PALETTE_BPP / BITS_IN_BYTE];
|
||||
} tic_tile;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char data[TIC_CODE_SIZE];
|
||||
} tic_code;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char data[TIC_BINARY_SIZE];
|
||||
u32 size;
|
||||
} tic_binary;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 r;
|
||||
u8 g;
|
||||
u8 b;
|
||||
} tic_rgb;
|
||||
|
||||
typedef union
|
||||
{
|
||||
tic_rgb colors[TIC_PALETTE_SIZE];
|
||||
|
||||
u8 data[TIC_PALETTE_SIZE * sizeof(tic_rgb)];
|
||||
} tic_palette;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 data[TIC_PALETTE_SIZE];
|
||||
} tic_blitpal;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_tile data[TIC_BANK_SPRITES];
|
||||
} tic_tiles, tic_sprites;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_FLAGS];
|
||||
} tic_flags;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_palette vbank0;
|
||||
tic_palette vbank1;
|
||||
} tic_palettes;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC80_WIDTH * TIC80_HEIGHT * TIC_PALETTE_BPP / BITS_IN_BYTE];
|
||||
} tic_screen;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_screen screen;
|
||||
tic_tiles tiles;
|
||||
tic_sprites sprites;
|
||||
tic_map map;
|
||||
tic_sfx sfx;
|
||||
tic_music music;
|
||||
tic_flags flags;
|
||||
tic_palettes palette;
|
||||
} tic_bank;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
tic_bank bank0;
|
||||
tic_bank banks[TIC_BANKS];
|
||||
};
|
||||
|
||||
tic_code code;
|
||||
tic_binary binary;
|
||||
u8 lang;
|
||||
|
||||
} tic_cartridge;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[(TIC_FONT_CHARS - 1) * BITS_IN_BYTE];
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 width;
|
||||
u8 height;
|
||||
};
|
||||
|
||||
u8 params[BITS_IN_BYTE];
|
||||
};
|
||||
} tic_font_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_font_data regular;
|
||||
tic_font_data alt;
|
||||
} tic_font;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic_screen screen;
|
||||
tic_palette palette;
|
||||
u8 mapping[TIC_PALETTE_SIZE * TIC_PALETTE_BPP / BITS_IN_BYTE];
|
||||
|
||||
struct
|
||||
{
|
||||
union
|
||||
{
|
||||
u8 border:TIC_PALETTE_BPP;
|
||||
|
||||
// clear color for the BANK1
|
||||
u8 clear:TIC_PALETTE_BPP;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
s8 x;
|
||||
s8 y;
|
||||
} offset;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 sprite:7;
|
||||
bool system:1;
|
||||
} cursor;
|
||||
} vars;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 segment:4;
|
||||
u8 reserved:4;
|
||||
} blit;
|
||||
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
u8 data[TIC_VRAM_SIZE];
|
||||
} tic_vram;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 data[TIC_PERSISTENT_SIZE];
|
||||
} tic_persistent;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 data[TIC_GAMEPADS * TIC_BUTTONS];
|
||||
} tic_mapping;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
tic_vram vram;
|
||||
tic_tiles tiles;
|
||||
tic_sprites sprites;
|
||||
tic_map map;
|
||||
tic80_input input;
|
||||
tic_sfx_pos sfxpos[TIC_SOUND_CHANNELS];
|
||||
tic_sound_register registers[TIC_SOUND_CHANNELS];
|
||||
tic_sfx sfx;
|
||||
tic_music music;
|
||||
tic_music_state music_state;
|
||||
tic_stereo_volume stereo;
|
||||
tic_persistent persistent;
|
||||
tic_flags flags;
|
||||
tic_font font;
|
||||
tic_mapping mapping;
|
||||
|
||||
u8 free;
|
||||
};
|
||||
|
||||
u8 data[TIC_RAM_SIZE];
|
||||
|
||||
} tic_ram;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_key_unknown,
|
||||
|
||||
tic_key_a,
|
||||
tic_key_b,
|
||||
tic_key_c,
|
||||
tic_key_d,
|
||||
tic_key_e,
|
||||
tic_key_f,
|
||||
tic_key_g,
|
||||
tic_key_h,
|
||||
tic_key_i,
|
||||
tic_key_j,
|
||||
tic_key_k,
|
||||
tic_key_l,
|
||||
tic_key_m,
|
||||
tic_key_n,
|
||||
tic_key_o,
|
||||
tic_key_p,
|
||||
tic_key_q,
|
||||
tic_key_r,
|
||||
tic_key_s,
|
||||
tic_key_t,
|
||||
tic_key_u,
|
||||
tic_key_v,
|
||||
tic_key_w,
|
||||
tic_key_x,
|
||||
tic_key_y,
|
||||
tic_key_z,
|
||||
|
||||
tic_key_0,
|
||||
tic_key_1,
|
||||
tic_key_2,
|
||||
tic_key_3,
|
||||
tic_key_4,
|
||||
tic_key_5,
|
||||
tic_key_6,
|
||||
tic_key_7,
|
||||
tic_key_8,
|
||||
tic_key_9,
|
||||
|
||||
tic_key_minus,
|
||||
tic_key_equals,
|
||||
tic_key_leftbracket,
|
||||
tic_key_rightbracket,
|
||||
tic_key_backslash,
|
||||
tic_key_semicolon,
|
||||
tic_key_apostrophe,
|
||||
tic_key_grave,
|
||||
tic_key_comma,
|
||||
tic_key_period,
|
||||
tic_key_slash,
|
||||
|
||||
tic_key_space,
|
||||
tic_key_tab,
|
||||
|
||||
tic_key_return,
|
||||
tic_key_backspace,
|
||||
tic_key_delete,
|
||||
tic_key_insert,
|
||||
|
||||
tic_key_pageup,
|
||||
tic_key_pagedown,
|
||||
tic_key_home,
|
||||
tic_key_end,
|
||||
tic_key_up,
|
||||
tic_key_down,
|
||||
tic_key_left,
|
||||
tic_key_right,
|
||||
|
||||
tic_key_capslock,
|
||||
tic_key_ctrl,
|
||||
tic_key_shift,
|
||||
tic_key_alt,
|
||||
|
||||
tic_key_escape,
|
||||
tic_key_f1,
|
||||
tic_key_f2,
|
||||
tic_key_f3,
|
||||
tic_key_f4,
|
||||
tic_key_f5,
|
||||
tic_key_f6,
|
||||
tic_key_f7,
|
||||
tic_key_f8,
|
||||
tic_key_f9,
|
||||
tic_key_f10,
|
||||
tic_key_f11,
|
||||
tic_key_f12,
|
||||
|
||||
////////////////
|
||||
|
||||
tic_keys_count
|
||||
} tic_keycode;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_mouse_left,
|
||||
tic_mouse_middle,
|
||||
tic_mouse_right,
|
||||
} tic_mouse_btn;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tic_cursor_arrow,
|
||||
tic_cursor_hand,
|
||||
tic_cursor_ibeam,
|
||||
} tic_cursor;
|
|
@ -0,0 +1,98 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
// Damien de Lemeny @ddelemeny // hello@ddelemeny.me
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "tilesheet.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static const tic_blit_segment segments[] = {
|
||||
// +page +nb_pages
|
||||
// | +bank +bank_size
|
||||
// | | | | +sheet_width
|
||||
// | | | | | +tile_width
|
||||
{0, 0, 1, 256, 16, 8, TIC_SPRITESIZE, tic_tool_peek1, tic_tool_poke1}, // system gfx
|
||||
{0, 0, 1, 256, 16, 8, TIC_SPRITESIZE, tic_tool_peek1, tic_tool_poke1}, // system font
|
||||
{0, 0, 1, 256, 16, 8, sizeof(tic_tile), tic_tool_peek4, tic_tool_poke4}, // 4bpp p0 bg
|
||||
{0, 1, 1, 256, 16, 8, sizeof(tic_tile), tic_tool_peek4, tic_tool_poke4}, // 4bpp p0 fg
|
||||
|
||||
{0, 0, 2, 512, 32, 16, sizeof(tic_tile), tic_tool_peek2, tic_tool_poke2}, // 2bpp p0 bg
|
||||
{1, 0, 2, 512, 32, 16, sizeof(tic_tile), tic_tool_peek2, tic_tool_poke2}, // 2bpp p1 bg
|
||||
{0, 1, 2, 512, 32, 16, sizeof(tic_tile), tic_tool_peek2, tic_tool_poke2}, // 2bpp p0 fg
|
||||
{1, 1, 2, 512, 32, 16, sizeof(tic_tile), tic_tool_peek2, tic_tool_poke2}, // 2bpp p1 fg
|
||||
|
||||
{0, 0, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p0 bg
|
||||
{1, 0, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p1 bg
|
||||
{2, 0, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p2 bg
|
||||
{3, 0, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p3 bg
|
||||
{0, 1, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p0 fg
|
||||
{1, 1, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p1 fg
|
||||
{2, 1, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p2 fg
|
||||
{3, 1, 4, 1024, 64, 32, sizeof(tic_tile), tic_tool_peek1, tic_tool_poke1}, // 1bpp p3 fg
|
||||
};
|
||||
|
||||
extern u8 tic_tilesheet_getpix(const tic_tilesheet* sheet, s32 x, s32 y);
|
||||
extern void tic_tilesheet_setpix(const tic_tilesheet* sheet, s32 x, s32 y, u8 value);
|
||||
extern u8 tic_tilesheet_gettilepix(const tic_tileptr* tile, s32 x, s32 y);
|
||||
extern void tic_tilesheet_settilepix(const tic_tileptr* tile, s32 x, s32 y, u8 value);
|
||||
|
||||
tic_tilesheet tic_tilesheet_get(u8 segment, u8* ptr)
|
||||
{
|
||||
return (tic_tilesheet) { &segments[segment], ptr };
|
||||
}
|
||||
|
||||
tic_tileptr tic_tilesheet_gettile(const tic_tilesheet* sheet, s32 index, bool local)
|
||||
{
|
||||
enum { Cols = 16, Size = 8 };
|
||||
const tic_blit_segment* segment = sheet->segment;
|
||||
|
||||
s32 bank, page, iy, ix;
|
||||
if (local) {
|
||||
index = index & 255;
|
||||
bank = segment->bank_orig;
|
||||
page = segment->page_orig;
|
||||
div_t ixy = div(index, Cols);
|
||||
iy = ixy.quot;
|
||||
ix = ixy.rem;
|
||||
}
|
||||
else {
|
||||
// reindex
|
||||
div_t ia = div(index, segment->bank_size); // bank, bank_index
|
||||
div_t ib = div(ia.rem, segment->sheet_width); // yi, bank_xi
|
||||
div_t ic = div(ib.rem, Cols); // page, xi
|
||||
bank = (ia.quot + segment->bank_orig) % 2;
|
||||
page = (ic.quot + segment->page_orig) % segment->nb_pages;
|
||||
iy = ib.quot % Cols;
|
||||
ix = ic.rem;
|
||||
}
|
||||
|
||||
div_t xdiv = div(ix, segment->nb_pages); // xbuffer, xoffset
|
||||
u32 ptr_offset = (bank * Cols + iy) * Cols + page * Cols / segment->nb_pages + xdiv.quot;
|
||||
u8* ptr = sheet->ptr + segment->ptr_size * ptr_offset;
|
||||
u32 offset = (xdiv.rem * Size);
|
||||
|
||||
return (tic_tileptr) { segment, offset, ptr };
|
||||
}
|
||||
|
||||
extern s32 tic_blit_calc_segment(const tic_blit* blit);
|
||||
extern void tic_blit_update_bpp(tic_blit* blit, tic_bpp bpp);
|
||||
extern s32 tic_blit_calc_index(const tic_blit* blit);
|
|
@ -0,0 +1,110 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
// Damien de Lemeny @ddelemeny // hello@ddelemeny.me
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 page_orig;
|
||||
u32 bank_orig;
|
||||
u32 nb_pages;
|
||||
u32 bank_size;
|
||||
u32 sheet_width;
|
||||
u32 tile_width;
|
||||
size_t ptr_size;
|
||||
u8 (*peek)(const void*, u32);
|
||||
void (*poke)(void*, u32, u8);
|
||||
} tic_blit_segment;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const tic_blit_segment* segment;
|
||||
u8* ptr;
|
||||
} tic_tilesheet;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const tic_blit_segment* segment;
|
||||
u32 offset;
|
||||
u8* ptr;
|
||||
} tic_tileptr;
|
||||
|
||||
tic_tilesheet tic_tilesheet_get(u8 segment, u8* ptr);
|
||||
tic_tileptr tic_tilesheet_gettile(const tic_tilesheet* sheet, s32 index, bool local);
|
||||
|
||||
inline u8 tic_tilesheet_getpix(const tic_tilesheet* sheet, s32 x, s32 y)
|
||||
{
|
||||
// tile coord
|
||||
u16 tile_index = ((y >> 3) << 4 ) + (x / sheet->segment->tile_width);
|
||||
// coord in tile
|
||||
u32 pix_addr = ((x & (sheet->segment->tile_width - 1)) + ((y & 7) * sheet->segment->tile_width)) ;
|
||||
return sheet->segment->peek(sheet->ptr+tile_index * sheet->segment->ptr_size, pix_addr);
|
||||
}
|
||||
|
||||
inline void tic_tilesheet_setpix(const tic_tilesheet* sheet, s32 x, s32 y, u8 value)
|
||||
{
|
||||
// tile coord
|
||||
u16 tile_index = ((y >> 3) << 4 ) + (x / sheet->segment->tile_width);
|
||||
// coord in tile
|
||||
u32 pix_addr = ((x & (sheet->segment->tile_width - 1)) + ((y & 7) * sheet->segment->tile_width)) ;
|
||||
sheet->segment->poke(sheet->ptr + tile_index * sheet->segment->ptr_size, pix_addr, value);
|
||||
}
|
||||
|
||||
inline u8 tic_tilesheet_gettilepix(const tic_tileptr* tile, s32 x, s32 y)
|
||||
{
|
||||
u32 addr = tile->offset + x + (y * tile->segment->tile_width);
|
||||
return tile->segment->peek(tile->ptr, addr);
|
||||
}
|
||||
|
||||
inline void tic_tilesheet_settilepix(const tic_tileptr* tile, s32 x, s32 y, u8 value)
|
||||
{
|
||||
u32 addr = tile->offset + x + (y * tile->segment->tile_width);
|
||||
tile->segment->poke(tile->ptr, addr, value);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tic_bpp mode;
|
||||
u8 pages;
|
||||
u8 page;
|
||||
u8 bank;
|
||||
} tic_blit;
|
||||
|
||||
inline s32 tic_blit_calc_segment(const tic_blit* blit)
|
||||
{
|
||||
return blit->pages * (2 + blit->bank) + blit->page;
|
||||
}
|
||||
|
||||
inline void tic_blit_update_bpp(tic_blit* blit, tic_bpp bpp)
|
||||
{
|
||||
blit->mode = bpp;
|
||||
blit->pages = 4 / bpp;
|
||||
blit->page %= blit->pages;
|
||||
}
|
||||
|
||||
inline s32 tic_blit_calc_index(const tic_blit* blit)
|
||||
{
|
||||
return blit->bank * blit->pages * TIC_BANK_SPRITES + blit->page * TIC_SPRITESHEET_COLS;
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "tools.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
extern void tic_tool_poke4(void* addr, u32 index, u8 value);
|
||||
extern u8 tic_tool_peek4(const void* addr, u32 index);
|
||||
extern void tic_tool_poke2(void* addr, u32 index, u8 value);
|
||||
extern u8 tic_tool_peek2(const void* addr, u32 index);
|
||||
extern void tic_tool_poke1(void* addr, u32 index, u8 value);
|
||||
extern u8 tic_tool_peek1(const void* addr, u32 index);
|
||||
extern s32 tic_tool_sfx_pos(s32 speed, s32 ticks);
|
||||
extern u32 tic_rgba(const tic_rgb* c);
|
||||
extern s32 tic_modulo(s32 x, s32 m);
|
||||
|
||||
static u32 getPatternData(const tic_track* track, s32 frame)
|
||||
{
|
||||
u32 patternData = 0;
|
||||
for(s32 b = 0; b < TRACK_PATTERNS_SIZE; b++)
|
||||
patternData |= track->data[frame * TRACK_PATTERNS_SIZE + b] << (BITS_IN_BYTE * b);
|
||||
|
||||
return patternData;
|
||||
}
|
||||
|
||||
s32 tic_tool_get_pattern_id(const tic_track* track, s32 frame, s32 channel)
|
||||
{
|
||||
return (getPatternData(track, frame) >> (channel * TRACK_PATTERN_BITS)) & TRACK_PATTERN_MASK;
|
||||
}
|
||||
|
||||
void tic_tool_set_pattern_id(tic_track* track, s32 frame, s32 channel, s32 pattern)
|
||||
{
|
||||
u32 patternData = getPatternData(track, frame);
|
||||
s32 shift = channel * TRACK_PATTERN_BITS;
|
||||
|
||||
patternData &= ~(TRACK_PATTERN_MASK << shift);
|
||||
patternData |= pattern << shift;
|
||||
|
||||
for(s32 b = 0; b < TRACK_PATTERNS_SIZE; b++)
|
||||
track->data[frame * TRACK_PATTERNS_SIZE + b] = (patternData >> (b * BITS_IN_BYTE)) & 0xff;
|
||||
}
|
||||
|
||||
bool tic_tool_parse_note(const char* noteStr, s32* note, s32* octave)
|
||||
{
|
||||
if(noteStr && strlen(noteStr) == 3)
|
||||
{
|
||||
static const char* Notes[] = SFX_NOTES;
|
||||
|
||||
for(s32 i = 0; i < COUNT_OF(Notes); i++)
|
||||
{
|
||||
if(memcmp(Notes[i], noteStr, 2) == 0)
|
||||
{
|
||||
*note = i;
|
||||
*octave = noteStr[2] - '1';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 tic_nearest_color(const tic_rgb* palette, const tic_rgb* color, s32 count)
|
||||
{
|
||||
u32 min = -1;
|
||||
s32 nearest, i = 0;
|
||||
|
||||
for(const tic_rgb *rgb = palette, *end = rgb + count; rgb < end; rgb++, i++)
|
||||
{
|
||||
s32 d[] = {color->r - rgb->r, color->g - rgb->g, color->b - rgb->b};
|
||||
|
||||
u32 dst = 0;
|
||||
for(const s32 *v = d, *end = v + COUNT_OF(d); v < end; v++)
|
||||
dst += (*v) * (*v);
|
||||
|
||||
if (dst < min)
|
||||
{
|
||||
min = dst;
|
||||
nearest = i;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
||||
tic_blitpal tic_tool_palette_blit(const tic_palette* srcpal, tic80_pixel_color_format fmt)
|
||||
{
|
||||
tic_blitpal pal;
|
||||
|
||||
const tic_rgb* src = srcpal->colors;
|
||||
const tic_rgb* end = src + TIC_PALETTE_SIZE;
|
||||
u8* dst = (u8*)pal.data;
|
||||
|
||||
while(src != end)
|
||||
{
|
||||
switch(fmt){
|
||||
case TIC80_PIXEL_COLOR_BGRA8888:
|
||||
*dst++ = src->b;
|
||||
*dst++ = src->g;
|
||||
*dst++ = src->r;
|
||||
*dst++ = 0xff;
|
||||
break;
|
||||
case TIC80_PIXEL_COLOR_RGBA8888:
|
||||
*dst++ = src->r;
|
||||
*dst++ = src->g;
|
||||
*dst++ = src->b;
|
||||
*dst++ = 0xff;
|
||||
break;
|
||||
case TIC80_PIXEL_COLOR_ABGR8888:
|
||||
*dst++ = 0xff;
|
||||
*dst++ = src->b;
|
||||
*dst++ = src->g;
|
||||
*dst++ = src->r;
|
||||
break;
|
||||
case TIC80_PIXEL_COLOR_ARGB8888:
|
||||
*dst++ = 0xff;
|
||||
*dst++ = src->r;
|
||||
*dst++ = src->g;
|
||||
*dst++ = src->b;
|
||||
break;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
|
||||
return pal;
|
||||
}
|
||||
|
||||
bool tic_project_ext(const char* name)
|
||||
{
|
||||
FOR_EACH_LANG(ln)
|
||||
{
|
||||
if(tic_tool_has_ext(name, ln->fileExtension))
|
||||
return true;
|
||||
}
|
||||
FOR_EACH_LANG_END
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tic_tool_has_ext(const char* name, const char* ext)
|
||||
{
|
||||
return strcmp(name + strlen(name) - strlen(ext), ext) == 0;
|
||||
}
|
||||
|
||||
s32 tic_tool_get_track_row_sfx(const tic_track_row* row)
|
||||
{
|
||||
return (row->sfxhi << MUSIC_SFXID_LOW_BITS) | row->sfxlow;
|
||||
}
|
||||
|
||||
void tic_tool_set_track_row_sfx(tic_track_row* row, s32 sfx)
|
||||
{
|
||||
if(sfx >= SFX_COUNT) sfx = SFX_COUNT-1;
|
||||
|
||||
row->sfxhi = (sfx & 0b00100000) >> MUSIC_SFXID_LOW_BITS;
|
||||
row->sfxlow = sfx & 0b00011111;
|
||||
}
|
||||
|
||||
bool tic_tool_empty(const void* buffer, s32 size)
|
||||
{
|
||||
for(const u8 *ptr = buffer, *end = ptr + size; ptr < end;)
|
||||
if(*ptr++)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tic_tool_flat4(const void* buffer, s32 size)
|
||||
{
|
||||
u8 first = (*(u8*)buffer) & 0xf;
|
||||
first |= first << 4;
|
||||
for(const u8 *ptr = buffer, *end = ptr + size; ptr < end;)
|
||||
if(*ptr++ != first)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tic_tool_noise(const tic_waveform* wave)
|
||||
{
|
||||
return FLAT4(wave->data) && *wave->data % 0xff == 0;
|
||||
}
|
||||
|
||||
void tic_tool_str2buf(const char* str, s32 size, void* buf, bool flip)
|
||||
{
|
||||
char val[] = "0x00";
|
||||
const char* ptr = str;
|
||||
|
||||
for(s32 i = 0; i < size/2; i++)
|
||||
{
|
||||
if(flip)
|
||||
{
|
||||
val[3] = *ptr++;
|
||||
val[2] = *ptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
val[2] = *ptr++;
|
||||
val[3] = *ptr++;
|
||||
}
|
||||
|
||||
((u8*)buf)[i] = (u8)strtol(val, NULL, 16);
|
||||
}
|
||||
}
|
||||
|
||||
u32 tic_tool_zip(void* dest, s32 destSize, const void* source, s32 size)
|
||||
{
|
||||
unsigned long destSizeLong = destSize;
|
||||
return compress2(dest, &destSizeLong, source, size, Z_BEST_COMPRESSION) == Z_OK ? destSizeLong : 0;
|
||||
}
|
||||
|
||||
u32 tic_tool_unzip(void* dest, s32 destSize, const void* source, s32 size)
|
||||
{
|
||||
unsigned long destSizeLong = destSize;
|
||||
return uncompress(dest, &destSizeLong, source, size) == Z_OK ? destSizeLong : 0;
|
||||
}
|
||||
|
||||
char* tic_tool_metatag(const char* code, const char* tag, const char* comment)
|
||||
{
|
||||
const char* start = NULL;
|
||||
|
||||
{
|
||||
static char format[] = "%s %s:";
|
||||
|
||||
char* tagBuffer = malloc(sizeof format + strlen(tag));
|
||||
|
||||
SCOPE(free(tagBuffer))
|
||||
{
|
||||
sprintf(tagBuffer, format, comment, tag);
|
||||
if ((start = strstr(code, tagBuffer)))
|
||||
start += strlen(tagBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (start)
|
||||
{
|
||||
const char* end = strstr(start, "\n");
|
||||
|
||||
if (end)
|
||||
{
|
||||
while (isspace(*start) && start < end) start++;
|
||||
while (isspace(*(end - 1)) && end > start) end--;
|
||||
|
||||
const s32 size = (s32)(end - start);
|
||||
|
||||
char* value = (char*)malloc(size + 1);
|
||||
|
||||
if (value)
|
||||
{
|
||||
memset(value, 0, size + 1);
|
||||
memcpy(value, start, size);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api.h"
|
||||
#include "tic.h"
|
||||
#include <stddef.h>
|
||||
|
||||
inline s32 tic_tool_sfx_pos(s32 speed, s32 ticks)
|
||||
{
|
||||
return speed > 0 ? ticks * (1 + speed) : ticks / (1 - speed);
|
||||
}
|
||||
|
||||
#define POKE_N(P,I,V,A,B,C,D) do \
|
||||
{ \
|
||||
u8* val = (u8*)(P) + ((I) >> (A)); \
|
||||
u8 offset = ((I) & (B)) << (C); \
|
||||
*val &= ~((D) << offset); \
|
||||
*val |= ((V) & (D)) << offset; \
|
||||
} while(0)
|
||||
|
||||
#define PEEK_N(P,I,A,B,C,D) ( ( ((u8*)(P))[((I) >> (A))] >> ( ((I) & (B)) << (C) ) ) & (D) )
|
||||
|
||||
inline void tic_tool_poke4(void* addr, u32 index, u8 value)
|
||||
{
|
||||
POKE_N(addr, index, value, 1,1,2,15);
|
||||
}
|
||||
|
||||
inline u8 tic_tool_peek4(const void* addr, u32 index)
|
||||
{
|
||||
return PEEK_N(addr, index, 1,1,2,15);
|
||||
}
|
||||
|
||||
inline void tic_tool_poke2(void* addr, u32 index, u8 value)
|
||||
{
|
||||
POKE_N(addr, index, value, 2,3,1,3);
|
||||
}
|
||||
|
||||
inline u8 tic_tool_peek2(const void* addr, u32 index)
|
||||
{
|
||||
return PEEK_N(addr, index, 2,3,1,3);
|
||||
}
|
||||
|
||||
inline void tic_tool_poke1(void* addr, u32 index, u8 value)
|
||||
{
|
||||
POKE_N(addr, index, value, 3,7,0,1);
|
||||
}
|
||||
|
||||
inline u8 tic_tool_peek1(const void* addr, u32 index)
|
||||
{
|
||||
return PEEK_N(addr, index, 3,7,0,1);
|
||||
}
|
||||
|
||||
#undef PEEK_N
|
||||
#undef POKE_N
|
||||
|
||||
inline u32 tic_rgba(const tic_rgb* c)
|
||||
{
|
||||
return (0xff << 24) | (c->b << 16) | (c->g << 8) | (c->r << 0);
|
||||
}
|
||||
|
||||
inline s32 tic_modulo(s32 x, s32 m)
|
||||
{
|
||||
if(x >= m) return x % m;
|
||||
if(x < 0) return x % m + m;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
tic_blitpal tic_tool_palette_blit(const tic_palette* src, tic80_pixel_color_format fmt);
|
||||
|
||||
bool tic_tool_parse_note(const char* noteStr, s32* note, s32* octave);
|
||||
s32 tic_tool_get_pattern_id(const tic_track* track, s32 frame, s32 channel);
|
||||
void tic_tool_set_pattern_id(tic_track* track, s32 frame, s32 channel, s32 id);
|
||||
bool tic_project_ext(const char* name);
|
||||
bool tic_tool_has_ext(const char* name, const char* ext);
|
||||
s32 tic_tool_get_track_row_sfx(const tic_track_row* row);
|
||||
void tic_tool_set_track_row_sfx(tic_track_row* row, s32 sfx);
|
||||
void tic_tool_str2buf(const char* str, s32 size, void* buf, bool flip);
|
||||
|
||||
u32 tic_tool_zip(void* dest, s32 destSize, const void* source, s32 size);
|
||||
u32 tic_tool_unzip(void* dest, s32 bufSize, const void* source, s32 size);
|
||||
|
||||
bool tic_tool_empty(const void* buffer, s32 size);
|
||||
#define EMPTY(BUFFER) (tic_tool_empty((BUFFER), sizeof (BUFFER)))
|
||||
|
||||
bool tic_tool_flat4(const void* buffer, s32 size);
|
||||
#define FLAT4(BUFFER) (tic_tool_flat4((BUFFER), sizeof (BUFFER)))
|
||||
|
||||
bool tic_tool_noise(const tic_waveform* wave);
|
||||
u32 tic_nearest_color(const tic_rgb* palette, const tic_rgb* color, s32 count);
|
||||
char* tic_tool_metatag(const char* code, const char* tag, const char* comment);
|
|
@ -0,0 +1,344 @@
|
|||
/* blip_buf $vers. http://www.slack.net/~ant/ */
|
||||
|
||||
#include "blip_buf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
|
||||
you can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
library is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#if defined (BLARGG_TEST) && BLARGG_TEST
|
||||
#include "blargg_test.h"
|
||||
#endif
|
||||
|
||||
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
|
||||
Avoids constants that don't fit in 32 bits. */
|
||||
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
|
||||
typedef unsigned long fixed_t;
|
||||
enum { pre_shift = 32 };
|
||||
|
||||
#elif defined(ULLONG_MAX)
|
||||
typedef unsigned long long fixed_t;
|
||||
enum { pre_shift = 32 };
|
||||
|
||||
#else
|
||||
typedef unsigned fixed_t;
|
||||
enum { pre_shift = 0 };
|
||||
|
||||
#endif
|
||||
|
||||
enum { time_bits = pre_shift + 20 };
|
||||
|
||||
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
|
||||
|
||||
enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
|
||||
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
|
||||
|
||||
enum { half_width = 8 };
|
||||
enum { buf_extra = half_width*2 + end_frame_extra };
|
||||
enum { phase_bits = 5 };
|
||||
enum { phase_count = 1 << phase_bits };
|
||||
enum { delta_bits = 15 };
|
||||
enum { delta_unit = 1 << delta_bits };
|
||||
enum { frac_bits = time_bits - pre_shift };
|
||||
|
||||
/* We could eliminate avail and encode whole samples in offset, but that would
|
||||
limit the total buffered samples to blip_max_frame. That could only be
|
||||
increased by decreasing time_bits, which would reduce resample ratio accuracy.
|
||||
*/
|
||||
|
||||
/** Sample buffer that resamples to output rate and accumulates samples
|
||||
until they're read out */
|
||||
struct blip_t
|
||||
{
|
||||
fixed_t factor;
|
||||
fixed_t offset;
|
||||
int avail;
|
||||
int size;
|
||||
int integrator;
|
||||
};
|
||||
|
||||
typedef int buf_t;
|
||||
|
||||
/* probably not totally portable */
|
||||
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
|
||||
|
||||
/* Arithmetic (sign-preserving) right shift */
|
||||
#define ARITH_SHIFT( n, shift ) \
|
||||
((n) >> (shift))
|
||||
|
||||
enum { max_sample = +32767 };
|
||||
enum { min_sample = -32768 };
|
||||
|
||||
#define CLAMP( n ) \
|
||||
{\
|
||||
if ( (short) n != n )\
|
||||
n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
|
||||
}
|
||||
|
||||
static void check_assumptions( void )
|
||||
{
|
||||
int n;
|
||||
|
||||
#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
|
||||
#error "int must be at least 32 bits"
|
||||
#endif
|
||||
|
||||
assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
|
||||
|
||||
n = max_sample * 2;
|
||||
CLAMP( n );
|
||||
assert( n == max_sample );
|
||||
|
||||
n = min_sample * 2;
|
||||
CLAMP( n );
|
||||
assert( n == min_sample );
|
||||
|
||||
assert( blip_max_ratio <= time_unit );
|
||||
assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
|
||||
}
|
||||
|
||||
blip_t* blip_new( int size )
|
||||
{
|
||||
blip_t* m;
|
||||
assert( size >= 0 );
|
||||
|
||||
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
|
||||
if ( m )
|
||||
{
|
||||
m->factor = time_unit / blip_max_ratio;
|
||||
m->size = size;
|
||||
blip_clear( m );
|
||||
check_assumptions();
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void blip_delete( blip_t* m )
|
||||
{
|
||||
if ( m != NULL )
|
||||
{
|
||||
/* Clear fields in case user tries to use after freeing */
|
||||
memset( m, 0, sizeof *m );
|
||||
free( m );
|
||||
}
|
||||
}
|
||||
|
||||
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
|
||||
{
|
||||
double factor = time_unit * sample_rate / clock_rate;
|
||||
m->factor = (fixed_t) factor;
|
||||
|
||||
/* Fails if clock_rate exceeds maximum, relative to sample_rate */
|
||||
assert( 0 <= factor - m->factor && factor - m->factor < 1 );
|
||||
|
||||
/* Avoid requiring math.h. Equivalent to
|
||||
m->factor = (int) ceil( factor ) */
|
||||
if ( m->factor < factor )
|
||||
m->factor++;
|
||||
|
||||
/* At this point, factor is most likely rounded up, but could still
|
||||
have been rounded down in the floating-point calculation. */
|
||||
}
|
||||
|
||||
void blip_clear( blip_t* m )
|
||||
{
|
||||
/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
|
||||
factor is rounded up. factor-1 is suitable if factor is rounded down.
|
||||
Since we don't know rounding direction, factor/2 accommodates either,
|
||||
with the slight loss of showing an error in half the time. Since for
|
||||
a 64-bit factor this is years, the halving isn't a problem. */
|
||||
|
||||
m->offset = m->factor / 2;
|
||||
m->avail = 0;
|
||||
m->integrator = 0;
|
||||
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
|
||||
}
|
||||
|
||||
int blip_clocks_needed( const blip_t* m, int samples )
|
||||
{
|
||||
fixed_t needed;
|
||||
|
||||
/* Fails if buffer can't hold that many more samples */
|
||||
assert( samples >= 0 && m->avail + samples <= m->size );
|
||||
|
||||
needed = (fixed_t) samples * time_unit;
|
||||
if ( needed < m->offset )
|
||||
return 0;
|
||||
|
||||
return (needed - m->offset + m->factor - 1) / m->factor;
|
||||
}
|
||||
|
||||
void blip_end_frame( blip_t* m, unsigned t )
|
||||
{
|
||||
fixed_t off = t * m->factor + m->offset;
|
||||
m->avail += off >> time_bits;
|
||||
m->offset = off & (time_unit - 1);
|
||||
|
||||
/* Fails if buffer size was exceeded */
|
||||
assert( m->avail <= m->size );
|
||||
}
|
||||
|
||||
int blip_samples_avail( const blip_t* m )
|
||||
{
|
||||
return m->avail;
|
||||
}
|
||||
|
||||
static void remove_samples( blip_t* m, int count )
|
||||
{
|
||||
buf_t* buf = SAMPLES( m );
|
||||
int remain = m->avail + buf_extra - count;
|
||||
m->avail -= count;
|
||||
|
||||
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
|
||||
memset( &buf [remain], 0, count * sizeof buf [0] );
|
||||
}
|
||||
|
||||
int blip_read_samples( blip_t* m, short out [], int count, int stereo )
|
||||
{
|
||||
assert( count >= 0 );
|
||||
|
||||
if ( count > m->avail )
|
||||
count = m->avail;
|
||||
|
||||
if ( count )
|
||||
{
|
||||
int const step = stereo ? 2 : 1;
|
||||
buf_t const* in = SAMPLES( m );
|
||||
buf_t const* end = in + count;
|
||||
int sum = m->integrator;
|
||||
do
|
||||
{
|
||||
/* Eliminate fraction */
|
||||
int s = ARITH_SHIFT( sum, delta_bits );
|
||||
|
||||
sum += *in++;
|
||||
|
||||
CLAMP( s );
|
||||
|
||||
*out = s;
|
||||
out += step;
|
||||
|
||||
/* High-pass filter */
|
||||
sum -= s << (delta_bits - bass_shift);
|
||||
}
|
||||
while ( in != end );
|
||||
m->integrator = sum;
|
||||
|
||||
remove_samples( m, count );
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Things that didn't help performance on x86:
|
||||
__attribute__((aligned(128)))
|
||||
#define short int
|
||||
restrict
|
||||
*/
|
||||
|
||||
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
|
||||
static short const bl_step [phase_count + 1] [half_width] =
|
||||
{
|
||||
{ 43, -115, 350, -488, 1136, -914, 5861,21022},
|
||||
{ 44, -118, 348, -473, 1076, -799, 5274,21001},
|
||||
{ 45, -121, 344, -454, 1011, -677, 4706,20936},
|
||||
{ 46, -122, 336, -431, 942, -549, 4156,20829},
|
||||
{ 47, -123, 327, -404, 868, -418, 3629,20679},
|
||||
{ 47, -122, 316, -375, 792, -285, 3124,20488},
|
||||
{ 47, -120, 303, -344, 714, -151, 2644,20256},
|
||||
{ 46, -117, 289, -310, 634, -17, 2188,19985},
|
||||
{ 46, -114, 273, -275, 553, 117, 1758,19675},
|
||||
{ 44, -108, 255, -237, 471, 247, 1356,19327},
|
||||
{ 43, -103, 237, -199, 390, 373, 981,18944},
|
||||
{ 42, -98, 218, -160, 310, 495, 633,18527},
|
||||
{ 40, -91, 198, -121, 231, 611, 314,18078},
|
||||
{ 38, -84, 178, -81, 153, 722, 22,17599},
|
||||
{ 36, -76, 157, -43, 80, 824, -241,17092},
|
||||
{ 34, -68, 135, -3, 8, 919, -476,16558},
|
||||
{ 32, -61, 115, 34, -60, 1006, -683,16001},
|
||||
{ 29, -52, 94, 70, -123, 1083, -862,15422},
|
||||
{ 27, -44, 73, 106, -184, 1152,-1015,14824},
|
||||
{ 25, -36, 53, 139, -239, 1211,-1142,14210},
|
||||
{ 22, -27, 34, 170, -290, 1261,-1244,13582},
|
||||
{ 20, -20, 16, 199, -335, 1301,-1322,12942},
|
||||
{ 18, -12, -3, 226, -375, 1331,-1376,12293},
|
||||
{ 15, -4, -19, 250, -410, 1351,-1408,11638},
|
||||
{ 13, 3, -35, 272, -439, 1361,-1419,10979},
|
||||
{ 11, 9, -49, 292, -464, 1362,-1410,10319},
|
||||
{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
|
||||
{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
|
||||
{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
|
||||
{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
|
||||
{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
|
||||
{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
|
||||
{ 0, 43, -115, 350, -488, 1136, -914, 5861}
|
||||
};
|
||||
|
||||
/* Shifting by pre_shift allows calculation using unsigned int rather than
|
||||
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
|
||||
And by having pre_shift 32, a 32-bit platform can easily do the shift by
|
||||
simply ignoring the low half. */
|
||||
|
||||
void blip_add_delta( blip_t* m, unsigned time, int delta )
|
||||
{
|
||||
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
|
||||
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
|
||||
|
||||
int const phase_shift = frac_bits - phase_bits;
|
||||
int phase = fixed >> phase_shift & (phase_count - 1);
|
||||
short const* in = bl_step [phase];
|
||||
short const* rev = bl_step [phase_count - phase];
|
||||
|
||||
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
|
||||
int delta2 = (delta * interp) >> delta_bits;
|
||||
delta -= delta2;
|
||||
|
||||
/* Fails if buffer size was exceeded */
|
||||
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
|
||||
|
||||
out [0] += in[0]*delta + in[half_width+0]*delta2;
|
||||
out [1] += in[1]*delta + in[half_width+1]*delta2;
|
||||
out [2] += in[2]*delta + in[half_width+2]*delta2;
|
||||
out [3] += in[3]*delta + in[half_width+3]*delta2;
|
||||
out [4] += in[4]*delta + in[half_width+4]*delta2;
|
||||
out [5] += in[5]*delta + in[half_width+5]*delta2;
|
||||
out [6] += in[6]*delta + in[half_width+6]*delta2;
|
||||
out [7] += in[7]*delta + in[half_width+7]*delta2;
|
||||
|
||||
in = rev;
|
||||
out [ 8] += in[7]*delta + in[7-half_width]*delta2;
|
||||
out [ 9] += in[6]*delta + in[6-half_width]*delta2;
|
||||
out [10] += in[5]*delta + in[5-half_width]*delta2;
|
||||
out [11] += in[4]*delta + in[4-half_width]*delta2;
|
||||
out [12] += in[3]*delta + in[3-half_width]*delta2;
|
||||
out [13] += in[2]*delta + in[2-half_width]*delta2;
|
||||
out [14] += in[1]*delta + in[1-half_width]*delta2;
|
||||
out [15] += in[0]*delta + in[0-half_width]*delta2;
|
||||
}
|
||||
|
||||
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
|
||||
{
|
||||
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
|
||||
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
|
||||
|
||||
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
|
||||
int delta2 = delta * interp;
|
||||
|
||||
/* Fails if buffer size was exceeded */
|
||||
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
|
||||
|
||||
out [7] += delta * delta_unit - delta2;
|
||||
out [8] += delta2;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/** \file
|
||||
Sample buffer that resamples from input clock rate to output sample rate */
|
||||
|
||||
/* blip_buf $vers */
|
||||
#ifndef BLIP_BUF_H
|
||||
#define BLIP_BUF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
|
||||
is changed. */
|
||||
typedef struct blip_t blip_t;
|
||||
|
||||
/** Creates new buffer that can hold at most sample_count samples. Sets rates
|
||||
so that there are blip_max_ratio clocks per sample. Returns pointer to new
|
||||
buffer, or NULL if insufficient memory. */
|
||||
blip_t* blip_new( int sample_count );
|
||||
|
||||
/** Sets approximate input clock rate and output sample rate. For every
|
||||
clock_rate input clocks, approximately sample_rate samples are generated. */
|
||||
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
|
||||
|
||||
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
|
||||
clock_rate must not be greater than sample_rate*blip_max_ratio. */
|
||||
blip_max_ratio = 1 << 20 };
|
||||
|
||||
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
|
||||
void blip_clear( blip_t* );
|
||||
|
||||
/** Adds positive/negative delta into buffer at specified clock time. */
|
||||
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
|
||||
|
||||
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
|
||||
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
|
||||
|
||||
/** Length of time frame, in clocks, needed to make sample_count additional
|
||||
samples available. */
|
||||
int blip_clocks_needed( const blip_t*, int sample_count );
|
||||
|
||||
enum { /** Maximum number of samples that can be generated from one time frame. */
|
||||
blip_max_frame = 4000 };
|
||||
|
||||
/** Makes input clocks before clock_duration available for reading as output
|
||||
samples. Also begins new time frame at clock_duration, so that clock time 0 in
|
||||
the new time frame specifies the same clock as clock_duration in the old time
|
||||
frame specified. Deltas can have been added slightly past clock_duration (up to
|
||||
however many clocks there are in two output samples). */
|
||||
void blip_end_frame( blip_t*, unsigned int clock_duration );
|
||||
|
||||
/** Number of buffered samples available for reading. */
|
||||
int blip_samples_avail( const blip_t* );
|
||||
|
||||
/** Reads and removes at most 'count' samples and writes them to 'out'. If
|
||||
'stereo' is true, writes output to every other element of 'out', allowing easy
|
||||
interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
|
||||
samples. Returns number of samples actually read. */
|
||||
int blip_read_samples( blip_t*, short out [], int count, int stereo );
|
||||
|
||||
/** Frees buffer. No effect if NULL is passed. */
|
||||
void blip_delete( blip_t* );
|
||||
|
||||
|
||||
/* Deprecated */
|
||||
typedef blip_t blip_buffer_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
|||
/*****************************************************************************
|
||||
|
||||
gif_err.c - handle error reporting for the GIF library.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_lib_private.h"
|
||||
|
||||
/*****************************************************************************
|
||||
Return a string description of the last GIF error
|
||||
*****************************************************************************/
|
||||
const char *
|
||||
GifErrorString(int ErrorCode)
|
||||
{
|
||||
const char *Err;
|
||||
|
||||
switch (ErrorCode) {
|
||||
case E_GIF_ERR_OPEN_FAILED:
|
||||
Err = "Failed to open given file";
|
||||
break;
|
||||
case E_GIF_ERR_WRITE_FAILED:
|
||||
Err = "Failed to write to given file";
|
||||
break;
|
||||
case E_GIF_ERR_HAS_SCRN_DSCR:
|
||||
Err = "Screen descriptor has already been set";
|
||||
break;
|
||||
case E_GIF_ERR_HAS_IMAG_DSCR:
|
||||
Err = "Image descriptor is still active";
|
||||
break;
|
||||
case E_GIF_ERR_NO_COLOR_MAP:
|
||||
Err = "Neither global nor local color map";
|
||||
break;
|
||||
case E_GIF_ERR_DATA_TOO_BIG:
|
||||
Err = "Number of pixels bigger than width * height";
|
||||
break;
|
||||
case E_GIF_ERR_NOT_ENOUGH_MEM:
|
||||
Err = "Failed to allocate required memory";
|
||||
break;
|
||||
case E_GIF_ERR_DISK_IS_FULL:
|
||||
Err = "Write failed (disk full?)";
|
||||
break;
|
||||
case E_GIF_ERR_CLOSE_FAILED:
|
||||
Err = "Failed to close given file";
|
||||
break;
|
||||
case E_GIF_ERR_NOT_WRITEABLE:
|
||||
Err = "Given file was not opened for write";
|
||||
break;
|
||||
case D_GIF_ERR_OPEN_FAILED:
|
||||
Err = "Failed to open given file";
|
||||
break;
|
||||
case D_GIF_ERR_READ_FAILED:
|
||||
Err = "Failed to read from given file";
|
||||
break;
|
||||
case D_GIF_ERR_NOT_GIF_FILE:
|
||||
Err = "Data is not in GIF format";
|
||||
break;
|
||||
case D_GIF_ERR_NO_SCRN_DSCR:
|
||||
Err = "No screen descriptor detected";
|
||||
break;
|
||||
case D_GIF_ERR_NO_IMAG_DSCR:
|
||||
Err = "No Image Descriptor detected";
|
||||
break;
|
||||
case D_GIF_ERR_NO_COLOR_MAP:
|
||||
Err = "Neither global nor local color map";
|
||||
break;
|
||||
case D_GIF_ERR_WRONG_RECORD:
|
||||
Err = "Wrong record type detected";
|
||||
break;
|
||||
case D_GIF_ERR_DATA_TOO_BIG:
|
||||
Err = "Number of pixels bigger than width * height";
|
||||
break;
|
||||
case D_GIF_ERR_NOT_ENOUGH_MEM:
|
||||
Err = "Failed to allocate required memory";
|
||||
break;
|
||||
case D_GIF_ERR_CLOSE_FAILED:
|
||||
Err = "Failed to close given file";
|
||||
break;
|
||||
case D_GIF_ERR_NOT_READABLE:
|
||||
Err = "Given file was not opened for read";
|
||||
break;
|
||||
case D_GIF_ERR_IMAGE_DEFECT:
|
||||
Err = "Image is defective, decoding aborted";
|
||||
break;
|
||||
case D_GIF_ERR_EOF_TOO_SOON:
|
||||
Err = "Image EOF detected before image complete";
|
||||
break;
|
||||
default:
|
||||
Err = NULL;
|
||||
break;
|
||||
}
|
||||
return Err;
|
||||
}
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,261 @@
|
|||
/*****************************************************************************
|
||||
|
||||
gif_font.c - utility font handling and simple drawing for the GIF library
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
|
||||
/*****************************************************************************
|
||||
Ascii 8 by 8 regular font - only first 128 characters are supported.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Each array entry holds the bits for 8 horizontal scan lines, topmost
|
||||
* first. The most significant bit of each constant is the leftmost bit of
|
||||
* the scan line.
|
||||
*/
|
||||
/*@+charint@*/
|
||||
const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* Ascii 0 */
|
||||
{0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00}, /* Ascii 1 */
|
||||
{0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00}, /* Ascii 2 */
|
||||
{0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 3 */
|
||||
{0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 4 */
|
||||
{0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00}, /* Ascii 5 */
|
||||
{0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00}, /* Ascii 6 */
|
||||
{0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00}, /* Ascii 7 */
|
||||
{0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff}, /* Ascii 8 */
|
||||
{0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00}, /* Ascii 9 */
|
||||
{0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff}, /* Ascii 10 */
|
||||
{0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* Ascii 11 */
|
||||
{0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18}, /* Ascii 12 */
|
||||
{0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00}, /* Ascii 13 */
|
||||
{0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f}, /* Ascii 14 */
|
||||
{0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00}, /* Ascii 15 */
|
||||
{0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00}, /* Ascii 16 */
|
||||
{0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00}, /* Ascii 17 */
|
||||
{0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18}, /* Ascii 18 */
|
||||
{0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, /* Ascii 19 */
|
||||
{0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00}, /* Ascii 20 */
|
||||
{0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e}, /* Ascii 21 */
|
||||
{0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00}, /* Ascii 22 */
|
||||
{0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff}, /* Ascii 23 */
|
||||
{0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* Ascii 24 */
|
||||
{0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00}, /* Ascii 25 */
|
||||
{0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00}, /* Ascii 26 */
|
||||
{0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00}, /* Ascii 27 */
|
||||
{0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00}, /* Ascii 28 */
|
||||
{0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, /* Ascii 29 */
|
||||
{0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00}, /* Ascii 30 */
|
||||
{0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 31 */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */
|
||||
{0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00}, /* ! */
|
||||
{0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */
|
||||
{0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, /* # */
|
||||
{0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00}, /* $ */
|
||||
{0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00}, /* % */
|
||||
{0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00}, /* & */
|
||||
{0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */
|
||||
{0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00}, /* ( */
|
||||
{0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00}, /* ) */
|
||||
{0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00}, /* * */
|
||||
{0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00}, /* + */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70}, /* , */
|
||||
{0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}, /* - */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00}, /* . */
|
||||
{0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00}, /* / */
|
||||
{0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* 0 */
|
||||
{0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* 1 */
|
||||
{0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00}, /* 2 */
|
||||
{0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00}, /* 3 */
|
||||
{0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00}, /* 4 */
|
||||
{0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00}, /* 5 */
|
||||
{0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00}, /* 6 */
|
||||
{0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00}, /* 7 */
|
||||
{0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* 8 */
|
||||
{0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00}, /* 9 */
|
||||
{0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, /* : */
|
||||
{0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00}, /* }, */
|
||||
{0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00}, /* < */
|
||||
{0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}, /* = */
|
||||
{0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00}, /* > */
|
||||
{0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, /* ? */
|
||||
{0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00}, /* @ */
|
||||
{0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, /* A */
|
||||
{0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00}, /* B */
|
||||
{0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00}, /* C */
|
||||
{0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00}, /* D */
|
||||
{0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00}, /* E */
|
||||
{0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* F */
|
||||
{0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00}, /* G */
|
||||
{0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00}, /* H */
|
||||
{0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, /* I */
|
||||
{0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00}, /* J */
|
||||
{0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00}, /* K */
|
||||
{0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00}, /* L */
|
||||
{0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00}, /* M */
|
||||
{0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, /* N */
|
||||
{0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* O */
|
||||
{0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* P */
|
||||
{0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06}, /* Q */
|
||||
{0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00}, /* R */
|
||||
{0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00}, /* S */
|
||||
{0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, /* T */
|
||||
{0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* U */
|
||||
{0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* V */
|
||||
{0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, /* W */
|
||||
{0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00}, /* X */
|
||||
{0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00}, /* Y */
|
||||
{0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00}, /* Z */
|
||||
{0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00}, /* [ */
|
||||
{0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00}, /* \ */
|
||||
{0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00}, /* ] */
|
||||
{0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, /* ^ */
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, /* _ */
|
||||
{0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */
|
||||
{0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00}, /* a */
|
||||
{0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00}, /* b */
|
||||
{0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00}, /* c */
|
||||
{0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00}, /* d */
|
||||
{0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00}, /* e */
|
||||
{0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00}, /* f */
|
||||
{0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* g */
|
||||
{0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* */
|
||||
{0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* i */
|
||||
{0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0}, /* j */
|
||||
{0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00}, /* k */
|
||||
{0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* l */
|
||||
{0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, /* m */
|
||||
{0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* n */
|
||||
{0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* o */
|
||||
{0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0}, /* p */
|
||||
{0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06}, /* q */
|
||||
{0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00}, /* r */
|
||||
{0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00}, /* s */
|
||||
{0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00}, /* t */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00}, /* u */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* v */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00}, /* w */
|
||||
{0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, /* x */
|
||||
{0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* y */
|
||||
{0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00}, /* z */
|
||||
{0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00}, /* { */
|
||||
{0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */
|
||||
{0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, /* } */
|
||||
{0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00}, /* ~ */
|
||||
{0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00} /* Ascii 127 */
|
||||
};
|
||||
/*@=charint@*/
|
||||
|
||||
void
|
||||
GifDrawText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int color)
|
||||
{
|
||||
int i, j;
|
||||
const char *cp;
|
||||
|
||||
for (i = 0; i < GIF_FONT_HEIGHT; i++) {
|
||||
int base = Image->ImageDesc.Width * (y + i) + x;
|
||||
|
||||
for (cp = legend; *cp; cp++)
|
||||
for (j = 0; j < GIF_FONT_WIDTH; j++) {
|
||||
if (GifAsciiTable8x8[(short)(*cp)][i] & (1 << (GIF_FONT_WIDTH - j)))
|
||||
Image->RasterBits[base] = color;
|
||||
base++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GifDrawBox(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d,
|
||||
const int color)
|
||||
{
|
||||
int j, base = Image->ImageDesc.Width * y + x;
|
||||
|
||||
for (j = 0; j < w; j++)
|
||||
Image->RasterBits[base + j] =
|
||||
Image->RasterBits[base + (d * Image->ImageDesc.Width) + j] = color;
|
||||
|
||||
for (j = 0; j < d; j++)
|
||||
Image->RasterBits[base + j * Image->ImageDesc.Width] =
|
||||
Image->RasterBits[base + j * Image->ImageDesc.Width + w] = color;
|
||||
}
|
||||
|
||||
void
|
||||
GifDrawRectangle(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d,
|
||||
const int color)
|
||||
{
|
||||
unsigned char *bp = Image->RasterBits + Image->ImageDesc.Width * y + x;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < d; i++)
|
||||
memset(bp + (i * Image->ImageDesc.Width), color, (size_t)w);
|
||||
}
|
||||
|
||||
void
|
||||
GifDrawBoxedText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int border,
|
||||
const int bg, const int fg)
|
||||
{
|
||||
int j = 0, LineCount = 0, TextWidth = 0;
|
||||
const char *cp;
|
||||
char *dup;
|
||||
|
||||
/* compute size of text to box */
|
||||
for (cp = legend; *cp; cp++)
|
||||
if (*cp == '\r') {
|
||||
if (j > TextWidth)
|
||||
TextWidth = j;
|
||||
j = 0;
|
||||
LineCount++;
|
||||
} else if (*cp != '\t')
|
||||
++j;
|
||||
LineCount++; /* count last line */
|
||||
if (j > TextWidth) /* last line might be longer than any previous */
|
||||
TextWidth = j;
|
||||
|
||||
/* draw the text */
|
||||
dup = malloc(strlen(legend)+1);
|
||||
/* FIXME: should return bad status, but that would require API change */
|
||||
if (dup != NULL) {
|
||||
int i = 0;
|
||||
/* fill the box */
|
||||
GifDrawRectangle(Image, x + 1, y + 1,
|
||||
border + TextWidth * GIF_FONT_WIDTH + border - 1,
|
||||
border + LineCount * GIF_FONT_HEIGHT + border - 1, bg);
|
||||
(void)strcpy(dup, (char *)legend);
|
||||
char *lasts;
|
||||
cp = strtok_r(dup, "\r\n", &lasts);
|
||||
do {
|
||||
int leadspace = 0;
|
||||
|
||||
if (cp[0] == '\t')
|
||||
leadspace = (TextWidth - strlen(++cp)) / 2;
|
||||
|
||||
GifDrawText8x8(Image, x + border + (leadspace * GIF_FONT_WIDTH),
|
||||
y + border + (GIF_FONT_HEIGHT * i++), cp, fg);
|
||||
cp = strtok_r(NULL, "\r\n", &lasts);
|
||||
} while (cp);
|
||||
(void)free((void *)dup);
|
||||
|
||||
/* outline the box */
|
||||
GifDrawBox(Image, x, y, border + TextWidth * GIF_FONT_WIDTH + border,
|
||||
border + LineCount * GIF_FONT_HEIGHT + border, fg);
|
||||
}
|
||||
}
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,133 @@
|
|||
/*****************************************************************************
|
||||
|
||||
gif_hash.c -- module to support the following operations:
|
||||
|
||||
1. InitHashTable - initialize hash table.
|
||||
2. ClearHashTable - clear the hash table to an empty state.
|
||||
2. InsertHashTable - insert one item into data structure.
|
||||
3. ExistsHashTable - test if item exists in data structure.
|
||||
|
||||
This module is used to hash the GIF codes during encoding.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_hash.h"
|
||||
#include "gif_lib_private.h"
|
||||
|
||||
/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
static long NumberOfTests = 0,
|
||||
NumberOfMisses = 0;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
static int KeyItem(uint32_t Item);
|
||||
|
||||
/******************************************************************************
|
||||
Initialize HashTable - allocate the memory needed and clear it. *
|
||||
******************************************************************************/
|
||||
GifHashTableType *_InitHashTable(void)
|
||||
{
|
||||
GifHashTableType *HashTable;
|
||||
|
||||
if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType)))
|
||||
== NULL)
|
||||
return NULL;
|
||||
|
||||
_ClearHashTable(HashTable);
|
||||
|
||||
return HashTable;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to clear the HashTable to an empty state. *
|
||||
This part is a little machine depended. Use the commented part otherwise. *
|
||||
******************************************************************************/
|
||||
void _ClearHashTable(GifHashTableType *HashTable)
|
||||
{
|
||||
memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to insert a new Item into the HashTable. The data is assumed to be *
|
||||
new one. *
|
||||
******************************************************************************/
|
||||
void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code)
|
||||
{
|
||||
int HKey = KeyItem(Key);
|
||||
uint32_t *HTable = HashTable -> HTable;
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfTests++;
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) {
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
HKey = (HKey + 1) & HT_KEY_MASK;
|
||||
}
|
||||
HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to test if given Key exists in HashTable and if so returns its code *
|
||||
Returns the Code if key was found, -1 if not. *
|
||||
******************************************************************************/
|
||||
int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key)
|
||||
{
|
||||
int HKey = KeyItem(Key);
|
||||
uint32_t *HTable = HashTable -> HTable, HTKey;
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfTests++;
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) {
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
NumberOfMisses++;
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
if (Key == HTKey) return HT_GET_CODE(HTable[HKey]);
|
||||
HKey = (HKey + 1) & HT_KEY_MASK;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Routine to generate an HKey for the hashtable out of the given unique key. *
|
||||
The given Key is assumed to be 20 bits as follows: lower 8 bits are the *
|
||||
new postfix character, while the upper 12 bits are the prefix code. *
|
||||
Because the average hit ratio is only 2 (2 hash references per entry), *
|
||||
evaluating more complex keys (such as twin prime keys) does not worth it! *
|
||||
******************************************************************************/
|
||||
static int KeyItem(uint32_t Item)
|
||||
{
|
||||
return ((Item >> 12) ^ Item) & HT_KEY_MASK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_HIT_RATE
|
||||
/******************************************************************************
|
||||
Debugging routine to print the hit ratio - number of times the hash table *
|
||||
was tested per operation. This routine was used to test the KeyItem routine *
|
||||
******************************************************************************/
|
||||
void HashTablePrintHitRatio(void)
|
||||
{
|
||||
printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n",
|
||||
NumberOfMisses, NumberOfTests,
|
||||
NumberOfMisses * 100 / NumberOfTests);
|
||||
}
|
||||
#endif /* DEBUG_HIT_RATE */
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,41 @@
|
|||
/******************************************************************************
|
||||
|
||||
gif_hash.h - magfic constants and declarations for GIF LZW
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _GIF_HASH_H_
|
||||
#define _GIF_HASH_H_
|
||||
|
||||
//#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */
|
||||
#define HT_KEY_MASK 0x1FFF /* 13bits keys */
|
||||
#define HT_KEY_NUM_BITS 13 /* 13bits keys */
|
||||
#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */
|
||||
#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
|
||||
|
||||
/* The 32 bits of the long are divided into two parts for the key & code: */
|
||||
/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */
|
||||
/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */
|
||||
/* The key is the upper 20 bits. The code is the lower 12. */
|
||||
#define HT_GET_KEY(l) (l >> 12)
|
||||
#define HT_GET_CODE(l) (l & 0x0FFF)
|
||||
#define HT_PUT_KEY(l) (l << 12)
|
||||
#define HT_PUT_CODE(l) (l & 0x0FFF)
|
||||
|
||||
typedef struct GifHashTableType {
|
||||
uint32_t HTable[HT_SIZE];
|
||||
} GifHashTableType;
|
||||
|
||||
GifHashTableType *_InitHashTable(void);
|
||||
void _ClearHashTable(GifHashTableType *HashTable);
|
||||
void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code);
|
||||
int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key);
|
||||
|
||||
#endif /* _GIF_HASH_H_ */
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,303 @@
|
|||
/******************************************************************************
|
||||
|
||||
gif_lib.h - service library for decoding and encoding GIF images
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _GIF_LIB_H_
|
||||
#define _GIF_LIB_H_ 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define GIFLIB_MAJOR 5
|
||||
#define GIFLIB_MINOR 2
|
||||
#define GIFLIB_RELEASE 1
|
||||
|
||||
#define GIF_ERROR 0
|
||||
#define GIF_OK 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */
|
||||
#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
|
||||
#define GIF_VERSION_POS 3 /* Version first character in stamp. */
|
||||
#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */
|
||||
#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */
|
||||
|
||||
typedef unsigned char GifPixelType;
|
||||
typedef unsigned char *GifRowType;
|
||||
typedef unsigned char GifByteType;
|
||||
typedef unsigned int GifPrefixType;
|
||||
typedef int GifWord;
|
||||
|
||||
typedef struct GifColorType {
|
||||
GifByteType Red, Green, Blue;
|
||||
} GifColorType;
|
||||
|
||||
typedef struct ColorMapObject {
|
||||
int ColorCount;
|
||||
int BitsPerPixel;
|
||||
bool SortFlag;
|
||||
GifColorType *Colors; /* on malloc(3) heap */
|
||||
} ColorMapObject;
|
||||
|
||||
typedef struct GifImageDesc {
|
||||
GifWord Left, Top, Width, Height; /* Current image dimensions. */
|
||||
bool Interlace; /* Sequential/Interlaced lines. */
|
||||
ColorMapObject *ColorMap; /* The local color map */
|
||||
} GifImageDesc;
|
||||
|
||||
typedef struct ExtensionBlock {
|
||||
int ByteCount;
|
||||
GifByteType *Bytes; /* on malloc(3) heap */
|
||||
int Function; /* The block function code */
|
||||
#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */
|
||||
#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */
|
||||
#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
|
||||
#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */
|
||||
#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */
|
||||
} ExtensionBlock;
|
||||
|
||||
typedef struct SavedImage {
|
||||
GifImageDesc ImageDesc;
|
||||
GifByteType *RasterBits; /* on malloc(3) heap */
|
||||
int ExtensionBlockCount; /* Count of extensions before image */
|
||||
ExtensionBlock *ExtensionBlocks; /* Extensions before image */
|
||||
} SavedImage;
|
||||
|
||||
typedef struct GifFileType {
|
||||
GifWord SWidth, SHeight; /* Size of virtual canvas */
|
||||
GifWord SColorResolution; /* How many colors can we generate? */
|
||||
GifWord SBackGroundColor; /* Background color for virtual canvas */
|
||||
GifByteType AspectByte; /* Used to compute pixel aspect ratio */
|
||||
ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */
|
||||
int ImageCount; /* Number of current image (both APIs) */
|
||||
GifImageDesc Image; /* Current image (low-level API) */
|
||||
SavedImage *SavedImages; /* Image sequence (high-level API) */
|
||||
int ExtensionBlockCount; /* Count extensions past last image */
|
||||
ExtensionBlock *ExtensionBlocks; /* Extensions past last image */
|
||||
int Error; /* Last error condition reported */
|
||||
void *UserData; /* hook to attach user data (TVT) */
|
||||
void *Private; /* Don't mess with this! */
|
||||
} GifFileType;
|
||||
|
||||
#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0)
|
||||
|
||||
typedef enum {
|
||||
UNDEFINED_RECORD_TYPE,
|
||||
SCREEN_DESC_RECORD_TYPE,
|
||||
IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */
|
||||
EXTENSION_RECORD_TYPE, /* Begin with '!' */
|
||||
TERMINATE_RECORD_TYPE /* Begin with ';' */
|
||||
} GifRecordType;
|
||||
|
||||
/* func type to read gif data from arbitrary sources (TVT) */
|
||||
typedef int (*InputFunc) (GifFileType *, GifByteType *, int);
|
||||
|
||||
/* func type to write gif data to arbitrary targets.
|
||||
* Returns count of bytes written. (MRB)
|
||||
*/
|
||||
typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
|
||||
|
||||
/******************************************************************************
|
||||
GIF89 structures
|
||||
******************************************************************************/
|
||||
|
||||
typedef struct GraphicsControlBlock {
|
||||
int DisposalMode;
|
||||
#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
|
||||
#define DISPOSE_DO_NOT 1 /* Leave image in place */
|
||||
#define DISPOSE_BACKGROUND 2 /* Set area too background color */
|
||||
#define DISPOSE_PREVIOUS 3 /* Restore to previous content */
|
||||
bool UserInputFlag; /* User confirmation required before disposal */
|
||||
int DelayTime; /* pre-display delay in 0.01sec units */
|
||||
int TransparentColor; /* Palette index for transparency, -1 if none */
|
||||
#define NO_TRANSPARENT_COLOR -1
|
||||
} GraphicsControlBlock;
|
||||
|
||||
/******************************************************************************
|
||||
GIF encoding routines
|
||||
******************************************************************************/
|
||||
|
||||
/* Main entry points */
|
||||
GifFileType *EGifOpenFileName(const char *GifFileName,
|
||||
const bool GifTestExistence, int *Error);
|
||||
GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error);
|
||||
GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error);
|
||||
int EGifSpew(GifFileType * GifFile);
|
||||
const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */
|
||||
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode);
|
||||
|
||||
#define E_GIF_SUCCEEDED 0
|
||||
#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */
|
||||
#define E_GIF_ERR_WRITE_FAILED 2
|
||||
#define E_GIF_ERR_HAS_SCRN_DSCR 3
|
||||
#define E_GIF_ERR_HAS_IMAG_DSCR 4
|
||||
#define E_GIF_ERR_NO_COLOR_MAP 5
|
||||
#define E_GIF_ERR_DATA_TOO_BIG 6
|
||||
#define E_GIF_ERR_NOT_ENOUGH_MEM 7
|
||||
#define E_GIF_ERR_DISK_IS_FULL 8
|
||||
#define E_GIF_ERR_CLOSE_FAILED 9
|
||||
#define E_GIF_ERR_NOT_WRITEABLE 10
|
||||
|
||||
/* These are legacy. You probably do not want to call them directly */
|
||||
int EGifPutScreenDesc(GifFileType *GifFile,
|
||||
const int GifWidth, const int GifHeight,
|
||||
const int GifColorRes,
|
||||
const int GifBackGround,
|
||||
const ColorMapObject *GifColorMap);
|
||||
int EGifPutImageDesc(GifFileType *GifFile,
|
||||
const int GifLeft, const int GifTop,
|
||||
const int GifWidth, const int GifHeight,
|
||||
const bool GifInterlace,
|
||||
const ColorMapObject *GifColorMap);
|
||||
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
|
||||
int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine,
|
||||
int GifLineLen);
|
||||
int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel);
|
||||
int EGifPutComment(GifFileType *GifFile, const char *GifComment);
|
||||
int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode);
|
||||
int EGifPutExtensionBlock(GifFileType *GifFile,
|
||||
const int GifExtLen, const void *GifExtension);
|
||||
int EGifPutExtensionTrailer(GifFileType *GifFile);
|
||||
int EGifPutExtension(GifFileType *GifFile, const int GifExtCode,
|
||||
const int GifExtLen,
|
||||
const void *GifExtension);
|
||||
int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
|
||||
const GifByteType *GifCodeBlock);
|
||||
int EGifPutCodeNext(GifFileType *GifFile,
|
||||
const GifByteType *GifCodeBlock);
|
||||
|
||||
/******************************************************************************
|
||||
GIF decoding routines
|
||||
******************************************************************************/
|
||||
|
||||
/* Main entry points */
|
||||
GifFileType *DGifOpenFileName(const char *GifFileName, int *Error);
|
||||
GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error);
|
||||
int DGifSlurp(GifFileType * GifFile);
|
||||
GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */
|
||||
int DGifCloseFile(GifFileType * GifFile, int *ErrorCode);
|
||||
|
||||
#define D_GIF_SUCCEEDED 0
|
||||
#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */
|
||||
#define D_GIF_ERR_READ_FAILED 102
|
||||
#define D_GIF_ERR_NOT_GIF_FILE 103
|
||||
#define D_GIF_ERR_NO_SCRN_DSCR 104
|
||||
#define D_GIF_ERR_NO_IMAG_DSCR 105
|
||||
#define D_GIF_ERR_NO_COLOR_MAP 106
|
||||
#define D_GIF_ERR_WRONG_RECORD 107
|
||||
#define D_GIF_ERR_DATA_TOO_BIG 108
|
||||
#define D_GIF_ERR_NOT_ENOUGH_MEM 109
|
||||
#define D_GIF_ERR_CLOSE_FAILED 110
|
||||
#define D_GIF_ERR_NOT_READABLE 111
|
||||
#define D_GIF_ERR_IMAGE_DEFECT 112
|
||||
#define D_GIF_ERR_EOF_TOO_SOON 113
|
||||
|
||||
/* These are legacy. You probably do not want to call them directly */
|
||||
int DGifGetScreenDesc(GifFileType *GifFile);
|
||||
int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);
|
||||
int DGifGetImageHeader(GifFileType *GifFile);
|
||||
int DGifGetImageDesc(GifFileType *GifFile);
|
||||
int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
|
||||
int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
|
||||
int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,
|
||||
GifByteType **GifExtension);
|
||||
int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension);
|
||||
int DGifGetCode(GifFileType *GifFile, int *GifCodeSize,
|
||||
GifByteType **GifCodeBlock);
|
||||
int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
|
||||
int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
|
||||
const char *DGifGetGifVersion(GifFileType *GifFile);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Error handling and reporting.
|
||||
******************************************************************************/
|
||||
extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */
|
||||
|
||||
/*****************************************************************************
|
||||
Everything below this point is new after version 1.2, supporting `slurp
|
||||
mode' for doing I/O in two big belts with all the image-bashing in core.
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
Color map handling from gif_alloc.c
|
||||
******************************************************************************/
|
||||
|
||||
extern ColorMapObject *GifMakeMapObject(int ColorCount,
|
||||
const GifColorType *ColorMap);
|
||||
extern void GifFreeMapObject(ColorMapObject *Object);
|
||||
extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
|
||||
const ColorMapObject *ColorIn2,
|
||||
GifPixelType ColorTransIn2[]);
|
||||
extern int GifBitSize(int n);
|
||||
|
||||
/******************************************************************************
|
||||
Support for the in-core structures allocation (slurp mode).
|
||||
******************************************************************************/
|
||||
|
||||
extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
|
||||
extern int GifAddExtensionBlock(int *ExtensionBlock_Count,
|
||||
ExtensionBlock **ExtensionBlocks,
|
||||
int Function,
|
||||
unsigned int Len, unsigned char ExtData[]);
|
||||
extern void GifFreeExtensions(int *ExtensionBlock_Count,
|
||||
ExtensionBlock **ExtensionBlocks);
|
||||
extern SavedImage *GifMakeSavedImage(GifFileType *GifFile,
|
||||
const SavedImage *CopyFrom);
|
||||
extern void GifFreeSavedImages(GifFileType *GifFile);
|
||||
|
||||
/******************************************************************************
|
||||
5.x functions for GIF89 graphics control blocks
|
||||
******************************************************************************/
|
||||
|
||||
int DGifExtensionToGCB(const size_t GifExtensionLength,
|
||||
const GifByteType *GifExtension,
|
||||
GraphicsControlBlock *GCB);
|
||||
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
|
||||
GifByteType *GifExtension);
|
||||
|
||||
int DGifSavedExtensionToGCB(GifFileType *GifFile,
|
||||
int ImageIndex,
|
||||
GraphicsControlBlock *GCB);
|
||||
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
|
||||
GifFileType *GifFile,
|
||||
int ImageIndex);
|
||||
|
||||
/******************************************************************************
|
||||
The library's internal utility font
|
||||
******************************************************************************/
|
||||
|
||||
#define GIF_FONT_WIDTH 8
|
||||
#define GIF_FONT_HEIGHT 8
|
||||
extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH];
|
||||
|
||||
extern void GifDrawText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend, const int color);
|
||||
|
||||
extern void GifDrawBox(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d, const int color);
|
||||
|
||||
extern void GifDrawRectangle(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const int w, const int d, const int color);
|
||||
|
||||
extern void GifDrawBoxedText8x8(SavedImage *Image,
|
||||
const int x, const int y,
|
||||
const char *legend,
|
||||
const int border, const int bg, const int fg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _GIF_LIB_H */
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,70 @@
|
|||
/****************************************************************************
|
||||
|
||||
gif_lib_private.h - internal giflib routines and structures
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _GIF_LIB_PRIVATE_H
|
||||
#define _GIF_LIB_PRIVATE_H
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_hash.h"
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX UINTPTR_MAX
|
||||
#endif
|
||||
|
||||
#define EXTENSION_INTRODUCER 0x21
|
||||
#define DESCRIPTOR_INTRODUCER 0x2c
|
||||
#define TERMINATOR_INTRODUCER 0x3b
|
||||
|
||||
#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
|
||||
#define LZ_BITS 12
|
||||
|
||||
#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */
|
||||
#define FIRST_CODE 4097 /* Impossible code, to signal first. */
|
||||
#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */
|
||||
|
||||
#define FILE_STATE_WRITE 0x01
|
||||
#define FILE_STATE_SCREEN 0x02
|
||||
#define FILE_STATE_IMAGE 0x04
|
||||
#define FILE_STATE_READ 0x08
|
||||
|
||||
#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ)
|
||||
#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE)
|
||||
|
||||
typedef struct GifFilePrivateType {
|
||||
GifWord FileState, FileHandle, /* Where all this data goes to! */
|
||||
BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */
|
||||
ClearCode, /* The CLEAR LZ code. */
|
||||
EOFCode, /* The EOF LZ code. */
|
||||
RunningCode, /* The next code algorithm can generate. */
|
||||
RunningBits, /* The number of bits required to represent RunningCode. */
|
||||
MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */
|
||||
LastCode, /* The code before the current code. */
|
||||
CrntCode, /* Current algorithm code. */
|
||||
StackPtr, /* For character stack (see below). */
|
||||
CrntShiftState; /* Number of bits in CrntShiftDWord. */
|
||||
unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */
|
||||
unsigned long PixelCount; /* Number of pixels in image. */
|
||||
FILE *File; /* File as stream. */
|
||||
InputFunc Read; /* function to read gif input (TVT) */
|
||||
OutputFunc Write; /* function to write gif output (MRB) */
|
||||
GifByteType Buf[256]; /* Compressed input is buffered here. */
|
||||
GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */
|
||||
GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */
|
||||
GifPrefixType Prefix[LZ_MAX_CODE + 1];
|
||||
GifHashTableType *HashTable;
|
||||
bool gif89;
|
||||
} GifFilePrivateType;
|
||||
|
||||
#ifndef HAVE_REALLOCARRAY
|
||||
extern void *openbsd_reallocarray(void *optr, size_t nmemb, size_t size);
|
||||
#define reallocarray openbsd_reallocarray
|
||||
#endif
|
||||
|
||||
#endif /* _GIF_LIB_PRIVATE_H */
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,420 @@
|
|||
/*****************************************************************************
|
||||
|
||||
GIF construction tools
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gif_lib.h"
|
||||
#include "gif_lib_private.h"
|
||||
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
/******************************************************************************
|
||||
Miscellaneous utility functions
|
||||
******************************************************************************/
|
||||
|
||||
/* return smallest bitfield size n will fit in */
|
||||
int
|
||||
GifBitSize(int n)
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 1; i <= 8; i++)
|
||||
if ((1 << i) >= n)
|
||||
break;
|
||||
return (i);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Color map object functions
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
* Allocate a color map of given size; initialize with contents of
|
||||
* ColorMap if that pointer is non-NULL.
|
||||
*/
|
||||
ColorMapObject *
|
||||
GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
|
||||
{
|
||||
ColorMapObject *Object;
|
||||
|
||||
/*** FIXME: Our ColorCount has to be a power of two. Is it necessary to
|
||||
* make the user know that or should we automatically round up instead? */
|
||||
if (ColorCount != (1 << GifBitSize(ColorCount))) {
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
|
||||
if (Object == (ColorMapObject *) NULL) {
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
|
||||
if (Object->Colors == (GifColorType *) NULL) {
|
||||
free(Object);
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
Object->ColorCount = ColorCount;
|
||||
Object->BitsPerPixel = GifBitSize(ColorCount);
|
||||
Object->SortFlag = false;
|
||||
|
||||
if (ColorMap != NULL) {
|
||||
memcpy((char *)Object->Colors,
|
||||
(char *)ColorMap, ColorCount * sizeof(GifColorType));
|
||||
}
|
||||
|
||||
return (Object);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Free a color map object
|
||||
*******************************************************************************/
|
||||
void
|
||||
GifFreeMapObject(ColorMapObject *Object)
|
||||
{
|
||||
if (Object != NULL) {
|
||||
(void)free(Object->Colors);
|
||||
(void)free(Object);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
DumpColorMap(ColorMapObject *Object,
|
||||
FILE * fp)
|
||||
{
|
||||
if (Object != NULL) {
|
||||
int i, j, Len = Object->ColorCount;
|
||||
|
||||
for (i = 0; i < Len; i += 4) {
|
||||
for (j = 0; j < 4 && j < Len; j++) {
|
||||
(void)fprintf(fp, "%3d: %02x %02x %02x ", i + j,
|
||||
Object->Colors[i + j].Red,
|
||||
Object->Colors[i + j].Green,
|
||||
Object->Colors[i + j].Blue);
|
||||
}
|
||||
(void)fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*******************************************************************************
|
||||
Compute the union of two given color maps and return it. If result can't
|
||||
fit into 256 colors, NULL is returned, the allocated union otherwise.
|
||||
ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
|
||||
copied iff they didn't exist before. ColorTransIn2 maps the old
|
||||
ColorIn2 into the ColorUnion color map table./
|
||||
*******************************************************************************/
|
||||
ColorMapObject *
|
||||
GifUnionColorMap(const ColorMapObject *ColorIn1,
|
||||
const ColorMapObject *ColorIn2,
|
||||
GifPixelType ColorTransIn2[])
|
||||
{
|
||||
int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
|
||||
ColorMapObject *ColorUnion;
|
||||
|
||||
/*
|
||||
* We don't worry about duplicates within either color map; if
|
||||
* the caller wants to resolve those, he can perform unions
|
||||
* with an empty color map.
|
||||
*/
|
||||
|
||||
/* Allocate table which will hold the result for sure. */
|
||||
ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
|
||||
ColorIn2->ColorCount) * 2, NULL);
|
||||
|
||||
if (ColorUnion == NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Copy ColorIn1 to ColorUnion.
|
||||
*/
|
||||
for (i = 0; i < ColorIn1->ColorCount; i++)
|
||||
ColorUnion->Colors[i] = ColorIn1->Colors[i];
|
||||
CrntSlot = ColorIn1->ColorCount;
|
||||
|
||||
/*
|
||||
* Potentially obnoxious hack:
|
||||
*
|
||||
* Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
|
||||
* of table 1. This is very useful if your display is limited to
|
||||
* 16 colors.
|
||||
*/
|
||||
while (ColorIn1->Colors[CrntSlot - 1].Red == 0
|
||||
&& ColorIn1->Colors[CrntSlot - 1].Green == 0
|
||||
&& ColorIn1->Colors[CrntSlot - 1].Blue == 0)
|
||||
CrntSlot--;
|
||||
|
||||
/* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
|
||||
for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
|
||||
/* Let's see if this color already exists: */
|
||||
for (j = 0; j < ColorIn1->ColorCount; j++)
|
||||
if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
|
||||
sizeof(GifColorType)) == 0)
|
||||
break;
|
||||
|
||||
if (j < ColorIn1->ColorCount)
|
||||
ColorTransIn2[i] = j; /* color exists in Color1 */
|
||||
else {
|
||||
/* Color is new - copy it to a new slot: */
|
||||
ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
|
||||
ColorTransIn2[i] = CrntSlot++;
|
||||
}
|
||||
}
|
||||
|
||||
if (CrntSlot > 256) {
|
||||
GifFreeMapObject(ColorUnion);
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
|
||||
NewGifBitSize = GifBitSize(CrntSlot);
|
||||
RoundUpTo = (1 << NewGifBitSize);
|
||||
|
||||
if (RoundUpTo != ColorUnion->ColorCount) {
|
||||
register GifColorType *Map = ColorUnion->Colors;
|
||||
|
||||
/*
|
||||
* Zero out slots up to next power of 2.
|
||||
* We know these slots exist because of the way ColorUnion's
|
||||
* start dimension was computed.
|
||||
*/
|
||||
for (j = CrntSlot; j < RoundUpTo; j++)
|
||||
Map[j].Red = Map[j].Green = Map[j].Blue = 0;
|
||||
|
||||
/* perhaps we can shrink the map? */
|
||||
if (RoundUpTo < ColorUnion->ColorCount) {
|
||||
GifColorType *new_map = (GifColorType *)reallocarray(Map,
|
||||
RoundUpTo, sizeof(GifColorType));
|
||||
if( new_map == NULL ) {
|
||||
GifFreeMapObject(ColorUnion);
|
||||
return ((ColorMapObject *) NULL);
|
||||
}
|
||||
ColorUnion->Colors = new_map;
|
||||
}
|
||||
}
|
||||
|
||||
ColorUnion->ColorCount = RoundUpTo;
|
||||
ColorUnion->BitsPerPixel = NewGifBitSize;
|
||||
|
||||
return (ColorUnion);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Apply a given color translation to the raster bits of an image
|
||||
*******************************************************************************/
|
||||
void
|
||||
GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
|
||||
{
|
||||
register int i;
|
||||
register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
|
||||
|
||||
for (i = 0; i < RasterSize; i++)
|
||||
Image->RasterBits[i] = Translation[Image->RasterBits[i]];
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Extension record functions
|
||||
******************************************************************************/
|
||||
int
|
||||
GifAddExtensionBlock(int *ExtensionBlockCount,
|
||||
ExtensionBlock **ExtensionBlocks,
|
||||
int Function,
|
||||
unsigned int Len,
|
||||
unsigned char ExtData[])
|
||||
{
|
||||
ExtensionBlock *ep;
|
||||
|
||||
if (*ExtensionBlocks == NULL)
|
||||
*ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
|
||||
else {
|
||||
ExtensionBlock* ep_new = (ExtensionBlock *)reallocarray
|
||||
(*ExtensionBlocks, (*ExtensionBlockCount + 1),
|
||||
sizeof(ExtensionBlock));
|
||||
if( ep_new == NULL )
|
||||
return (GIF_ERROR);
|
||||
*ExtensionBlocks = ep_new;
|
||||
}
|
||||
|
||||
if (*ExtensionBlocks == NULL)
|
||||
return (GIF_ERROR);
|
||||
|
||||
ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
|
||||
|
||||
ep->Function = Function;
|
||||
ep->ByteCount=Len;
|
||||
ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
|
||||
if (ep->Bytes == NULL)
|
||||
return (GIF_ERROR);
|
||||
|
||||
if (ExtData != NULL) {
|
||||
memcpy(ep->Bytes, ExtData, Len);
|
||||
}
|
||||
|
||||
return (GIF_OK);
|
||||
}
|
||||
|
||||
void
|
||||
GifFreeExtensions(int *ExtensionBlockCount,
|
||||
ExtensionBlock **ExtensionBlocks)
|
||||
{
|
||||
ExtensionBlock *ep;
|
||||
|
||||
if (*ExtensionBlocks == NULL)
|
||||
return;
|
||||
|
||||
for (ep = *ExtensionBlocks;
|
||||
ep < (*ExtensionBlocks + *ExtensionBlockCount);
|
||||
ep++)
|
||||
(void)free((char *)ep->Bytes);
|
||||
(void)free((char *)*ExtensionBlocks);
|
||||
*ExtensionBlocks = NULL;
|
||||
*ExtensionBlockCount = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Image block allocation functions
|
||||
******************************************************************************/
|
||||
|
||||
/* Private Function:
|
||||
* Frees the last image in the GifFile->SavedImages array
|
||||
*/
|
||||
void
|
||||
FreeLastSavedImage(GifFileType *GifFile)
|
||||
{
|
||||
SavedImage *sp;
|
||||
|
||||
if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
|
||||
return;
|
||||
|
||||
/* Remove one SavedImage from the GifFile */
|
||||
GifFile->ImageCount--;
|
||||
sp = &GifFile->SavedImages[GifFile->ImageCount];
|
||||
|
||||
/* Deallocate its Colormap */
|
||||
if (sp->ImageDesc.ColorMap != NULL) {
|
||||
GifFreeMapObject(sp->ImageDesc.ColorMap);
|
||||
sp->ImageDesc.ColorMap = NULL;
|
||||
}
|
||||
|
||||
/* Deallocate the image data */
|
||||
if (sp->RasterBits != NULL)
|
||||
free((char *)sp->RasterBits);
|
||||
|
||||
/* Deallocate any extensions */
|
||||
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
|
||||
|
||||
/*** FIXME: We could realloc the GifFile->SavedImages structure but is
|
||||
* there a point to it? Saves some memory but we'd have to do it every
|
||||
* time. If this is used in GifFreeSavedImages then it would be inefficient
|
||||
* (The whole array is going to be deallocated.) If we just use it when
|
||||
* we want to free the last Image it's convenient to do it here.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Append an image block to the SavedImages array
|
||||
*/
|
||||
SavedImage *
|
||||
GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
|
||||
{
|
||||
if (GifFile->SavedImages == NULL)
|
||||
GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
|
||||
else {
|
||||
SavedImage* newSavedImages = (SavedImage *)reallocarray(GifFile->SavedImages,
|
||||
(GifFile->ImageCount + 1), sizeof(SavedImage));
|
||||
if( newSavedImages == NULL)
|
||||
return ((SavedImage *)NULL);
|
||||
GifFile->SavedImages = newSavedImages;
|
||||
}
|
||||
if (GifFile->SavedImages == NULL)
|
||||
return ((SavedImage *)NULL);
|
||||
else {
|
||||
SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
|
||||
|
||||
if (CopyFrom != NULL) {
|
||||
memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
|
||||
|
||||
/*
|
||||
* Make our own allocated copies of the heap fields in the
|
||||
* copied record. This guards against potential aliasing
|
||||
* problems.
|
||||
*/
|
||||
|
||||
/* first, the local color map */
|
||||
if (CopyFrom->ImageDesc.ColorMap != NULL) {
|
||||
sp->ImageDesc.ColorMap = GifMakeMapObject(
|
||||
CopyFrom->ImageDesc.ColorMap->ColorCount,
|
||||
CopyFrom->ImageDesc.ColorMap->Colors);
|
||||
if (sp->ImageDesc.ColorMap == NULL) {
|
||||
FreeLastSavedImage(GifFile);
|
||||
return (SavedImage *)(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* next, the raster */
|
||||
sp->RasterBits = (unsigned char *)reallocarray(NULL,
|
||||
(CopyFrom->ImageDesc.Height *
|
||||
CopyFrom->ImageDesc.Width),
|
||||
sizeof(GifPixelType));
|
||||
if (sp->RasterBits == NULL) {
|
||||
FreeLastSavedImage(GifFile);
|
||||
return (SavedImage *)(NULL);
|
||||
}
|
||||
memcpy(sp->RasterBits, CopyFrom->RasterBits,
|
||||
sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
|
||||
CopyFrom->ImageDesc.Width);
|
||||
|
||||
/* finally, the extension blocks */
|
||||
if (CopyFrom->ExtensionBlocks != NULL) {
|
||||
sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL,
|
||||
CopyFrom->ExtensionBlockCount,
|
||||
sizeof(ExtensionBlock));
|
||||
if (sp->ExtensionBlocks == NULL) {
|
||||
FreeLastSavedImage(GifFile);
|
||||
return (SavedImage *)(NULL);
|
||||
}
|
||||
memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
|
||||
sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
|
||||
}
|
||||
}
|
||||
else {
|
||||
memset((char *)sp, '\0', sizeof(SavedImage));
|
||||
}
|
||||
|
||||
return (sp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GifFreeSavedImages(GifFileType *GifFile)
|
||||
{
|
||||
SavedImage *sp;
|
||||
|
||||
if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
|
||||
return;
|
||||
}
|
||||
for (sp = GifFile->SavedImages;
|
||||
sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
|
||||
if (sp->ImageDesc.ColorMap != NULL) {
|
||||
GifFreeMapObject(sp->ImageDesc.ColorMap);
|
||||
sp->ImageDesc.ColorMap = NULL;
|
||||
}
|
||||
|
||||
if (sp->RasterBits != NULL)
|
||||
free((char *)sp->RasterBits);
|
||||
|
||||
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
|
||||
}
|
||||
free((char *)GifFile->SavedImages);
|
||||
GifFile->SavedImages = NULL;
|
||||
}
|
||||
|
||||
/* end */
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX UINTPTR_MAX
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
|
||||
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
|
||||
*/
|
||||
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
|
||||
|
||||
void *
|
||||
openbsd_reallocarray(void *optr, size_t nmemb, size_t size)
|
||||
{
|
||||
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
|
||||
nmemb > 0 && SIZE_MAX / nmemb < size) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Head off variations in realloc behavior on different
|
||||
* platforms (reported by MarkR <mrogers6@users.sf.net>)
|
||||
*
|
||||
* The behaviour of reallocarray is implementation-defined if
|
||||
* nmemb or size is zero. It can return NULL or non-NULL
|
||||
* depending on the platform.
|
||||
* https://www.securecoding.cert.org/confluence/display/c/MEM04-C.Beware+of+zero-lengthallocations
|
||||
*
|
||||
* Here are some extracts from realloc man pages on different platforms.
|
||||
*
|
||||
* void realloc( void memblock, size_t size );
|
||||
*
|
||||
* Windows:
|
||||
*
|
||||
* If there is not enough available memory to expand the block
|
||||
* to the given size, the original block is left unchanged,
|
||||
* and NULL is returned. If size is zero, then the block
|
||||
* pointed to by memblock is freed; the return value is NULL,
|
||||
* and memblock is left pointing at a freed block.
|
||||
*
|
||||
* OpenBSD:
|
||||
*
|
||||
* If size or nmemb is equal to 0, a unique pointer to an
|
||||
* access protected, zero sized object is returned. Access via
|
||||
* this pointer will generate a SIGSEGV exception.
|
||||
*
|
||||
* Linux:
|
||||
*
|
||||
* If size was equal to 0, either NULL or a pointer suitable
|
||||
* to be passed to free() is returned.
|
||||
*
|
||||
* OS X:
|
||||
*
|
||||
* If size is zero and ptr is not NULL, a new, minimum sized
|
||||
* object is allocated and the original object is freed.
|
||||
*
|
||||
* It looks like images with zero width or height can trigger
|
||||
* this, and fuzzing behaviour will differ by platform, so
|
||||
* fuzzing on one platform may not detect zero-size allocation
|
||||
* problems on other platforms.
|
||||
*/
|
||||
if (size == 0 || nmemb == 0)
|
||||
return NULL;
|
||||
return realloc(optr, size * nmemb);
|
||||
}
|
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
#define captype(cap) ((cap)->kind)
|
||||
|
||||
#define isclosecap(cap) (captype(cap) == Cclose)
|
||||
|
||||
#define closeaddr(c) ((c)->s + (c)->siz - 1)
|
||||
|
||||
#define isfullcap(cap) ((cap)->siz != 0)
|
||||
|
||||
#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
|
||||
|
||||
#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Put at the cache for Lua values the value indexed by 'v' in ktable
|
||||
** of the running pattern (if it is not there yet); returns its index.
|
||||
*/
|
||||
static int updatecache (CapState *cs, int v) {
|
||||
int idx = cs->ptop + 1; /* stack index of cache for Lua values */
|
||||
if (v != cs->valuecached) { /* not there? */
|
||||
getfromktable(cs, v); /* get value from 'ktable' */
|
||||
lua_replace(cs->L, idx); /* put it at reserved stack position */
|
||||
cs->valuecached = v; /* keep track of what is there */
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
static int pushcapture (CapState *cs);
|
||||
|
||||
|
||||
/*
|
||||
** Goes back in a list of captures looking for an open capture
|
||||
** corresponding to a close
|
||||
*/
|
||||
static Capture *findopen (Capture *cap) {
|
||||
int n = 0; /* number of closes waiting an open */
|
||||
for (;;) {
|
||||
cap--;
|
||||
if (isclosecap(cap)) n++; /* one more open to skip */
|
||||
else if (!isfullcap(cap))
|
||||
if (n-- == 0) return cap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Go to the next capture
|
||||
*/
|
||||
static void nextcap (CapState *cs) {
|
||||
Capture *cap = cs->cap;
|
||||
if (!isfullcap(cap)) { /* not a single capture? */
|
||||
int n = 0; /* number of opens waiting a close */
|
||||
for (;;) { /* look for corresponding close */
|
||||
cap++;
|
||||
if (isclosecap(cap)) {
|
||||
if (n-- == 0) break;
|
||||
}
|
||||
else if (!isfullcap(cap)) n++;
|
||||
}
|
||||
}
|
||||
cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push on the Lua stack all values generated by nested captures inside
|
||||
** the current capture. Returns number of values pushed. 'addextra'
|
||||
** makes it push the entire match after all captured values. The
|
||||
** entire match is pushed also if there are no other nested values,
|
||||
** so the function never returns zero.
|
||||
*/
|
||||
static int pushnestedvalues (CapState *cs, int addextra) {
|
||||
Capture *co = cs->cap;
|
||||
if (isfullcap(cs->cap++)) { /* no nested captures? */
|
||||
lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */
|
||||
return 1; /* that is it */
|
||||
}
|
||||
else {
|
||||
int n = 0;
|
||||
while (!isclosecap(cs->cap)) /* repeat for all nested patterns */
|
||||
n += pushcapture(cs);
|
||||
if (addextra || n == 0) { /* need extra? */
|
||||
lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */
|
||||
n++;
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push only the first value generated by nested captures
|
||||
*/
|
||||
static void pushonenestedvalue (CapState *cs) {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n > 1)
|
||||
lua_pop(cs->L, n - 1); /* pop extra values */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a named group capture with the name given at the top of
|
||||
** the stack; goes backward from 'cap'.
|
||||
*/
|
||||
static Capture *findback (CapState *cs, Capture *cap) {
|
||||
lua_State *L = cs->L;
|
||||
while (cap-- > cs->ocap) { /* repeat until end of list */
|
||||
if (isclosecap(cap))
|
||||
cap = findopen(cap); /* skip nested captures */
|
||||
else if (!isfullcap(cap))
|
||||
continue; /* opening an enclosing capture: skip and get previous */
|
||||
if (captype(cap) == Cgroup) {
|
||||
getfromktable(cs, cap->idx); /* get group name */
|
||||
if (lp_equal(L, -2, -1)) { /* right group? */
|
||||
lua_pop(L, 2); /* remove reference name and group name */
|
||||
return cap;
|
||||
}
|
||||
else lua_pop(L, 1); /* remove group name */
|
||||
}
|
||||
}
|
||||
luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
|
||||
return NULL; /* to avoid warnings */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Back-reference capture. Return number of values pushed.
|
||||
*/
|
||||
static int backrefcap (CapState *cs) {
|
||||
int n;
|
||||
Capture *curr = cs->cap;
|
||||
pushluaval(cs); /* reference name */
|
||||
cs->cap = findback(cs, curr); /* find corresponding group */
|
||||
n = pushnestedvalues(cs, 0); /* push group's values */
|
||||
cs->cap = curr + 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table capture: creates a new table and populates it with nested
|
||||
** captures.
|
||||
*/
|
||||
static int tablecap (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
int n = 0;
|
||||
lua_newtable(L);
|
||||
if (isfullcap(cs->cap++))
|
||||
return 1; /* table is empty */
|
||||
while (!isclosecap(cs->cap)) {
|
||||
if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
|
||||
pushluaval(cs); /* push group name */
|
||||
pushonenestedvalue(cs);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
else { /* not a named group */
|
||||
int i;
|
||||
int k = pushcapture(cs);
|
||||
for (i = k; i > 0; i--) /* store all values into table */
|
||||
lua_rawseti(L, -(i + 1), n + i);
|
||||
n += k;
|
||||
}
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return 1; /* number of values pushed (only the table) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table-query capture
|
||||
*/
|
||||
static int querycap (CapState *cs) {
|
||||
int idx = cs->cap->idx;
|
||||
pushonenestedvalue(cs); /* get nested capture */
|
||||
lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
|
||||
if (!lua_isnil(cs->L, -1))
|
||||
return 1;
|
||||
else { /* no value */
|
||||
lua_pop(cs->L, 1); /* remove nil */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Fold capture
|
||||
*/
|
||||
static int foldcap (CapState *cs) {
|
||||
int n;
|
||||
lua_State *L = cs->L;
|
||||
int idx = cs->cap->idx;
|
||||
if (isfullcap(cs->cap++) || /* no nested captures? */
|
||||
isclosecap(cs->cap) || /* no nested captures (large subject)? */
|
||||
(n = pushcapture(cs)) == 0) /* nested captures with no values? */
|
||||
return luaL_error(L, "no initial value for fold capture");
|
||||
if (n > 1)
|
||||
lua_pop(L, n - 1); /* leave only one result for accumulator */
|
||||
while (!isclosecap(cs->cap)) {
|
||||
lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
|
||||
lua_insert(L, -2); /* put it before accumulator */
|
||||
n = pushcapture(cs); /* get next capture's values */
|
||||
lua_call(L, n + 1, 1); /* call folding function */
|
||||
}
|
||||
cs->cap++; /* skip close entry */
|
||||
return 1; /* only accumulator left on the stack */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function capture
|
||||
*/
|
||||
static int functioncap (CapState *cs) {
|
||||
int n;
|
||||
int top = lua_gettop(cs->L);
|
||||
pushluaval(cs); /* push function */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(cs->L, n, LUA_MULTRET); /* call function */
|
||||
return lua_gettop(cs->L) - top; /* return function's results */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Select capture
|
||||
*/
|
||||
static int numcap (CapState *cs) {
|
||||
int idx = cs->cap->idx; /* value to select */
|
||||
if (idx == 0) { /* no values? */
|
||||
nextcap(cs); /* skip entire capture */
|
||||
return 0; /* no value produced */
|
||||
}
|
||||
else {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n < idx) /* invalid index? */
|
||||
return luaL_error(cs->L, "no capture '%d'", idx);
|
||||
else {
|
||||
lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
|
||||
lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
|
||||
lua_pop(cs->L, n - 1); /* remove other captures */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the stack index of the first runtime capture in the given
|
||||
** list of captures (or zero if no runtime captures)
|
||||
*/
|
||||
int finddyncap (Capture *cap, Capture *last) {
|
||||
for (; cap < last; cap++) {
|
||||
if (cap->kind == Cruntime)
|
||||
return cap->idx; /* stack position of first capture */
|
||||
}
|
||||
return 0; /* no dynamic captures in this segment */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls a runtime capture. Returns number of captures removed by
|
||||
** the call, including the initial Cgroup. (Captures to be added are
|
||||
** on the Lua stack.)
|
||||
*/
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
|
||||
int n, id;
|
||||
lua_State *L = cs->L;
|
||||
int otop = lua_gettop(L);
|
||||
Capture *open = findopen(close);
|
||||
assert(captype(open) == Cgroup);
|
||||
id = finddyncap(open, close); /* get first dynamic capture argument */
|
||||
close->kind = Cclose; /* closes the group */
|
||||
close->s = s;
|
||||
cs->cap = open; cs->valuecached = 0; /* prepare capture state */
|
||||
luaL_checkstack(L, 4, "too many runtime captures");
|
||||
pushluaval(cs); /* push function to be called */
|
||||
lua_pushvalue(L, SUBJIDX); /* push original subject */
|
||||
lua_pushinteger(L, s - cs->s + 1); /* push current position */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
|
||||
if (id > 0) { /* are there old dynamic captures to be removed? */
|
||||
int i;
|
||||
for (i = id; i <= otop; i++)
|
||||
lua_remove(L, id); /* remove old dynamic captures */
|
||||
*rem = otop - id + 1; /* total number of dynamic captures removed */
|
||||
}
|
||||
else
|
||||
*rem = 0; /* no dynamic captures removed */
|
||||
return close - open; /* number of captures of all kinds removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary structure for substitution and string captures: keep
|
||||
** information about nested captures for future use, avoiding to push
|
||||
** string results into Lua
|
||||
*/
|
||||
typedef struct StrAux {
|
||||
int isstring; /* whether capture is a string */
|
||||
union {
|
||||
Capture *cp; /* if not a string, respective capture */
|
||||
struct { /* if it is a string... */
|
||||
const char *s; /* ... starts here */
|
||||
const char *e; /* ... ends here */
|
||||
} s;
|
||||
} u;
|
||||
} StrAux;
|
||||
|
||||
#define MAXSTRCAPS 10
|
||||
|
||||
/*
|
||||
** Collect values from current capture into array 'cps'. Current
|
||||
** capture must be Cstring (first call) or Csimple (recursive calls).
|
||||
** (In first call, fills %0 with whole match for Cstring.)
|
||||
** Returns number of elements in the array that were filled.
|
||||
*/
|
||||
static int getstrcaps (CapState *cs, StrAux *cps, int n) {
|
||||
int k = n++;
|
||||
cps[k].isstring = 1; /* get string value */
|
||||
cps[k].u.s.s = cs->cap->s; /* starts here */
|
||||
if (!isfullcap(cs->cap++)) { /* nested captures? */
|
||||
while (!isclosecap(cs->cap)) { /* traverse them */
|
||||
if (n >= MAXSTRCAPS) /* too many captures? */
|
||||
nextcap(cs); /* skip extra captures (will not need them) */
|
||||
else if (captype(cs->cap) == Csimple) /* string? */
|
||||
n = getstrcaps(cs, cps, n); /* put info. into array */
|
||||
else {
|
||||
cps[n].isstring = 0; /* not a string */
|
||||
cps[n].u.cp = cs->cap; /* keep original capture */
|
||||
nextcap(cs);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
cs->cap++; /* skip close */
|
||||
}
|
||||
cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** add next capture value (which should be a string) to buffer 'b'
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
|
||||
|
||||
|
||||
/*
|
||||
** String capture: add result to buffer 'b' (instead of pushing
|
||||
** it into the stack)
|
||||
*/
|
||||
static void stringcap (luaL_Buffer *b, CapState *cs) {
|
||||
StrAux cps[MAXSTRCAPS];
|
||||
int n;
|
||||
size_t len, i;
|
||||
const char *fmt; /* format string */
|
||||
fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
|
||||
n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
|
||||
for (i = 0; i < len; i++) { /* traverse them */
|
||||
if (fmt[i] != '%') /* not an escape? */
|
||||
luaL_addchar(b, fmt[i]); /* add it to buffer */
|
||||
else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
|
||||
luaL_addchar(b, fmt[i]); /* add to buffer */
|
||||
else {
|
||||
int l = fmt[i] - '0'; /* capture index */
|
||||
if (l > n)
|
||||
luaL_error(cs->L, "invalid capture index (%d)", l);
|
||||
else if (cps[l].isstring)
|
||||
luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
|
||||
else {
|
||||
Capture *curr = cs->cap;
|
||||
cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
|
||||
if (!addonestring(b, cs, "capture"))
|
||||
luaL_error(cs->L, "no values in capture index %d", l);
|
||||
cs->cap = curr; /* continue from where it stopped */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Substitution capture: add result to buffer 'b'
|
||||
*/
|
||||
static void substcap (luaL_Buffer *b, CapState *cs) {
|
||||
const char *curr = cs->cap->s;
|
||||
if (isfullcap(cs->cap)) /* no nested captures? */
|
||||
luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */
|
||||
else {
|
||||
cs->cap++; /* skip open entry */
|
||||
while (!isclosecap(cs->cap)) { /* traverse nested captures */
|
||||
const char *next = cs->cap->s;
|
||||
luaL_addlstring(b, curr, next - curr); /* add text up to capture */
|
||||
if (addonestring(b, cs, "replacement"))
|
||||
curr = closeaddr(cs->cap - 1); /* continue after match */
|
||||
else /* no capture value */
|
||||
curr = next; /* keep original text in final result */
|
||||
}
|
||||
luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */
|
||||
}
|
||||
cs->cap++; /* go to next capture */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Evaluates a capture and adds its first value to buffer 'b'; returns
|
||||
** whether there was a value
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
|
||||
switch (captype(cs->cap)) {
|
||||
case Cstring:
|
||||
stringcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
case Csubst:
|
||||
substcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
default: {
|
||||
lua_State *L = cs->L;
|
||||
int n = pushcapture(cs);
|
||||
if (n > 0) {
|
||||
if (n > 1) lua_pop(L, n - 1); /* only one result */
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push all values of the current capture into the stack; returns
|
||||
** number of values pushed
|
||||
*/
|
||||
static int pushcapture (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
luaL_checkstack(L, 4, "too many captures");
|
||||
switch (captype(cs->cap)) {
|
||||
case Cposition: {
|
||||
lua_pushinteger(L, cs->cap->s - cs->s + 1);
|
||||
cs->cap++;
|
||||
return 1;
|
||||
}
|
||||
case Cconst: {
|
||||
pushluaval(cs);
|
||||
cs->cap++;
|
||||
return 1;
|
||||
}
|
||||
case Carg: {
|
||||
int arg = (cs->cap++)->idx;
|
||||
if (arg + FIXEDARGS > cs->ptop)
|
||||
return luaL_error(L, "reference to absent extra argument #%d", arg);
|
||||
lua_pushvalue(L, arg + FIXEDARGS);
|
||||
return 1;
|
||||
}
|
||||
case Csimple: {
|
||||
int k = pushnestedvalues(cs, 1);
|
||||
lua_insert(L, -k); /* make whole match be first result */
|
||||
return k;
|
||||
}
|
||||
case Cruntime: {
|
||||
lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
|
||||
return 1;
|
||||
}
|
||||
case Cstring: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
stringcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
case Csubst: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
substcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
case Cgroup: {
|
||||
if (cs->cap->idx == 0) /* anonymous group? */
|
||||
return pushnestedvalues(cs, 0); /* add all nested values */
|
||||
else { /* named group: add no values */
|
||||
nextcap(cs); /* skip capture */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case Cbackref: return backrefcap(cs);
|
||||
case Ctable: return tablecap(cs);
|
||||
case Cfunction: return functioncap(cs);
|
||||
case Cnum: return numcap(cs);
|
||||
case Cquery: return querycap(cs);
|
||||
case Cfold: return foldcap(cs);
|
||||
default: assert(0); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a CapState structure and traverse the entire list of
|
||||
** captures in the stack pushing its results. 's' is the subject
|
||||
** string, 'r' is the final position of the match, and 'ptop'
|
||||
** the index in the stack where some useful values were pushed.
|
||||
** Returns the number of results pushed. (If the list produces no
|
||||
** results, push the final position of the match.)
|
||||
*/
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
|
||||
Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
|
||||
int n = 0;
|
||||
if (!isclosecap(capture)) { /* is there any capture? */
|
||||
CapState cs;
|
||||
cs.ocap = cs.cap = capture; cs.L = L;
|
||||
cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
|
||||
do { /* collect their values */
|
||||
n += pushcapture(&cs);
|
||||
} while (!isclosecap(cs.cap));
|
||||
}
|
||||
if (n == 0) { /* no capture values? */
|
||||
lua_pushinteger(L, r - s + 1); /* return only end position */
|
||||
n = 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
** $Id: lpcap.h,v 1.3 2016/09/13 17:45:58 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpcap_h)
|
||||
#define lpcap_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/* kinds of captures */
|
||||
typedef enum CapKind {
|
||||
Cclose, /* not used in trees */
|
||||
Cposition,
|
||||
Cconst, /* ktable[key] is Lua constant */
|
||||
Cbackref, /* ktable[key] is "name" of group to get capture */
|
||||
Carg, /* 'key' is arg's number */
|
||||
Csimple, /* next node is pattern */
|
||||
Ctable, /* next node is pattern */
|
||||
Cfunction, /* ktable[key] is function; next node is pattern */
|
||||
Cquery, /* ktable[key] is table; next node is pattern */
|
||||
Cstring, /* ktable[key] is string; next node is pattern */
|
||||
Cnum, /* numbered capture; 'key' is number of value to return */
|
||||
Csubst, /* substitution capture; next node is pattern */
|
||||
Cfold, /* ktable[key] is function; next node is pattern */
|
||||
Cruntime, /* not used in trees (is uses another type for tree) */
|
||||
Cgroup /* ktable[key] is group's "name" */
|
||||
} CapKind;
|
||||
|
||||
|
||||
typedef struct Capture {
|
||||
const char *s; /* subject position */
|
||||
unsigned short idx; /* extra info (group name, arg index, etc.) */
|
||||
byte kind; /* kind of capture */
|
||||
byte siz; /* size of full capture + 1 (0 = not a full capture) */
|
||||
} Capture;
|
||||
|
||||
|
||||
typedef struct CapState {
|
||||
Capture *cap; /* current capture */
|
||||
Capture *ocap; /* (original) capture list */
|
||||
lua_State *L;
|
||||
int ptop; /* index of last argument to 'match' */
|
||||
const char *s; /* original string */
|
||||
int valuecached; /* value stored in cache slot */
|
||||
} CapState;
|
||||
|
||||
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
|
||||
int finddyncap (Capture *cap, Capture *last);
|
||||
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
** $Id: lpcode.h,v 1.8 2016/09/15 17:46:13 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpcode_h)
|
||||
#define lpcode_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
int tocharset (TTree *tree, Charset *cs);
|
||||
int checkaux (TTree *tree, int pred);
|
||||
int fixedlen (TTree *tree);
|
||||
int hascaptures (TTree *tree);
|
||||
int lp_gc (lua_State *L);
|
||||
Instruction *compile (lua_State *L, Pattern *p);
|
||||
void realloccode (lua_State *L, Pattern *p, int nsize);
|
||||
int sizei (const Instruction *i);
|
||||
|
||||
|
||||
#define PEnullable 0
|
||||
#define PEnofail 1
|
||||
|
||||
/*
|
||||
** nofail(t) implies that 't' cannot fail with any input
|
||||
*/
|
||||
#define nofail(t) checkaux(t, PEnofail)
|
||||
|
||||
/*
|
||||
** (not nullable(t)) implies 't' cannot match without consuming
|
||||
** something
|
||||
*/
|
||||
#define nullable(t) checkaux(t, PEnullable)
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
** $Id: lpprint.c,v 1.10 2016/09/13 16:06:03 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lpprint.h"
|
||||
#include "lpcode.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing patterns (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
void printcharset (const byte *st) {
|
||||
int i;
|
||||
printf("[");
|
||||
for (i = 0; i <= UCHAR_MAX; i++) {
|
||||
int first = i;
|
||||
while (testchar(st, i) && i <= UCHAR_MAX) i++;
|
||||
if (i - 1 == first) /* unary range? */
|
||||
printf("(%02x)", first);
|
||||
else if (i - 1 > first) /* non-empty range? */
|
||||
printf("(%02x-%02x)", first, i - 1);
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
|
||||
|
||||
static const char *capkind (int kind) {
|
||||
const char *const modes[] = {
|
||||
"close", "position", "constant", "backref",
|
||||
"argument", "simple", "table", "function",
|
||||
"query", "string", "num", "substitution", "fold",
|
||||
"runtime", "group"};
|
||||
return modes[kind];
|
||||
}
|
||||
|
||||
|
||||
static void printjmp (const Instruction *op, const Instruction *p) {
|
||||
printf("-> %d", (int)(p + (p + 1)->offset - op));
|
||||
}
|
||||
|
||||
|
||||
void printinst (const Instruction *op, const Instruction *p) {
|
||||
const char *const names[] = {
|
||||
"any", "char", "set",
|
||||
"testany", "testchar", "testset",
|
||||
"span", "behind",
|
||||
"ret", "end",
|
||||
"choice", "jmp", "call", "open_call",
|
||||
"commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
|
||||
"fullcapture", "opencapture", "closecapture", "closeruntime"
|
||||
};
|
||||
printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IChar: {
|
||||
printf("'%c'", p->i.aux);
|
||||
break;
|
||||
}
|
||||
case ITestChar: {
|
||||
printf("'%c'", p->i.aux); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case IFullCapture: {
|
||||
printf("%s (size = %d) (idx = %d)",
|
||||
capkind(getkind(p)), getoff(p), p->i.key);
|
||||
break;
|
||||
}
|
||||
case IOpenCapture: {
|
||||
printf("%s (idx = %d)", capkind(getkind(p)), p->i.key);
|
||||
break;
|
||||
}
|
||||
case ISet: {
|
||||
printcharset((p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case ITestSet: {
|
||||
printcharset((p+2)->buff); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case ISpan: {
|
||||
printcharset((p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case IOpenCall: {
|
||||
printf("-> %d", (p + 1)->offset);
|
||||
break;
|
||||
}
|
||||
case IBehind: {
|
||||
printf("%d", p->i.aux);
|
||||
break;
|
||||
}
|
||||
case IJmp: case ICall: case ICommit: case IChoice:
|
||||
case IPartialCommit: case IBackCommit: case ITestAny: {
|
||||
printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
void printpatt (Instruction *p, int n) {
|
||||
Instruction *op = p;
|
||||
while (p < op + n) {
|
||||
printinst(op, p);
|
||||
p += sizei(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
static void printcap (Capture *cap) {
|
||||
printf("%s (idx: %d - size: %d) -> %p\n",
|
||||
capkind(cap->kind), cap->idx, cap->siz, cap->s);
|
||||
}
|
||||
|
||||
|
||||
void printcaplist (Capture *cap, Capture *limit) {
|
||||
printf(">======\n");
|
||||
for (; cap->s && (limit == NULL || cap < limit); cap++)
|
||||
printcap(cap);
|
||||
printf("=======\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing trees (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static const char *tagnames[] = {
|
||||
"char", "set", "any",
|
||||
"true", "false",
|
||||
"rep",
|
||||
"seq", "choice",
|
||||
"not", "and",
|
||||
"call", "opencall", "rule", "grammar",
|
||||
"behind",
|
||||
"capture", "run-time"
|
||||
};
|
||||
|
||||
|
||||
void printtree (TTree *tree, int ident) {
|
||||
int i;
|
||||
for (i = 0; i < ident; i++) printf(" ");
|
||||
printf("%s", tagnames[tree->tag]);
|
||||
switch (tree->tag) {
|
||||
case TChar: {
|
||||
int c = tree->u.n;
|
||||
if (isprint(c))
|
||||
printf(" '%c'\n", c);
|
||||
else
|
||||
printf(" (%02X)\n", c);
|
||||
break;
|
||||
}
|
||||
case TSet: {
|
||||
printcharset(treebuffer(tree));
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
case TOpenCall: case TCall: {
|
||||
assert(sib2(tree)->tag == TRule);
|
||||
printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap);
|
||||
break;
|
||||
}
|
||||
case TBehind: {
|
||||
printf(" %d\n", tree->u.n);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break;
|
||||
}
|
||||
case TCapture: {
|
||||
printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break;
|
||||
}
|
||||
case TRule: {
|
||||
printf(" n: %d key: %d\n", tree->cap, tree->key);
|
||||
printtree(sib1(tree), ident + 2);
|
||||
break; /* do not print next rule as a sibling */
|
||||
}
|
||||
case TGrammar: {
|
||||
TTree *rule = sib1(tree);
|
||||
printf(" %d\n", tree->u.n); /* number of rules */
|
||||
for (i = 0; i < tree->u.n; i++) {
|
||||
printtree(rule, ident + 2);
|
||||
rule = sib2(rule);
|
||||
}
|
||||
assert(rule->tag == TTrue); /* sentinel */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
int sibs = numsiblings[tree->tag];
|
||||
printf("\n");
|
||||
if (sibs >= 1) {
|
||||
printtree(sib1(tree), ident + 2);
|
||||
if (sibs >= 2)
|
||||
printtree(sib2(tree), ident + 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printktable (lua_State *L, int idx) {
|
||||
int n, i;
|
||||
lua_getuservalue(L, idx);
|
||||
if (lua_isnil(L, -1)) /* no ktable? */
|
||||
return;
|
||||
n = lua_rawlen(L, -1);
|
||||
printf("[");
|
||||
for (i = 1; i <= n; i++) {
|
||||
printf("%d = ", i);
|
||||
lua_rawgeti(L, -1, i);
|
||||
if (lua_isstring(L, -1))
|
||||
printf("%s ", lua_tostring(L, -1));
|
||||
else
|
||||
printf("%s ", lua_typename(L, lua_type(L, -1)));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
printf("]\n");
|
||||
/* leave ktable at the stack */
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
** $Id: lpprint.h,v 1.2 2015/06/12 18:18:08 roberto Exp $
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(lpprint_h)
|
||||
#define lpprint_h
|
||||
|
||||
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
void printpatt (Instruction *p, int n);
|
||||
void printtree (TTree *tree, int ident);
|
||||
void printktable (lua_State *L, int idx);
|
||||
void printcharset (const byte *st);
|
||||
void printcaplist (Capture *cap, Capture *limit);
|
||||
void printinst (const Instruction *op, const Instruction *p);
|
||||
|
||||
#else
|
||||
|
||||
#define printktable(L,idx) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printtree(tree,i) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printpatt(p,n) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
** $Id: lptree.h,v 1.3 2016/09/13 18:07:51 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lptree_h)
|
||||
#define lptree_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/*
|
||||
** types of trees
|
||||
*/
|
||||
typedef enum TTag {
|
||||
TChar = 0, /* 'n' = char */
|
||||
TSet, /* the set is stored in next CHARSETSIZE bytes */
|
||||
TAny,
|
||||
TTrue,
|
||||
TFalse,
|
||||
TRep, /* 'sib1'* */
|
||||
TSeq, /* 'sib1' 'sib2' */
|
||||
TChoice, /* 'sib1' / 'sib2' */
|
||||
TNot, /* !'sib1' */
|
||||
TAnd, /* &'sib1' */
|
||||
TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */
|
||||
TOpenCall, /* ktable[key] is rule's key */
|
||||
TRule, /* ktable[key] is rule's key (but key == 0 for unused rules);
|
||||
'sib1' is rule's pattern;
|
||||
'sib2' is next rule; 'cap' is rule's sequential number */
|
||||
TGrammar, /* 'sib1' is initial (and first) rule */
|
||||
TBehind, /* 'sib1' is pattern, 'n' is how much to go back */
|
||||
TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind');
|
||||
ktable[key] is Lua value associated with capture;
|
||||
'sib1' is capture body */
|
||||
TRunTime /* run-time capture: 'key' is Lua function;
|
||||
'sib1' is capture body */
|
||||
} TTag;
|
||||
|
||||
|
||||
/*
|
||||
** Tree trees
|
||||
** The first child of a tree (if there is one) is immediately after
|
||||
** the tree. A reference to a second child (ps) is its position
|
||||
** relative to the position of the tree itself.
|
||||
*/
|
||||
typedef struct TTree {
|
||||
byte tag;
|
||||
byte cap; /* kind of capture (if it is a capture) */
|
||||
unsigned short key; /* key in ktable for Lua data (0 if no key) */
|
||||
union {
|
||||
int ps; /* occasional second child */
|
||||
int n; /* occasional counter */
|
||||
} u;
|
||||
} TTree;
|
||||
|
||||
|
||||
/*
|
||||
** A complete pattern has its tree plus, if already compiled,
|
||||
** its corresponding code
|
||||
*/
|
||||
typedef struct Pattern {
|
||||
union Instruction *code;
|
||||
int codesize;
|
||||
TTree tree[1];
|
||||
} Pattern;
|
||||
|
||||
|
||||
/* number of children for each tree */
|
||||
extern const byte numsiblings[];
|
||||
|
||||
/* access to children */
|
||||
#define sib1(t) ((t) + 1)
|
||||
#define sib2(t) ((t) + (t)->u.ps)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
** $Id: lptypes.h,v 1.16 2017/01/13 13:33:17 roberto Exp $
|
||||
** LPeg - PEG pattern matching for Lua
|
||||
** Copyright 2007-2017, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
** written by Roberto Ierusalimschy
|
||||
*/
|
||||
|
||||
#if !defined(lptypes_h)
|
||||
#define lptypes_h
|
||||
|
||||
|
||||
#if !defined(LPEG_DEBUG)
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#define VERSION "1.0.1"
|
||||
|
||||
|
||||
#define PATTERN_T "lpeg-pattern"
|
||||
#define MAXSTACKIDX "lpeg-maxstack"
|
||||
|
||||
|
||||
/*
|
||||
** compatibility with Lua 5.1
|
||||
*/
|
||||
#if (LUA_VERSION_NUM == 501)
|
||||
|
||||
#define lp_equal lua_equal
|
||||
|
||||
#define lua_getuservalue lua_getfenv
|
||||
#define lua_setuservalue lua_setfenv
|
||||
|
||||
#define lua_rawlen lua_objlen
|
||||
|
||||
#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)
|
||||
#define luaL_newlib(L,f) luaL_register(L,"lpeg",f)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(lp_equal)
|
||||
#define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
|
||||
#endif
|
||||
|
||||
|
||||
/* default maximum size for call/backtrack stack */
|
||||
#if !defined(MAXBACK)
|
||||
#define MAXBACK 400
|
||||
#endif
|
||||
|
||||
|
||||
/* maximum number of rules in a grammar (limited by 'unsigned char') */
|
||||
#if !defined(MAXRULES)
|
||||
#define MAXRULES 250
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* initial size for capture's list */
|
||||
#define INITCAPSIZE 32
|
||||
|
||||
|
||||
/* index, on Lua stack, for subject */
|
||||
#define SUBJIDX 2
|
||||
|
||||
/* number of fixed arguments to 'match' (before capture arguments) */
|
||||
#define FIXEDARGS 3
|
||||
|
||||
/* index, on Lua stack, for capture list */
|
||||
#define caplistidx(ptop) ((ptop) + 2)
|
||||
|
||||
/* index, on Lua stack, for pattern's ktable */
|
||||
#define ktableidx(ptop) ((ptop) + 3)
|
||||
|
||||
/* index, on Lua stack, for backtracking stack */
|
||||
#define stackidx(ptop) ((ptop) + 4)
|
||||
|
||||
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
|
||||
#define BITSPERCHAR 8
|
||||
|
||||
#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1)
|
||||
|
||||
|
||||
|
||||
typedef struct Charset {
|
||||
byte cs[CHARSETSIZE];
|
||||
} Charset;
|
||||
|
||||
|
||||
|
||||
#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }
|
||||
|
||||
/* access to charset */
|
||||
#define treebuffer(t) ((byte *)((t) + 1))
|
||||
|
||||
/* number of slots needed for 'n' bytes */
|
||||
#define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1)
|
||||
|
||||
/* set 'b' bit in charset 'cs' */
|
||||
#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))
|
||||
|
||||
|
||||
/*
|
||||
** in capture instructions, 'kind' of capture and its offset are
|
||||
** packed in field 'aux', 4 bits for each
|
||||
*/
|
||||
#define getkind(op) ((op)->i.aux & 0xF)
|
||||
#define getoff(op) (((op)->i.aux >> 4) & 0xF)
|
||||
#define joinkindoff(k,o) ((k) | ((o) << 4))
|
||||
|
||||
#define MAXOFF 0xF
|
||||
#define MAXAUX 0xFF
|
||||
|
||||
|
||||
/* maximum number of bytes to look behind */
|
||||
#define MAXBEHIND MAXAUX
|
||||
|
||||
|
||||
/* maximum size (in elements) for a pattern */
|
||||
#define MAXPATTSIZE (SHRT_MAX - 10)
|
||||
|
||||
|
||||
/* size (in elements) for an instruction plus extra l bytes */
|
||||
#define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1)
|
||||
|
||||
|
||||
/* size (in elements) for a ISet instruction */
|
||||
#define CHARSETINSTSIZE instsize(CHARSETSIZE)
|
||||
|
||||
/* size (in elements) for a IFunc instruction */
|
||||
#define funcinstsize(p) ((p)->i.aux + 2)
|
||||
|
||||
|
||||
|
||||
#define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7))))
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
** $Id: lpvm.c,v 1.9 2016/06/03 20:11:18 roberto Exp $
|
||||
** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
#include "lpvm.h"
|
||||
#include "lpprint.h"
|
||||
|
||||
|
||||
/* initial size for call/backtrack stack */
|
||||
#if !defined(INITBACK)
|
||||
#define INITBACK MAXBACK
|
||||
#endif
|
||||
|
||||
|
||||
#define getoffset(p) (((p) + 1)->offset)
|
||||
|
||||
static const Instruction giveup = {{IGiveup, 0, 0}};
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Virtual Machine
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct Stack {
|
||||
const char *s; /* saved position (or NULL for calls) */
|
||||
const Instruction *p; /* next instruction */
|
||||
int caplevel;
|
||||
} Stack;
|
||||
|
||||
|
||||
#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
|
||||
|
||||
|
||||
/*
|
||||
** Make the size of the array of captures 'cap' twice as large as needed
|
||||
** (which is 'captop'). ('n' is the number of new elements.)
|
||||
*/
|
||||
static Capture *doublecap (lua_State *L, Capture *cap, int captop,
|
||||
int n, int ptop) {
|
||||
Capture *newc;
|
||||
if (captop >= INT_MAX/((int)sizeof(Capture) * 2))
|
||||
luaL_error(L, "too many captures");
|
||||
newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture));
|
||||
memcpy(newc, cap, (captop - n) * sizeof(Capture));
|
||||
lua_replace(L, caplistidx(ptop));
|
||||
return newc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Double the size of the stack
|
||||
*/
|
||||
static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
|
||||
Stack *stack = getstackbase(L, ptop);
|
||||
Stack *newstack;
|
||||
int n = *stacklimit - stack; /* current stack size */
|
||||
int max, newn;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
|
||||
max = lua_tointeger(L, -1); /* maximum allowed size */
|
||||
lua_pop(L, 1);
|
||||
if (n >= max) /* already at maximum size? */
|
||||
luaL_error(L, "backtrack stack overflow (current limit is %d)", max);
|
||||
newn = 2 * n; /* new size */
|
||||
if (newn > max) newn = max;
|
||||
newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
|
||||
memcpy(newstack, stack, n * sizeof(Stack));
|
||||
lua_replace(L, stackidx(ptop));
|
||||
*stacklimit = newstack + newn;
|
||||
return newstack + n; /* return next position */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interpret the result of a dynamic capture: false -> fail;
|
||||
** true -> keep current position; number -> next position.
|
||||
** Return new subject position. 'fr' is stack index where
|
||||
** is the result; 'curr' is current subject position; 'limit'
|
||||
** is subject's size.
|
||||
*/
|
||||
static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
|
||||
lua_Integer res;
|
||||
if (!lua_toboolean(L, fr)) { /* false value? */
|
||||
lua_settop(L, fr - 1); /* remove results */
|
||||
return -1; /* and fail */
|
||||
}
|
||||
else if (lua_isboolean(L, fr)) /* true? */
|
||||
res = curr; /* keep current position */
|
||||
else {
|
||||
res = lua_tointeger(L, fr) - 1; /* new position */
|
||||
if (res < curr || res > limit)
|
||||
luaL_error(L, "invalid position returned by match-time capture");
|
||||
}
|
||||
lua_remove(L, fr); /* remove first result (offset) */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add capture values returned by a dynamic capture to the capture list
|
||||
** 'base', nested inside a group capture. 'fd' indexes the first capture
|
||||
** value, 'n' is the number of values (at least 1).
|
||||
*/
|
||||
static void adddyncaptures (const char *s, Capture *base, int n, int fd) {
|
||||
int i;
|
||||
base[0].kind = Cgroup; /* create group capture */
|
||||
base[0].siz = 0;
|
||||
base[0].idx = 0; /* make it an anonymous group */
|
||||
for (i = 1; i <= n; i++) { /* add runtime captures */
|
||||
base[i].kind = Cruntime;
|
||||
base[i].siz = 1; /* mark it as closed */
|
||||
base[i].idx = fd + i - 1; /* stack index of capture value */
|
||||
base[i].s = s;
|
||||
}
|
||||
base[i].kind = Cclose; /* close group */
|
||||
base[i].siz = 1;
|
||||
base[i].s = s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove dynamic captures from the Lua stack (called in case of failure)
|
||||
*/
|
||||
static int removedyncap (lua_State *L, Capture *capture,
|
||||
int level, int last) {
|
||||
int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */
|
||||
int top = lua_gettop(L);
|
||||
if (id == 0) return 0; /* no dynamic captures? */
|
||||
lua_settop(L, id - 1); /* remove captures */
|
||||
return top - id + 1; /* number of values removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Opcode interpreter
|
||||
*/
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop) {
|
||||
Stack stackbase[INITBACK];
|
||||
Stack *stacklimit = stackbase + INITBACK;
|
||||
Stack *stack = stackbase; /* point to first empty slot in stack */
|
||||
int capsize = INITCAPSIZE;
|
||||
int captop = 0; /* point to first empty slot in captures */
|
||||
int ndyncap = 0; /* number of dynamic captures (in Lua stack) */
|
||||
const Instruction *p = op; /* current instruction */
|
||||
stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
|
||||
lua_pushlightuserdata(L, stackbase);
|
||||
for (;;) {
|
||||
#if defined(DEBUG)
|
||||
printf("-------------------------------------\n");
|
||||
printcaplist(capture, capture + captop);
|
||||
printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
|
||||
s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop);
|
||||
printinst(op, p);
|
||||
#endif
|
||||
assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IEnd: {
|
||||
assert(stack == getstackbase(L, ptop) + 1);
|
||||
capture[captop].kind = Cclose;
|
||||
capture[captop].s = NULL;
|
||||
return s;
|
||||
}
|
||||
case IGiveup: {
|
||||
assert(stack == getstackbase(L, ptop));
|
||||
return NULL;
|
||||
}
|
||||
case IRet: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
|
||||
p = (--stack)->p;
|
||||
continue;
|
||||
}
|
||||
case IAny: {
|
||||
if (s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestAny: {
|
||||
if (s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChar: {
|
||||
if ((byte)*s == p->i.aux && s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestChar: {
|
||||
if ((byte)*s == p->i.aux && s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ISet: {
|
||||
int c = (byte)*s;
|
||||
if (testchar((p+1)->buff, c) && s < e)
|
||||
{ p += CHARSETINSTSIZE; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestSet: {
|
||||
int c = (byte)*s;
|
||||
if (testchar((p + 2)->buff, c) && s < e)
|
||||
p += 1 + CHARSETINSTSIZE;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBehind: {
|
||||
int n = p->i.aux;
|
||||
if (n > s - o) goto fail;
|
||||
s -= n; p++;
|
||||
continue;
|
||||
}
|
||||
case ISpan: {
|
||||
for (; s < e; s++) {
|
||||
int c = (byte)*s;
|
||||
if (!testchar((p+1)->buff, c)) break;
|
||||
}
|
||||
p += CHARSETINSTSIZE;
|
||||
continue;
|
||||
}
|
||||
case IJmp: {
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChoice: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->p = p + getoffset(p);
|
||||
stack->s = s;
|
||||
stack->caplevel = captop;
|
||||
stack++;
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
case ICall: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->s = NULL;
|
||||
stack->p = p + 2; /* save return address */
|
||||
stack++;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ICommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
stack--;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IPartialCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
(stack - 1)->s = s;
|
||||
(stack - 1)->caplevel = captop;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBackCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
s = (--stack)->s;
|
||||
captop = stack->caplevel;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IFailTwice:
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
stack--;
|
||||
/* go through */
|
||||
case IFail:
|
||||
fail: { /* pattern failed: try to backtrack */
|
||||
do { /* remove pending calls */
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
s = (--stack)->s;
|
||||
} while (s == NULL);
|
||||
if (ndyncap > 0) /* is there matchtime captures? */
|
||||
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
|
||||
captop = stack->caplevel;
|
||||
p = stack->p;
|
||||
#if defined(DEBUG)
|
||||
printf("**FAIL**\n");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
case ICloseRunTime: {
|
||||
CapState cs;
|
||||
int rem, res, n;
|
||||
int fr = lua_gettop(L) + 1; /* stack index of first result */
|
||||
cs.s = o; cs.L = L; cs.ocap = capture; cs.ptop = ptop;
|
||||
n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
|
||||
captop -= n; /* remove nested captures */
|
||||
ndyncap -= rem; /* update number of dynamic captures */
|
||||
fr -= rem; /* 'rem' items were popped from Lua stack */
|
||||
res = resdyncaptures(L, fr, s - o, e - o); /* get result */
|
||||
if (res == -1) /* fail? */
|
||||
goto fail;
|
||||
s = o + res; /* else update current position */
|
||||
n = lua_gettop(L) - fr + 1; /* number of new captures */
|
||||
ndyncap += n; /* update number of dynamic captures */
|
||||
if (n > 0) { /* any new capture? */
|
||||
if (fr + n >= SHRT_MAX)
|
||||
luaL_error(L, "too many results in match-time capture");
|
||||
if ((captop += n + 2) >= capsize) {
|
||||
capture = doublecap(L, capture, captop, n + 2, ptop);
|
||||
capsize = 2 * captop;
|
||||
}
|
||||
/* add new captures to 'capture' list */
|
||||
adddyncaptures(s, capture + captop - n - 2, n, fr);
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
case ICloseCapture: {
|
||||
const char *s1 = s;
|
||||
assert(captop > 0);
|
||||
/* if possible, turn capture into a full capture */
|
||||
if (capture[captop - 1].siz == 0 &&
|
||||
s1 - capture[captop - 1].s < UCHAR_MAX) {
|
||||
capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
capture[captop].siz = 1; /* mark entry as closed */
|
||||
capture[captop].s = s;
|
||||
goto pushcapture;
|
||||
}
|
||||
}
|
||||
case IOpenCapture:
|
||||
capture[captop].siz = 0; /* mark entry as open */
|
||||
capture[captop].s = s;
|
||||
goto pushcapture;
|
||||
case IFullCapture:
|
||||
capture[captop].siz = getoff(p) + 1; /* save capture size */
|
||||
capture[captop].s = s - getoff(p);
|
||||
/* goto pushcapture; */
|
||||
pushcapture: {
|
||||
capture[captop].idx = p->i.key;
|
||||
capture[captop].kind = getkind(p);
|
||||
if (++captop >= capsize) {
|
||||
capture = doublecap(L, capture, captop, 0, ptop);
|
||||
capsize = 2 * captop;
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
default: assert(0); return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
** $Id: lpvm.h,v 1.3 2014/02/21 13:06:41 roberto Exp $
|
||||
*/
|
||||
|
||||
#if !defined(lpvm_h)
|
||||
#define lpvm_h
|
||||
|
||||
#include "lpcap.h"
|
||||
|
||||
|
||||
/* Virtual Machine's instructions */
|
||||
typedef enum Opcode {
|
||||
IAny, /* if no char, fail */
|
||||
IChar, /* if char != aux, fail */
|
||||
ISet, /* if char not in buff, fail */
|
||||
ITestAny, /* in no char, jump to 'offset' */
|
||||
ITestChar, /* if char != aux, jump to 'offset' */
|
||||
ITestSet, /* if char not in buff, jump to 'offset' */
|
||||
ISpan, /* read a span of chars in buff */
|
||||
IBehind, /* walk back 'aux' characters (fail if not possible) */
|
||||
IRet, /* return from a rule */
|
||||
IEnd, /* end of pattern */
|
||||
IChoice, /* stack a choice; next fail will jump to 'offset' */
|
||||
IJmp, /* jump to 'offset' */
|
||||
ICall, /* call rule at 'offset' */
|
||||
IOpenCall, /* call rule number 'key' (must be closed to a ICall) */
|
||||
ICommit, /* pop choice and jump to 'offset' */
|
||||
IPartialCommit, /* update top choice to current position and jump */
|
||||
IBackCommit, /* "fails" but jump to its own 'offset' */
|
||||
IFailTwice, /* pop one choice and then fail */
|
||||
IFail, /* go back to saved state on choice and jump to saved offset */
|
||||
IGiveup, /* internal use */
|
||||
IFullCapture, /* complete capture of last 'off' chars */
|
||||
IOpenCapture, /* start a capture */
|
||||
ICloseCapture,
|
||||
ICloseRunTime
|
||||
} Opcode;
|
||||
|
||||
|
||||
|
||||
typedef union Instruction {
|
||||
struct Inst {
|
||||
byte code;
|
||||
byte aux;
|
||||
short key;
|
||||
} i;
|
||||
int offset;
|
||||
byte buff[1];
|
||||
} Instruction;
|
||||
|
||||
|
||||
void printpatt (Instruction *p, int n);
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop);
|
||||
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
** $Id: lapi.h,v 2.9.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Auxiliary functions from Lua API
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lapi_h
|
||||
#define lapi_h
|
||||
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lstate.h"
|
||||
|
||||
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||
"stack overflow");}
|
||||
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
|
||||
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||
"not enough elements in the stack")
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
** $Id: lauxlib.h,v 1.131.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Auxiliary functions for building Lua libraries
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef lauxlib_h
|
||||
#define lauxlib_h
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
|
||||
/* extra error code for 'luaL_loadfilex' */
|
||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||
|
||||
|
||||
/* key, in the registry, for table of loaded modules */
|
||||
#define LUA_LOADED_TABLE "_LOADED"
|
||||
|
||||
|
||||
/* key, in the registry, for table of preloaded loaders */
|
||||
#define LUA_PRELOAD_TABLE "_PRELOAD"
|
||||
|
||||
|
||||
typedef struct luaL_Reg {
|
||||
const char *name;
|
||||
lua_CFunction func;
|
||||
} luaL_Reg;
|
||||
|
||||
|
||||
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
|
||||
|
||||
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
|
||||
#define luaL_checkversion(L) \
|
||||
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
|
||||
|
||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
||||
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
||||
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
|
||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
|
||||
size_t *l);
|
||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
|
||||
const char *def, size_t *l);
|
||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
|
||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
|
||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
|
||||
lua_Integer def);
|
||||
|
||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
||||
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
|
||||
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
|
||||
|
||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||
|
||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
||||
|
||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
|
||||
const char *const lst[]);
|
||||
|
||||
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
||||
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
|
||||
|
||||
/* predefined references */
|
||||
#define LUA_NOREF (-2)
|
||||
#define LUA_REFNIL (-1)
|
||||
|
||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
||||
|
||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||
const char *mode);
|
||||
|
||||
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
||||
|
||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name, const char *mode);
|
||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
||||
|
||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
|
||||
const char *r);
|
||||
|
||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
||||
|
||||
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
||||
|
||||
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
|
||||
const char *msg, int level);
|
||||
|
||||
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
lua_CFunction openf, int glb);
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** some useful macros
|
||||
** ===============================================================
|
||||
*/
|
||||
|
||||
|
||||
#define luaL_newlibtable(L,l) \
|
||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
||||
|
||||
#define luaL_newlib(L,l) \
|
||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
|
||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
|
||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
||||
|
||||
#define luaL_dofile(L, fn) \
|
||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_dostring(L, s) \
|
||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
||||
|
||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
|
||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
||||
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
typedef struct luaL_Buffer {
|
||||
char *b; /* buffer address */
|
||||
size_t size; /* buffer size */
|
||||
size_t n; /* number of characters in buffer */
|
||||
lua_State *L;
|
||||
char initb[LUAL_BUFFERSIZE]; /* initial buffer */
|
||||
} luaL_Buffer;
|
||||
|
||||
|
||||
#define luaL_addchar(B,c) \
|
||||
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
|
||||
((B)->b[(B)->n++] = (c)))
|
||||
|
||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
||||
|
||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
||||
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
||||
|
||||
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** File handles for IO library
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
|
||||
** initial structure 'luaL_Stream' (it may contain other fields
|
||||
** after that initial structure).
|
||||
*/
|
||||
|
||||
#define LUA_FILEHANDLE "FILE*"
|
||||
|
||||
|
||||
typedef struct luaL_Stream {
|
||||
FILE *f; /* stream (NULL for incompletely created streams) */
|
||||
lua_CFunction closef; /* to close stream (NULL for closed streams) */
|
||||
} luaL_Stream;
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/* compatibility with old module system */
|
||||
#if defined(LUA_COMPAT_MODULE)
|
||||
|
||||
LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname,
|
||||
int sizehint);
|
||||
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup);
|
||||
|
||||
#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** "Abstraction Layer" for basic report of messages and errors
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* print a string */
|
||||
#if !defined(lua_writestring)
|
||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
||||
#endif
|
||||
|
||||
/* print a newline and flush the output */
|
||||
#if !defined(lua_writeline)
|
||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
||||
#endif
|
||||
|
||||
/* print an error message */
|
||||
#if !defined(lua_writestringerror)
|
||||
#define lua_writestringerror(s,p) \
|
||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {============================================================
|
||||
** Compatibility with deprecated conversions
|
||||
** =============================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
||||
|
||||
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
|
||||
#define luaL_optunsigned(L,a,d) \
|
||||
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
|
||||
|
||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
||||
|
||||
#endif
|
||||
/* }============================================================ */
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
** $Id: lbaselib.c,v 1.314.1.1 2017/04/19 17:39:34 roberto Exp $
|
||||
** Basic library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lbaselib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
static int luaB_print (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int i;
|
||||
lua_getglobal(L, "tostring");
|
||||
for (i=1; i<=n; i++) {
|
||||
const char *s;
|
||||
size_t l;
|
||||
lua_pushvalue(L, -1); /* function to be called */
|
||||
lua_pushvalue(L, i); /* value to print */
|
||||
lua_call(L, 1, 1);
|
||||
s = lua_tolstring(L, -1, &l); /* get result */
|
||||
if (s == NULL)
|
||||
return luaL_error(L, "'tostring' must return a string to 'print'");
|
||||
if (i>1) lua_writestring("\t", 1);
|
||||
lua_writestring(s, l);
|
||||
lua_pop(L, 1); /* pop result */
|
||||
}
|
||||
lua_writeline();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define SPACECHARS " \f\n\r\t\v"
|
||||
|
||||
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
|
||||
lua_Unsigned n = 0;
|
||||
int neg = 0;
|
||||
s += strspn(s, SPACECHARS); /* skip initial spaces */
|
||||
if (*s == '-') { s++; neg = 1; } /* handle signal */
|
||||
else if (*s == '+') s++;
|
||||
if (!isalnum((unsigned char)*s)) /* no digit? */
|
||||
return NULL;
|
||||
do {
|
||||
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
|
||||
: (toupper((unsigned char)*s) - 'A') + 10;
|
||||
if (digit >= base) return NULL; /* invalid numeral */
|
||||
n = n * base + digit;
|
||||
s++;
|
||||
} while (isalnum((unsigned char)*s));
|
||||
s += strspn(s, SPACECHARS); /* skip trailing spaces */
|
||||
*pn = (lua_Integer)((neg) ? (0u - n) : n);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_tonumber (lua_State *L) {
|
||||
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
|
||||
luaL_checkany(L, 1);
|
||||
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
|
||||
lua_settop(L, 1); /* yes; return it */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
const char *s = lua_tolstring(L, 1, &l);
|
||||
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
|
||||
return 1; /* successful conversion to number */
|
||||
/* else not a number */
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
const char *s;
|
||||
lua_Integer n = 0; /* to avoid warnings */
|
||||
lua_Integer base = luaL_checkinteger(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
|
||||
s = lua_tolstring(L, 1, &l);
|
||||
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
|
||||
if (b_str2int(s, (int)base, &n) == s + l) {
|
||||
lua_pushinteger(L, n);
|
||||
return 1;
|
||||
} /* else not a number */
|
||||
} /* else not a number */
|
||||
lua_pushnil(L); /* not a number */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_error (lua_State *L) {
|
||||
int level = (int)luaL_optinteger(L, 2, 1);
|
||||
lua_settop(L, 1);
|
||||
if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
|
||||
luaL_where(L, level); /* add extra information */
|
||||
lua_pushvalue(L, 1);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_getmetatable (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (!lua_getmetatable(L, 1)) {
|
||||
lua_pushnil(L);
|
||||
return 1; /* no metatable */
|
||||
}
|
||||
luaL_getmetafield(L, 1, "__metatable");
|
||||
return 1; /* returns either __metatable field (if present) or metatable */
|
||||
}
|
||||
|
||||
|
||||
static int luaB_setmetatable (lua_State *L) {
|
||||
int t = lua_type(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
|
||||
"nil or table expected");
|
||||
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
|
||||
return luaL_error(L, "cannot change a protected metatable");
|
||||
lua_settop(L, 2);
|
||||
lua_setmetatable(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_rawequal (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
luaL_checkany(L, 2);
|
||||
lua_pushboolean(L, lua_rawequal(L, 1, 2));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_rawlen (lua_State *L) {
|
||||
int t = lua_type(L, 1);
|
||||
luaL_argcheck(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
|
||||
"table or string expected");
|
||||
lua_pushinteger(L, lua_rawlen(L, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_rawget (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checkany(L, 2);
|
||||
lua_settop(L, 2);
|
||||
lua_rawget(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luaB_rawset (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_checkany(L, 2);
|
||||
luaL_checkany(L, 3);
|
||||
lua_settop(L, 3);
|
||||
lua_rawset(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_collectgarbage (lua_State *L) {
|
||||
static const char *const opts[] = {"stop", "restart", "collect",
|
||||
"count", "step", "setpause", "setstepmul",
|
||||
"isrunning", NULL};
|
||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
||||
LUA_GCISRUNNING};
|
||||
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
|
||||
int ex = (int)luaL_optinteger(L, 2, 0);
|
||||
int res = lua_gc(L, o, ex);
|
||||
switch (o) {
|
||||
case LUA_GCCOUNT: {
|
||||
int b = lua_gc(L, LUA_GCCOUNTB, 0);
|
||||
lua_pushnumber(L, (lua_Number)res + ((lua_Number)b/1024));
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSTEP: case LUA_GCISRUNNING: {
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_type (lua_State *L) {
|
||||
int t = lua_type(L, 1);
|
||||
luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
|
||||
lua_pushstring(L, lua_typename(L, t));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int pairsmeta (lua_State *L, const char *method, int iszero,
|
||||
lua_CFunction iter) {
|
||||
luaL_checkany(L, 1);
|
||||
if (luaL_getmetafield(L, 1, method) == LUA_TNIL) { /* no metamethod? */
|
||||
lua_pushcfunction(L, iter); /* will return generator, */
|
||||
lua_pushvalue(L, 1); /* state, */
|
||||
if (iszero) lua_pushinteger(L, 0); /* and initial value */
|
||||
else lua_pushnil(L);
|
||||
}
|
||||
else {
|
||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
||||
lua_call(L, 1, 3); /* get 3 values from metamethod */
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_next (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
|
||||
if (lua_next(L, 1))
|
||||
return 2;
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_pairs (lua_State *L) {
|
||||
return pairsmeta(L, "__pairs", 0, luaB_next);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Traversal function for 'ipairs'
|
||||
*/
|
||||
static int ipairsaux (lua_State *L) {
|
||||
lua_Integer i = luaL_checkinteger(L, 2) + 1;
|
||||
lua_pushinteger(L, i);
|
||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
|
||||
** (The given "table" may not be a table.)
|
||||
*/
|
||||
static int luaB_ipairs (lua_State *L) {
|
||||
#if defined(LUA_COMPAT_IPAIRS)
|
||||
return pairsmeta(L, "__ipairs", 1, ipairsaux);
|
||||
#else
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushcfunction(L, ipairsaux); /* iteration function */
|
||||
lua_pushvalue(L, 1); /* state */
|
||||
lua_pushinteger(L, 0); /* initial value */
|
||||
return 3;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static int load_aux (lua_State *L, int status, int envidx) {
|
||||
if (status == LUA_OK) {
|
||||
if (envidx != 0) { /* 'env' parameter? */
|
||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
||||
lua_pop(L, 1); /* remove 'env' if not used by previous call */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else { /* error (message is on top of the stack) */
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2); /* put before error message */
|
||||
return 2; /* return nil plus error message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_loadfile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
const char *mode = luaL_optstring(L, 2, NULL);
|
||||
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
|
||||
int status = luaL_loadfilex(L, fname, mode);
|
||||
return load_aux(L, status, env);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Read function
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** reserved slot, above all arguments, to hold a copy of the returned
|
||||
** string to avoid it being collected while parsed. 'load' has four
|
||||
** optional arguments (chunk, source name, mode, and environment).
|
||||
*/
|
||||
#define RESERVEDSLOT 5
|
||||
|
||||
|
||||
/*
|
||||
** Reader for generic 'load' function: 'lua_load' uses the
|
||||
** stack for internal stuff, so the reader cannot change the
|
||||
** stack top. Instead, it keeps its resulting string in a
|
||||
** reserved slot inside the stack.
|
||||
*/
|
||||
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
||||
(void)(ud); /* not used */
|
||||
luaL_checkstack(L, 2, "too many nested functions");
|
||||
lua_pushvalue(L, 1); /* get function */
|
||||
lua_call(L, 0, 1); /* call it */
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1); /* pop result */
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
else if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "reader function must return a string");
|
||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_load (lua_State *L) {
|
||||
int status;
|
||||
size_t l;
|
||||
const char *s = lua_tolstring(L, 1, &l);
|
||||
const char *mode = luaL_optstring(L, 3, "bt");
|
||||
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
|
||||
if (s != NULL) { /* loading a string? */
|
||||
const char *chunkname = luaL_optstring(L, 2, s);
|
||||
status = luaL_loadbufferx(L, s, l, chunkname, mode);
|
||||
}
|
||||
else { /* loading from a reader function */
|
||||
const char *chunkname = luaL_optstring(L, 2, "=(load)");
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_settop(L, RESERVEDSLOT); /* create reserved slot */
|
||||
status = lua_load(L, generic_reader, NULL, chunkname, mode);
|
||||
}
|
||||
return load_aux(L, status, env);
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
||||
(void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
|
||||
return lua_gettop(L) - 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_dofile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
lua_settop(L, 1);
|
||||
if (luaL_loadfile(L, fname) != LUA_OK)
|
||||
return lua_error(L);
|
||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
||||
return dofilecont(L, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_assert (lua_State *L) {
|
||||
if (lua_toboolean(L, 1)) /* condition is true? */
|
||||
return lua_gettop(L); /* return all arguments */
|
||||
else { /* error */
|
||||
luaL_checkany(L, 1); /* there must be a condition */
|
||||
lua_remove(L, 1); /* remove it */
|
||||
lua_pushliteral(L, "assertion failed!"); /* default message */
|
||||
lua_settop(L, 1); /* leave only message (default if no other one) */
|
||||
return luaB_error(L); /* call 'error' */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_select (lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
|
||||
lua_pushinteger(L, n-1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_Integer i = luaL_checkinteger(L, 1);
|
||||
if (i < 0) i = n + i;
|
||||
else if (i > n) i = n;
|
||||
luaL_argcheck(L, 1 <= i, 1, "index out of range");
|
||||
return n - (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Continuation function for 'pcall' and 'xpcall'. Both functions
|
||||
** already pushed a 'true' before doing the call, so in case of success
|
||||
** 'finishpcall' only has to return everything in the stack minus
|
||||
** 'extra' values (where 'extra' is exactly the number of items to be
|
||||
** ignored).
|
||||
*/
|
||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
||||
if (status != LUA_OK && status != LUA_YIELD) { /* error? */
|
||||
lua_pushboolean(L, 0); /* first result (false) */
|
||||
lua_pushvalue(L, -2); /* error message */
|
||||
return 2; /* return false, msg */
|
||||
}
|
||||
else
|
||||
return lua_gettop(L) - (int)extra; /* return all results */
|
||||
}
|
||||
|
||||
|
||||
static int luaB_pcall (lua_State *L) {
|
||||
int status;
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushboolean(L, 1); /* first result if no errors */
|
||||
lua_insert(L, 1); /* put it in place */
|
||||
status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
|
||||
return finishpcall(L, status, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do a protected call with error handling. After 'lua_rotate', the
|
||||
** stack will have <f, err, true, f, [args...]>; so, the function passes
|
||||
** 2 to 'finishpcall' to skip the 2 first values when returning results.
|
||||
*/
|
||||
static int luaB_xpcall (lua_State *L) {
|
||||
int status;
|
||||
int n = lua_gettop(L);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
|
||||
lua_pushboolean(L, 1); /* first result */
|
||||
lua_pushvalue(L, 1); /* function */
|
||||
lua_rotate(L, 3, 2); /* move them below function's arguments */
|
||||
status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
|
||||
return finishpcall(L, status, 2);
|
||||
}
|
||||
|
||||
|
||||
static int luaB_tostring (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
luaL_tolstring(L, 1, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg base_funcs[] = {
|
||||
{"assert", luaB_assert},
|
||||
{"collectgarbage", luaB_collectgarbage},
|
||||
{"dofile", luaB_dofile},
|
||||
{"error", luaB_error},
|
||||
{"getmetatable", luaB_getmetatable},
|
||||
{"ipairs", luaB_ipairs},
|
||||
{"loadfile", luaB_loadfile},
|
||||
{"load", luaB_load},
|
||||
#if defined(LUA_COMPAT_LOADSTRING)
|
||||
{"loadstring", luaB_load},
|
||||
#endif
|
||||
{"next", luaB_next},
|
||||
{"pairs", luaB_pairs},
|
||||
{"pcall", luaB_pcall},
|
||||
{"print", luaB_print},
|
||||
{"rawequal", luaB_rawequal},
|
||||
{"rawlen", luaB_rawlen},
|
||||
{"rawget", luaB_rawget},
|
||||
{"rawset", luaB_rawset},
|
||||
{"select", luaB_select},
|
||||
{"setmetatable", luaB_setmetatable},
|
||||
{"tonumber", luaB_tonumber},
|
||||
{"tostring", luaB_tostring},
|
||||
{"type", luaB_type},
|
||||
{"xpcall", luaB_xpcall},
|
||||
/* placeholders */
|
||||
{"_G", NULL},
|
||||
{"_VERSION", NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_base (lua_State *L) {
|
||||
/* open lib into global table */
|
||||
lua_pushglobaltable(L);
|
||||
luaL_setfuncs(L, base_funcs, 0);
|
||||
/* set global _G */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "_G");
|
||||
/* set global _VERSION */
|
||||
lua_pushliteral(L, LUA_VERSION);
|
||||
lua_setfield(L, -2, "_VERSION");
|
||||
return 1;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
** $Id: lcode.h,v 1.64.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Code generator for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lcode_h
|
||||
#define lcode_h
|
||||
|
||||
#include "llex.h"
|
||||
#include "lobject.h"
|
||||
#include "lopcodes.h"
|
||||
#include "lparser.h"
|
||||
|
||||
|
||||
/*
|
||||
** Marks the end of a patch list. It is an invalid value both as an absolute
|
||||
** address, and as a list link (would link an element to itself).
|
||||
*/
|
||||
#define NO_JUMP (-1)
|
||||
|
||||
|
||||
/*
|
||||
** grep "ORDER OPR" if you change these enums (ORDER OP)
|
||||
*/
|
||||
typedef enum BinOpr {
|
||||
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
|
||||
OPR_DIV,
|
||||
OPR_IDIV,
|
||||
OPR_BAND, OPR_BOR, OPR_BXOR,
|
||||
OPR_SHL, OPR_SHR,
|
||||
OPR_CONCAT,
|
||||
OPR_EQ, OPR_LT, OPR_LE,
|
||||
OPR_NE, OPR_GT, OPR_GE,
|
||||
OPR_AND, OPR_OR,
|
||||
OPR_NOBINOPR
|
||||
} BinOpr;
|
||||
|
||||
|
||||
typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
||||
|
||||
|
||||
/* get (pointer to) instruction of given 'expdesc' */
|
||||
#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
|
||||
|
||||
#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
|
||||
|
||||
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
|
||||
|
||||
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
|
||||
|
||||
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
|
||||
LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);
|
||||
LUAI_FUNC int luaK_codek (FuncState *fs, int reg, int k);
|
||||
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
||||
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
||||
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
||||
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
||||
LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
|
||||
LUAI_FUNC int luaK_intK (FuncState *fs, lua_Integer n);
|
||||
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
|
||||
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
|
||||
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
|
||||
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
|
||||
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_jump (FuncState *fs);
|
||||
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
|
||||
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
|
||||
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
|
||||
LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level);
|
||||
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
|
||||
LUAI_FUNC int luaK_getlabel (FuncState *fs);
|
||||
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
|
||||
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
|
||||
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
|
||||
expdesc *v2, int line);
|
||||
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
** $Id: lcorolib.c,v 1.10.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Coroutine Library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lcorolib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
static lua_State *getco (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, 1);
|
||||
luaL_argcheck(L, co, 1, "thread expected");
|
||||
return co;
|
||||
}
|
||||
|
||||
|
||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||
int status;
|
||||
if (!lua_checkstack(co, narg)) {
|
||||
lua_pushliteral(L, "too many arguments to resume");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) {
|
||||
lua_pushliteral(L, "cannot resume dead coroutine");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(L, co, narg);
|
||||
status = lua_resume(co, L, narg);
|
||||
if (status == LUA_OK || status == LUA_YIELD) {
|
||||
int nres = lua_gettop(co);
|
||||
if (!lua_checkstack(L, nres + 1)) {
|
||||
lua_pop(co, nres); /* remove results anyway */
|
||||
lua_pushliteral(L, "too many results to resume");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(co, L, nres); /* move yielded values */
|
||||
return nres;
|
||||
}
|
||||
else {
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
return -1; /* error flag */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_coresume (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
int r;
|
||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||
if (r < 0) {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return false + error message */
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 1);
|
||||
lua_insert(L, -(r + 1));
|
||||
return r + 1; /* return true + 'resume' returns */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int luaB_auxwrap (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||
int r = auxresume(L, co, lua_gettop(L));
|
||||
if (r < 0) {
|
||||
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
|
||||
luaL_where(L, 1); /* add extra info */
|
||||
lua_insert(L, -2);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
return lua_error(L); /* propagate error */
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_cocreate (lua_State *L) {
|
||||
lua_State *NL;
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
NL = lua_newthread(L);
|
||||
lua_pushvalue(L, 1); /* move function to top */
|
||||
lua_xmove(L, NL, 1); /* move function from L to NL */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_cowrap (lua_State *L) {
|
||||
luaB_cocreate(L);
|
||||
lua_pushcclosure(L, luaB_auxwrap, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_yield (lua_State *L) {
|
||||
return lua_yield(L, lua_gettop(L));
|
||||
}
|
||||
|
||||
|
||||
static int luaB_costatus (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
if (L == co) lua_pushliteral(L, "running");
|
||||
else {
|
||||
switch (lua_status(co)) {
|
||||
case LUA_YIELD:
|
||||
lua_pushliteral(L, "suspended");
|
||||
break;
|
||||
case LUA_OK: {
|
||||
lua_Debug ar;
|
||||
if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
|
||||
lua_pushliteral(L, "normal"); /* it is running */
|
||||
else if (lua_gettop(co) == 0)
|
||||
lua_pushliteral(L, "dead");
|
||||
else
|
||||
lua_pushliteral(L, "suspended"); /* initial state */
|
||||
break;
|
||||
}
|
||||
default: /* some error occurred */
|
||||
lua_pushliteral(L, "dead");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_yieldable (lua_State *L) {
|
||||
lua_pushboolean(L, lua_isyieldable(L));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_corunning (lua_State *L) {
|
||||
int ismain = lua_pushthread(L);
|
||||
lua_pushboolean(L, ismain);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg co_funcs[] = {
|
||||
{"create", luaB_cocreate},
|
||||
{"resume", luaB_coresume},
|
||||
{"running", luaB_corunning},
|
||||
{"status", luaB_costatus},
|
||||
{"wrap", luaB_cowrap},
|
||||
{"yield", luaB_yield},
|
||||
{"isyieldable", luaB_yieldable},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_coroutine (lua_State *L) {
|
||||
luaL_newlib(L, co_funcs);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
** $Id: lctype.c,v 1.12.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** 'ctype' functions for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lctype_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include "lctype.h"
|
||||
|
||||
#if !LUA_USE_CTYPE /* { */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
||||
0x00, /* EOZ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
|
||||
0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
|
||||
0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
#endif /* } */
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
** $Id: lctype.h,v 1.12.1.1 2013/04/12 18:48:47 roberto Exp $
|
||||
** 'ctype' functions for Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lctype_h
|
||||
#define lctype_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/*
|
||||
** WARNING: the functions defined here do not necessarily correspond
|
||||
** to the similar functions in the standard C ctype.h. They are
|
||||
** optimized for the specific needs of Lua
|
||||
*/
|
||||
|
||||
#if !defined(LUA_USE_CTYPE)
|
||||
|
||||
#if 'A' == 65 && '0' == 48
|
||||
/* ASCII case: can use its own tables; faster and fixed */
|
||||
#define LUA_USE_CTYPE 0
|
||||
#else
|
||||
/* must use standard C ctype */
|
||||
#define LUA_USE_CTYPE 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !LUA_USE_CTYPE /* { */
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "llimits.h"
|
||||
|
||||
|
||||
#define ALPHABIT 0
|
||||
#define DIGITBIT 1
|
||||
#define PRINTBIT 2
|
||||
#define SPACEBIT 3
|
||||
#define XDIGITBIT 4
|
||||
|
||||
|
||||
#define MASK(B) (1 << (B))
|
||||
|
||||
|
||||
/*
|
||||
** add 1 to char to allow index -1 (EOZ)
|
||||
*/
|
||||
#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
|
||||
|
||||
/*
|
||||
** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
|
||||
*/
|
||||
#define lislalpha(c) testprop(c, MASK(ALPHABIT))
|
||||
#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
|
||||
#define lisdigit(c) testprop(c, MASK(DIGITBIT))
|
||||
#define lisspace(c) testprop(c, MASK(SPACEBIT))
|
||||
#define lisprint(c) testprop(c, MASK(PRINTBIT))
|
||||
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
|
||||
|
||||
/*
|
||||
** this 'ltolower' only works for alphabetic characters
|
||||
*/
|
||||
#define ltolower(c) ((c) | ('A' ^ 'a'))
|
||||
|
||||
|
||||
/* two more entries for 0 and -1 (EOZ) */
|
||||
LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2];
|
||||
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/*
|
||||
** use standard C ctypes
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#define lislalpha(c) (isalpha(c) || (c) == '_')
|
||||
#define lislalnum(c) (isalnum(c) || (c) == '_')
|
||||
#define lisdigit(c) (isdigit(c))
|
||||
#define lisspace(c) (isspace(c))
|
||||
#define lisprint(c) (isprint(c))
|
||||
#define lisxdigit(c) (isxdigit(c))
|
||||
|
||||
#define ltolower(c) (tolower(c))
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
** $Id: ldblib.c,v 1.151.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Interface from Lua to its debug API
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define ldblib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
/*
|
||||
** The hook table at registry[&HOOKKEY] maps threads to their current
|
||||
** hook function. (We only need the unique address of 'HOOKKEY'.)
|
||||
*/
|
||||
static const int HOOKKEY = 0;
|
||||
|
||||
|
||||
/*
|
||||
** If L1 != L, L1 can be in any state, and therefore there are no
|
||||
** guarantees about its stack space; any push in L1 must be
|
||||
** checked.
|
||||
*/
|
||||
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
||||
if (L != L1 && !lua_checkstack(L1, n))
|
||||
luaL_error(L, "stack overflow");
|
||||
}
|
||||
|
||||
|
||||
static int db_getregistry (lua_State *L) {
|
||||
lua_pushvalue(L, LUA_REGISTRYINDEX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_getmetatable (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (!lua_getmetatable(L, 1)) {
|
||||
lua_pushnil(L); /* no metatable */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_setmetatable (lua_State *L) {
|
||||
int t = lua_type(L, 2);
|
||||
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
|
||||
"nil or table expected");
|
||||
lua_settop(L, 2);
|
||||
lua_setmetatable(L, 1);
|
||||
return 1; /* return 1st argument */
|
||||
}
|
||||
|
||||
|
||||
static int db_getuservalue (lua_State *L) {
|
||||
if (lua_type(L, 1) != LUA_TUSERDATA)
|
||||
lua_pushnil(L);
|
||||
else
|
||||
lua_getuservalue(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_setuservalue (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TUSERDATA);
|
||||
luaL_checkany(L, 2);
|
||||
lua_settop(L, 2);
|
||||
lua_setuservalue(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary function used by several library functions: check for
|
||||
** an optional thread as function's first argument and set 'arg' with
|
||||
** 1 if this argument is present (so that functions can skip it to
|
||||
** access their other arguments)
|
||||
*/
|
||||
static lua_State *getthread (lua_State *L, int *arg) {
|
||||
if (lua_isthread(L, 1)) {
|
||||
*arg = 1;
|
||||
return lua_tothread(L, 1);
|
||||
}
|
||||
else {
|
||||
*arg = 0;
|
||||
return L; /* function will operate over current thread */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Variations of 'lua_settable', used by 'db_getinfo' to put results
|
||||
** from 'lua_getinfo' into result table. Key is always a string;
|
||||
** value can be a string, an int, or a boolean.
|
||||
*/
|
||||
static void settabss (lua_State *L, const char *k, const char *v) {
|
||||
lua_pushstring(L, v);
|
||||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
static void settabsi (lua_State *L, const char *k, int v) {
|
||||
lua_pushinteger(L, v);
|
||||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
static void settabsb (lua_State *L, const char *k, int v) {
|
||||
lua_pushboolean(L, v);
|
||||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** In function 'db_getinfo', the call to 'lua_getinfo' may push
|
||||
** results on the stack; later it creates the result table to put
|
||||
** these objects. Function 'treatstackoption' puts the result from
|
||||
** 'lua_getinfo' on top of the result table so that it can call
|
||||
** 'lua_setfield'.
|
||||
*/
|
||||
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
|
||||
if (L == L1)
|
||||
lua_rotate(L, -2, 1); /* exchange object and table */
|
||||
else
|
||||
lua_xmove(L1, L, 1); /* move object to the "main" stack */
|
||||
lua_setfield(L, -2, fname); /* put object into table */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls 'lua_getinfo' and collects all results in a new table.
|
||||
** L1 needs stack space for an optional input (function) plus
|
||||
** two optional outputs (function and line table) from function
|
||||
** 'lua_getinfo'.
|
||||
*/
|
||||
static int db_getinfo (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *options = luaL_optstring(L, arg+2, "flnStu");
|
||||
checkstack(L, L1, 3);
|
||||
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
||||
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
||||
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
||||
lua_xmove(L, L1, 1);
|
||||
}
|
||||
else { /* stack level */
|
||||
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
|
||||
lua_pushnil(L); /* level out of range */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!lua_getinfo(L1, options, &ar))
|
||||
return luaL_argerror(L, arg+2, "invalid option");
|
||||
lua_newtable(L); /* table to collect results */
|
||||
if (strchr(options, 'S')) {
|
||||
settabss(L, "source", ar.source);
|
||||
settabss(L, "short_src", ar.short_src);
|
||||
settabsi(L, "linedefined", ar.linedefined);
|
||||
settabsi(L, "lastlinedefined", ar.lastlinedefined);
|
||||
settabss(L, "what", ar.what);
|
||||
}
|
||||
if (strchr(options, 'l'))
|
||||
settabsi(L, "currentline", ar.currentline);
|
||||
if (strchr(options, 'u')) {
|
||||
settabsi(L, "nups", ar.nups);
|
||||
settabsi(L, "nparams", ar.nparams);
|
||||
settabsb(L, "isvararg", ar.isvararg);
|
||||
}
|
||||
if (strchr(options, 'n')) {
|
||||
settabss(L, "name", ar.name);
|
||||
settabss(L, "namewhat", ar.namewhat);
|
||||
}
|
||||
if (strchr(options, 't'))
|
||||
settabsb(L, "istailcall", ar.istailcall);
|
||||
if (strchr(options, 'L'))
|
||||
treatstackoption(L, L1, "activelines");
|
||||
if (strchr(options, 'f'))
|
||||
treatstackoption(L, L1, "func");
|
||||
return 1; /* return table */
|
||||
}
|
||||
|
||||
|
||||
static int db_getlocal (lua_State *L) {
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
|
||||
if (lua_isfunction(L, arg + 1)) { /* function argument? */
|
||||
lua_pushvalue(L, arg + 1); /* push function */
|
||||
lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
|
||||
return 1; /* return only name (there is no value) */
|
||||
}
|
||||
else { /* stack-level argument */
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
checkstack(L, L1, 1);
|
||||
name = lua_getlocal(L1, &ar, nvar);
|
||||
if (name) {
|
||||
lua_xmove(L1, L, 1); /* move local value */
|
||||
lua_pushstring(L, name); /* push name */
|
||||
lua_rotate(L, -2, 1); /* re-order */
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L); /* no name (nor value) */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int db_setlocal (lua_State *L) {
|
||||
int arg;
|
||||
const char *name;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
luaL_checkany(L, arg+3);
|
||||
lua_settop(L, arg+3);
|
||||
checkstack(L, L1, 1);
|
||||
lua_xmove(L, L1, 1);
|
||||
name = lua_setlocal(L1, &ar, nvar);
|
||||
if (name == NULL)
|
||||
lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
|
||||
lua_pushstring(L, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** get (if 'get' is true) or set an upvalue from a closure
|
||||
*/
|
||||
static int auxupvalue (lua_State *L, int get) {
|
||||
const char *name;
|
||||
int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
|
||||
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
|
||||
if (name == NULL) return 0;
|
||||
lua_pushstring(L, name);
|
||||
lua_insert(L, -(get+1)); /* no-op if get is false */
|
||||
return get + 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_getupvalue (lua_State *L) {
|
||||
return auxupvalue(L, 1);
|
||||
}
|
||||
|
||||
|
||||
static int db_setupvalue (lua_State *L) {
|
||||
luaL_checkany(L, 3);
|
||||
return auxupvalue(L, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether a given upvalue from a given closure exists and
|
||||
** returns its index
|
||||
*/
|
||||
static int checkupval (lua_State *L, int argf, int argnup) {
|
||||
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
|
||||
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
|
||||
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup,
|
||||
"invalid upvalue index");
|
||||
return nup;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvalueid (lua_State *L) {
|
||||
int n = checkupval(L, 1, 2);
|
||||
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvaluejoin (lua_State *L) {
|
||||
int n1 = checkupval(L, 1, 2);
|
||||
int n2 = checkupval(L, 3, 4);
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
|
||||
lua_upvaluejoin(L, 1, n1, 3, n2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call hook function registered at hook table for the current
|
||||
** thread (if there is one)
|
||||
*/
|
||||
static void hookf (lua_State *L, lua_Debug *ar) {
|
||||
static const char *const hooknames[] =
|
||||
{"call", "return", "line", "count", "tail call"};
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
|
||||
lua_pushthread(L);
|
||||
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
|
||||
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
|
||||
if (ar->currentline >= 0)
|
||||
lua_pushinteger(L, ar->currentline); /* push current line */
|
||||
else lua_pushnil(L);
|
||||
lua_assert(lua_getinfo(L, "lS", ar));
|
||||
lua_call(L, 2, 0); /* call hook function */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a string mask (for 'sethook') into a bit mask
|
||||
*/
|
||||
static int makemask (const char *smask, int count) {
|
||||
int mask = 0;
|
||||
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
|
||||
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
|
||||
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
|
||||
if (count > 0) mask |= LUA_MASKCOUNT;
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a bit mask (for 'gethook') into a string mask
|
||||
*/
|
||||
static char *unmakemask (int mask, char *smask) {
|
||||
int i = 0;
|
||||
if (mask & LUA_MASKCALL) smask[i++] = 'c';
|
||||
if (mask & LUA_MASKRET) smask[i++] = 'r';
|
||||
if (mask & LUA_MASKLINE) smask[i++] = 'l';
|
||||
smask[i] = '\0';
|
||||
return smask;
|
||||
}
|
||||
|
||||
|
||||
static int db_sethook (lua_State *L) {
|
||||
int arg, mask, count;
|
||||
lua_Hook func;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
if (lua_isnoneornil(L, arg+1)) { /* no hook? */
|
||||
lua_settop(L, arg+1);
|
||||
func = NULL; mask = 0; count = 0; /* turn off hooks */
|
||||
}
|
||||
else {
|
||||
const char *smask = luaL_checkstring(L, arg+2);
|
||||
luaL_checktype(L, arg+1, LUA_TFUNCTION);
|
||||
count = (int)luaL_optinteger(L, arg + 3, 0);
|
||||
func = hookf; mask = makemask(smask, count);
|
||||
}
|
||||
if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) {
|
||||
lua_createtable(L, 0, 2); /* create a hook table */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */
|
||||
lua_pushstring(L, "k");
|
||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
|
||||
}
|
||||
checkstack(L, L1, 1);
|
||||
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
|
||||
lua_pushvalue(L, arg + 1); /* value (hook function) */
|
||||
lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
|
||||
lua_sethook(L1, func, mask, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int db_gethook (lua_State *L) {
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
char buff[5];
|
||||
int mask = lua_gethookmask(L1);
|
||||
lua_Hook hook = lua_gethook(L1);
|
||||
if (hook == NULL) /* no hook? */
|
||||
lua_pushnil(L);
|
||||
else if (hook != hookf) /* external hook? */
|
||||
lua_pushliteral(L, "external hook");
|
||||
else { /* hook table must exist */
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
|
||||
checkstack(L, L1, 1);
|
||||
lua_pushthread(L1); lua_xmove(L1, L, 1);
|
||||
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
|
||||
lua_remove(L, -2); /* remove hook table */
|
||||
}
|
||||
lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
|
||||
lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static int db_debug (lua_State *L) {
|
||||
for (;;) {
|
||||
char buffer[250];
|
||||
lua_writestringerror("%s", "lua_debug> ");
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
|
||||
strcmp(buffer, "cont\n") == 0)
|
||||
return 0;
|
||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||
lua_pcall(L, 0, 0, 0))
|
||||
lua_writestringerror("%s\n", lua_tostring(L, -1));
|
||||
lua_settop(L, 0); /* remove eventual returns */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int db_traceback (lua_State *L) {
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *msg = lua_tostring(L, arg + 1);
|
||||
if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
|
||||
lua_pushvalue(L, arg + 1); /* return it untouched */
|
||||
else {
|
||||
int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
|
||||
luaL_traceback(L, L1, msg, level);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg dblib[] = {
|
||||
{"debug", db_debug},
|
||||
{"getuservalue", db_getuservalue},
|
||||
{"gethook", db_gethook},
|
||||
{"getinfo", db_getinfo},
|
||||
{"getlocal", db_getlocal},
|
||||
{"getregistry", db_getregistry},
|
||||
{"getmetatable", db_getmetatable},
|
||||
{"getupvalue", db_getupvalue},
|
||||
{"upvaluejoin", db_upvaluejoin},
|
||||
{"upvalueid", db_upvalueid},
|
||||
{"setuservalue", db_setuservalue},
|
||||
{"sethook", db_sethook},
|
||||
{"setlocal", db_setlocal},
|
||||
{"setmetatable", db_setmetatable},
|
||||
{"setupvalue", db_setupvalue},
|
||||
{"traceback", db_traceback},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_debug (lua_State *L) {
|
||||
luaL_newlib(L, dblib);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
** $Id: ldebug.c,v 2.121.1.2 2017/07/10 17:21:50 roberto Exp $
|
||||
** Debug Interface
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define ldebug_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lapi.h"
|
||||
#include "lcode.h"
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lfunc.h"
|
||||
#include "lobject.h"
|
||||
#include "lopcodes.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "ltm.h"
|
||||
#include "lvm.h"
|
||||
|
||||
|
||||
|
||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL)
|
||||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue((ci)->func))
|
||||
|
||||
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
|
||||
|
||||
static int currentpc (CallInfo *ci) {
|
||||
lua_assert(isLua(ci));
|
||||
return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
|
||||
}
|
||||
|
||||
|
||||
static int currentline (CallInfo *ci) {
|
||||
return getfuncline(ci_func(ci)->p, currentpc(ci));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If function yielded, its 'func' can be in the 'extra' field. The
|
||||
** next function restores 'func' to its correct value for debugging
|
||||
** purposes. (It exchanges 'func' and 'extra'; so, when called again,
|
||||
** after debugging, it also "re-restores" ** 'func' to its altered value.
|
||||
*/
|
||||
static void swapextra (lua_State *L) {
|
||||
if (L->status == LUA_YIELD) {
|
||||
CallInfo *ci = L->ci; /* get function that yielded */
|
||||
StkId temp = ci->func; /* exchange its 'func' and 'extra' values */
|
||||
ci->func = restorestack(L, ci->extra);
|
||||
ci->extra = savestack(L, temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function can be called asynchronously (e.g. during a signal).
|
||||
** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by
|
||||
** 'resethookcount') are for debug only, and it is no problem if they
|
||||
** get arbitrary values (causes at most one wrong hook call). 'hookmask'
|
||||
** is an atomic value. We assume that pointers are atomic too (e.g., gcc
|
||||
** ensures that for all platforms where it runs). Moreover, 'hook' is
|
||||
** always checked before being called (see 'luaD_hook').
|
||||
*/
|
||||
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
||||
if (func == NULL || mask == 0) { /* turn off hooks? */
|
||||
mask = 0;
|
||||
func = NULL;
|
||||
}
|
||||
if (isLua(L->ci))
|
||||
L->oldpc = L->ci->u.l.savedpc;
|
||||
L->hook = func;
|
||||
L->basehookcount = count;
|
||||
resethookcount(L);
|
||||
L->hookmask = cast_byte(mask);
|
||||
}
|
||||
|
||||
|
||||
LUA_API lua_Hook lua_gethook (lua_State *L) {
|
||||
return L->hook;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_gethookmask (lua_State *L) {
|
||||
return L->hookmask;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_gethookcount (lua_State *L) {
|
||||
return L->basehookcount;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
|
||||
int status;
|
||||
CallInfo *ci;
|
||||
if (level < 0) return 0; /* invalid (negative) level */
|
||||
lua_lock(L);
|
||||
for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
|
||||
level--;
|
||||
if (level == 0 && ci != &L->base_ci) { /* level found? */
|
||||
status = 1;
|
||||
ar->i_ci = ci;
|
||||
}
|
||||
else status = 0; /* no such level */
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static const char *upvalname (Proto *p, int uv) {
|
||||
TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
|
||||
if (s == NULL) return "?";
|
||||
else return getstr(s);
|
||||
}
|
||||
|
||||
|
||||
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||
int nparams = clLvalue(ci->func)->p->numparams;
|
||||
int nvararg = cast_int(ci->u.l.base - ci->func) - nparams;
|
||||
if (n <= -nvararg)
|
||||
return NULL; /* no such vararg */
|
||||
else {
|
||||
*pos = ci->func + nparams - n;
|
||||
return "(*vararg)"; /* generic name for any vararg */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *findlocal (lua_State *L, CallInfo *ci, int n,
|
||||
StkId *pos) {
|
||||
const char *name = NULL;
|
||||
StkId base;
|
||||
if (isLua(ci)) {
|
||||
if (n < 0) /* access to vararg values? */
|
||||
return findvararg(ci, n, pos);
|
||||
else {
|
||||
base = ci->u.l.base;
|
||||
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
||||
}
|
||||
}
|
||||
else
|
||||
base = ci->func + 1;
|
||||
if (name == NULL) { /* no 'standard' name? */
|
||||
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
||||
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
|
||||
name = "(*temporary)"; /* generic name for any valid slot */
|
||||
else
|
||||
return NULL; /* no name */
|
||||
}
|
||||
*pos = base + (n - 1);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||
const char *name;
|
||||
lua_lock(L);
|
||||
swapextra(L);
|
||||
if (ar == NULL) { /* information about non-active function? */
|
||||
if (!isLfunction(L->top - 1)) /* not a Lua function? */
|
||||
name = NULL;
|
||||
else /* consider live variables at function start (parameters) */
|
||||
name = luaF_getlocalname(clLvalue(L->top - 1)->p, n, 0);
|
||||
}
|
||||
else { /* active function; get information through 'ar' */
|
||||
StkId pos = NULL; /* to avoid warnings */
|
||||
name = findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobj2s(L, L->top, pos);
|
||||
api_incr_top(L);
|
||||
}
|
||||
}
|
||||
swapextra(L);
|
||||
lua_unlock(L);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||
StkId pos = NULL; /* to avoid warnings */
|
||||
const char *name;
|
||||
lua_lock(L);
|
||||
swapextra(L);
|
||||
name = findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, pos, L->top - 1);
|
||||
L->top--; /* pop value */
|
||||
}
|
||||
swapextra(L);
|
||||
lua_unlock(L);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
static void funcinfo (lua_Debug *ar, Closure *cl) {
|
||||
if (noLuaClosure(cl)) {
|
||||
ar->source = "=[C]";
|
||||
ar->linedefined = -1;
|
||||
ar->lastlinedefined = -1;
|
||||
ar->what = "C";
|
||||
}
|
||||
else {
|
||||
Proto *p = cl->l.p;
|
||||
ar->source = p->source ? getstr(p->source) : "=?";
|
||||
ar->linedefined = p->linedefined;
|
||||
ar->lastlinedefined = p->lastlinedefined;
|
||||
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
|
||||
}
|
||||
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
|
||||
}
|
||||
|
||||
|
||||
static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
if (noLuaClosure(f)) {
|
||||
setnilvalue(L->top);
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
TValue v;
|
||||
int *lineinfo = f->l.p->lineinfo;
|
||||
Table *t = luaH_new(L); /* new table to store active lines */
|
||||
sethvalue(L, L->top, t); /* push it on stack */
|
||||
api_incr_top(L);
|
||||
setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */
|
||||
for (i = 0; i < f->l.p->sizelineinfo; i++) /* for all lines with code */
|
||||
luaH_setint(L, t, lineinfo[i], &v); /* table[line] = true */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
||||
if (ci == NULL) /* no 'ci'? */
|
||||
return NULL; /* no info */
|
||||
else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
/* calling function is a known Lua function? */
|
||||
else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous))
|
||||
return funcnamefromcode(L, ci->previous, name);
|
||||
else return NULL; /* no way to find a name */
|
||||
}
|
||||
|
||||
|
||||
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
||||
Closure *f, CallInfo *ci) {
|
||||
int status = 1;
|
||||
for (; *what; what++) {
|
||||
switch (*what) {
|
||||
case 'S': {
|
||||
funcinfo(ar, f);
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1;
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
|
||||
if (noLuaClosure(f)) {
|
||||
ar->isvararg = 1;
|
||||
ar->nparams = 0;
|
||||
}
|
||||
else {
|
||||
ar->isvararg = f->l.p->is_vararg;
|
||||
ar->nparams = f->l.p->numparams;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
ar->namewhat = getfuncname(L, ci, &ar->name);
|
||||
if (ar->namewhat == NULL) {
|
||||
ar->namewhat = ""; /* not found */
|
||||
ar->name = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
case 'f': /* handled by lua_getinfo */
|
||||
break;
|
||||
default: status = 0; /* invalid option */
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
||||
int status;
|
||||
Closure *cl;
|
||||
CallInfo *ci;
|
||||
StkId func;
|
||||
lua_lock(L);
|
||||
swapextra(L);
|
||||
if (*what == '>') {
|
||||
ci = NULL;
|
||||
func = L->top - 1;
|
||||
api_check(L, ttisfunction(func), "function expected");
|
||||
what++; /* skip the '>' */
|
||||
L->top--; /* pop function */
|
||||
}
|
||||
else {
|
||||
ci = ar->i_ci;
|
||||
func = ci->func;
|
||||
lua_assert(ttisfunction(ci->func));
|
||||
}
|
||||
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
||||
status = auxgetinfo(L, what, ar, cl, ci);
|
||||
if (strchr(what, 'f')) {
|
||||
setobjs2s(L, L->top, func);
|
||||
api_incr_top(L);
|
||||
}
|
||||
swapextra(L); /* correct before option 'L', which can raise a mem. error */
|
||||
if (strchr(what, 'L'))
|
||||
collectvalidlines(L, cl);
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Symbolic Execution
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static const char *getobjname (Proto *p, int lastpc, int reg,
|
||||
const char **name);
|
||||
|
||||
|
||||
/*
|
||||
** find a "name" for the RK value 'c'
|
||||
*/
|
||||
static void kname (Proto *p, int pc, int c, const char **name) {
|
||||
if (ISK(c)) { /* is 'c' a constant? */
|
||||
TValue *kvalue = &p->k[INDEXK(c)];
|
||||
if (ttisstring(kvalue)) { /* literal constant? */
|
||||
*name = svalue(kvalue); /* it is its own name */
|
||||
return;
|
||||
}
|
||||
/* else no reasonable name found */
|
||||
}
|
||||
else { /* 'c' is a register */
|
||||
const char *what = getobjname(p, pc, c, name); /* search for 'c' */
|
||||
if (what && *what == 'c') { /* found a constant name? */
|
||||
return; /* 'name' already filled */
|
||||
}
|
||||
/* else no reasonable name found */
|
||||
}
|
||||
*name = "?"; /* no reasonable name found */
|
||||
}
|
||||
|
||||
|
||||
static int filterpc (int pc, int jmptarget) {
|
||||
if (pc < jmptarget) /* is code conditional (inside a jump)? */
|
||||
return -1; /* cannot know who sets that register */
|
||||
else return pc; /* current position sets that register */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** try to find last instruction before 'lastpc' that modified register 'reg'
|
||||
*/
|
||||
static int findsetreg (Proto *p, int lastpc, int reg) {
|
||||
int pc;
|
||||
int setreg = -1; /* keep last instruction that changed 'reg' */
|
||||
int jmptarget = 0; /* any code before this address is conditional */
|
||||
for (pc = 0; pc < lastpc; pc++) {
|
||||
Instruction i = p->code[pc];
|
||||
OpCode op = GET_OPCODE(i);
|
||||
int a = GETARG_A(i);
|
||||
switch (op) {
|
||||
case OP_LOADNIL: {
|
||||
int b = GETARG_B(i);
|
||||
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */
|
||||
setreg = filterpc(pc, jmptarget);
|
||||
break;
|
||||
}
|
||||
case OP_TFORCALL: {
|
||||
if (reg >= a + 2) /* affect all regs above its base */
|
||||
setreg = filterpc(pc, jmptarget);
|
||||
break;
|
||||
}
|
||||
case OP_CALL:
|
||||
case OP_TAILCALL: {
|
||||
if (reg >= a) /* affect all registers above base */
|
||||
setreg = filterpc(pc, jmptarget);
|
||||
break;
|
||||
}
|
||||
case OP_JMP: {
|
||||
int b = GETARG_sBx(i);
|
||||
int dest = pc + 1 + b;
|
||||
/* jump is forward and do not skip 'lastpc'? */
|
||||
if (pc < dest && dest <= lastpc) {
|
||||
if (dest > jmptarget)
|
||||
jmptarget = dest; /* update 'jmptarget' */
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (testAMode(op) && reg == a) /* any instruction that set A */
|
||||
setreg = filterpc(pc, jmptarget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return setreg;
|
||||
}
|
||||
|
||||
|
||||
static const char *getobjname (Proto *p, int lastpc, int reg,
|
||||
const char **name) {
|
||||
int pc;
|
||||
*name = luaF_getlocalname(p, reg + 1, lastpc);
|
||||
if (*name) /* is a local? */
|
||||
return "local";
|
||||
/* else try symbolic execution */
|
||||
pc = findsetreg(p, lastpc, reg);
|
||||
if (pc != -1) { /* could find instruction? */
|
||||
Instruction i = p->code[pc];
|
||||
OpCode op = GET_OPCODE(i);
|
||||
switch (op) {
|
||||
case OP_MOVE: {
|
||||
int b = GETARG_B(i); /* move from 'b' to 'a' */
|
||||
if (b < GETARG_A(i))
|
||||
return getobjname(p, pc, b, name); /* get name for 'b' */
|
||||
break;
|
||||
}
|
||||
case OP_GETTABUP:
|
||||
case OP_GETTABLE: {
|
||||
int k = GETARG_C(i); /* key index */
|
||||
int t = GETARG_B(i); /* table index */
|
||||
const char *vn = (op == OP_GETTABLE) /* name of indexed variable */
|
||||
? luaF_getlocalname(p, t + 1, pc)
|
||||
: upvalname(p, t);
|
||||
kname(p, pc, k, name);
|
||||
return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field";
|
||||
}
|
||||
case OP_GETUPVAL: {
|
||||
*name = upvalname(p, GETARG_B(i));
|
||||
return "upvalue";
|
||||
}
|
||||
case OP_LOADK:
|
||||
case OP_LOADKX: {
|
||||
int b = (op == OP_LOADK) ? GETARG_Bx(i)
|
||||
: GETARG_Ax(p->code[pc + 1]);
|
||||
if (ttisstring(&p->k[b])) {
|
||||
*name = svalue(&p->k[b]);
|
||||
return "constant";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_SELF: {
|
||||
int k = GETARG_C(i); /* key index */
|
||||
kname(p, pc, k, name);
|
||||
return "method";
|
||||
}
|
||||
default: break; /* go through to return NULL */
|
||||
}
|
||||
}
|
||||
return NULL; /* could not find reasonable name */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a name for a function based on the code that called it.
|
||||
** (Only works when function was called by a Lua function.)
|
||||
** Returns what the name is (e.g., "for iterator", "method",
|
||||
** "metamethod") and sets '*name' to point to the name.
|
||||
*/
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
TMS tm = (TMS)0; /* (initial value avoids warnings) */
|
||||
Proto *p = ci_func(ci)->p; /* calling function */
|
||||
int pc = currentpc(ci); /* calling instruction index */
|
||||
Instruction i = p->code[pc]; /* calling instruction */
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
switch (GET_OPCODE(i)) {
|
||||
case OP_CALL:
|
||||
case OP_TAILCALL:
|
||||
return getobjname(p, pc, GETARG_A(i), name); /* get function name */
|
||||
case OP_TFORCALL: { /* for iterator */
|
||||
*name = "for iterator";
|
||||
return "for iterator";
|
||||
}
|
||||
/* other instructions can do calls through metamethods */
|
||||
case OP_SELF: case OP_GETTABUP: case OP_GETTABLE:
|
||||
tm = TM_INDEX;
|
||||
break;
|
||||
case OP_SETTABUP: case OP_SETTABLE:
|
||||
tm = TM_NEWINDEX;
|
||||
break;
|
||||
case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD:
|
||||
case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND:
|
||||
case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: {
|
||||
int offset = cast_int(GET_OPCODE(i)) - cast_int(OP_ADD); /* ORDER OP */
|
||||
tm = cast(TMS, offset + cast_int(TM_ADD)); /* ORDER TM */
|
||||
break;
|
||||
}
|
||||
case OP_UNM: tm = TM_UNM; break;
|
||||
case OP_BNOT: tm = TM_BNOT; break;
|
||||
case OP_LEN: tm = TM_LEN; break;
|
||||
case OP_CONCAT: tm = TM_CONCAT; break;
|
||||
case OP_EQ: tm = TM_EQ; break;
|
||||
case OP_LT: tm = TM_LT; break;
|
||||
case OP_LE: tm = TM_LE; break;
|
||||
default:
|
||||
return NULL; /* cannot find a reasonable name */
|
||||
}
|
||||
*name = getstr(G(L)->tmname[tm]);
|
||||
return "metamethod";
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The subtraction of two potentially unrelated pointers is
|
||||
** not ISO C, but it should not crash a program; the subsequent
|
||||
** checks are ISO C and ensure a correct result.
|
||||
*/
|
||||
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||
ptrdiff_t i = o - ci->u.l.base;
|
||||
return (0 <= i && i < (ci->top - ci->u.l.base) && ci->u.l.base + i == o);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Checks whether value 'o' came from an upvalue. (That can only happen
|
||||
** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
|
||||
** upvalues.)
|
||||
*/
|
||||
static const char *getupvalname (CallInfo *ci, const TValue *o,
|
||||
const char **name) {
|
||||
LClosure *c = ci_func(ci);
|
||||
int i;
|
||||
for (i = 0; i < c->nupvalues; i++) {
|
||||
if (c->upvals[i]->v == o) {
|
||||
*name = upvalname(c->p, i);
|
||||
return "upvalue";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static const char *varinfo (lua_State *L, const TValue *o) {
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
CallInfo *ci = L->ci;
|
||||
const char *kind = NULL;
|
||||
if (isLua(ci)) {
|
||||
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
||||
if (!kind && isinstack(ci, o)) /* no? try a register */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
||||
cast_int(o - ci->u.l.base), &name);
|
||||
}
|
||||
return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : "";
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
const char *t = luaT_objtypename(L, o);
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o));
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||
if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
|
||||
luaG_typeerror(L, p1, "concatenate");
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2, const char *msg) {
|
||||
lua_Number temp;
|
||||
if (!tonumber(p1, &temp)) /* first operand is wrong? */
|
||||
p2 = p1; /* now second is wrong */
|
||||
luaG_typeerror(L, p2, msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Error when both values are convertible to numbers, but not to integers
|
||||
*/
|
||||
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||
lua_Integer temp;
|
||||
if (!tointeger(p1, &temp))
|
||||
p2 = p1;
|
||||
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||
const char *t1 = luaT_objtypename(L, p1);
|
||||
const char *t2 = luaT_objtypename(L, p2);
|
||||
if (strcmp(t1, t2) == 0)
|
||||
luaG_runerror(L, "attempt to compare two %s values", t1);
|
||||
else
|
||||
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
|
||||
}
|
||||
|
||||
|
||||
/* add src:line information to 'msg' */
|
||||
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
|
||||
int line) {
|
||||
char buff[LUA_IDSIZE];
|
||||
if (src)
|
||||
luaO_chunkid(buff, getstr(src), LUA_IDSIZE);
|
||||
else { /* no source available; use "?" instead */
|
||||
buff[0] = '?'; buff[1] = '\0';
|
||||
}
|
||||
return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_errormsg (lua_State *L) {
|
||||
if (L->errfunc != 0) { /* is there an error handling function? */
|
||||
StkId errfunc = restorestack(L, L->errfunc);
|
||||
setobjs2s(L, L->top, L->top - 1); /* move argument */
|
||||
setobjs2s(L, L->top - 1, errfunc); /* push function */
|
||||
L->top++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top - 2, 1); /* call it */
|
||||
}
|
||||
luaD_throw(L, LUA_ERRRUN);
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
CallInfo *ci = L->ci;
|
||||
const char *msg;
|
||||
va_list argp;
|
||||
luaC_checkGC(L); /* error message uses memory */
|
||||
va_start(argp, fmt);
|
||||
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
||||
va_end(argp);
|
||||
if (isLua(ci)) /* if Lua function, add source:line information */
|
||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, currentline(ci));
|
||||
luaG_errormsg(L);
|
||||
}
|
||||
|
||||
|
||||
void luaG_traceexec (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
lu_byte mask = L->hookmask;
|
||||
int counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
|
||||
if (counthook)
|
||||
resethookcount(L); /* reset count */
|
||||
else if (!(mask & LUA_MASKLINE))
|
||||
return; /* no line hook and count != 0; nothing to be done */
|
||||
if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
|
||||
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
||||
return; /* do not call hook again (VM yielded, so it did not move) */
|
||||
}
|
||||
if (counthook)
|
||||
luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */
|
||||
if (mask & LUA_MASKLINE) {
|
||||
Proto *p = ci_func(ci)->p;
|
||||
int npc = pcRel(ci->u.l.savedpc, p);
|
||||
int newline = getfuncline(p, npc);
|
||||
if (npc == 0 || /* call linehook when enter a new function, */
|
||||
ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */
|
||||
newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */
|
||||
luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */
|
||||
}
|
||||
L->oldpc = ci->u.l.savedpc;
|
||||
if (L->status == LUA_YIELD) { /* did hook yield? */
|
||||
if (counthook)
|
||||
L->hookcount = 1; /* undo decrement to zero */
|
||||
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
|
||||
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
|
||||
ci->func = L->top - 1; /* protect stack below results */
|
||||
luaD_throw(L, LUA_YIELD);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
** $Id: ldebug.h,v 2.14.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Auxiliary functions from Debug Interface module
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef ldebug_h
|
||||
#define ldebug_h
|
||||
|
||||
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
|
||||
|
||||
#define getfuncline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : -1)
|
||||
|
||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||
|
||||
|
||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||
const char *opname);
|
||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2,
|
||||
const char *msg);
|
||||
LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
|
||||
const TValue *p2);
|
||||
LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
|
||||
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
|
||||
TString *src, int line);
|
||||
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
|
||||
LUAI_FUNC void luaG_traceexec (lua_State *L);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,802 @@
|
|||
/*
|
||||
** $Id: ldo.c,v 2.157.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Stack and Call structure of Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define ldo_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lapi.h"
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lfunc.h"
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lopcodes.h"
|
||||
#include "lparser.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "ltm.h"
|
||||
#include "lundump.h"
|
||||
#include "lvm.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
|
||||
#define errorstatus(s) ((s) > LUA_YIELD)
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Error-recovery functions
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
|
||||
** default, Lua handles errors with exceptions when compiling as
|
||||
** C++ code, with _longjmp/_setjmp when asked to use them, and with
|
||||
** longjmp/setjmp otherwise.
|
||||
*/
|
||||
#if !defined(LUAI_THROW) /* { */
|
||||
|
||||
#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */
|
||||
|
||||
/* C++ exceptions */
|
||||
#define LUAI_THROW(L,c) throw(c)
|
||||
#define LUAI_TRY(L,c,a) \
|
||||
try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
|
||||
#define luai_jmpbuf int /* dummy variable */
|
||||
|
||||
#elif defined(LUA_USE_POSIX) /* }{ */
|
||||
|
||||
/* in POSIX, try _longjmp/_setjmp (more efficient) */
|
||||
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
|
||||
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
|
||||
#define luai_jmpbuf jmp_buf
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C handling with long jumps */
|
||||
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
|
||||
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
|
||||
#define luai_jmpbuf jmp_buf
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
|
||||
/* chain list of long jump buffers */
|
||||
struct lua_longjmp {
|
||||
struct lua_longjmp *previous;
|
||||
luai_jmpbuf b;
|
||||
volatile int status; /* error code */
|
||||
};
|
||||
|
||||
|
||||
static void seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
||||
switch (errcode) {
|
||||
case LUA_ERRMEM: { /* memory error? */
|
||||
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
|
||||
break;
|
||||
}
|
||||
case LUA_ERRERR: {
|
||||
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
||||
break;
|
||||
}
|
||||
}
|
||||
L->top = oldtop + 1;
|
||||
}
|
||||
|
||||
|
||||
l_noret luaD_throw (lua_State *L, int errcode) {
|
||||
if (L->errorJmp) { /* thread has an error handler? */
|
||||
L->errorJmp->status = errcode; /* set status */
|
||||
LUAI_THROW(L, L->errorJmp); /* jump to it */
|
||||
}
|
||||
else { /* thread has no error handler */
|
||||
global_State *g = G(L);
|
||||
L->status = cast_byte(errcode); /* mark it as dead */
|
||||
if (g->mainthread->errorJmp) { /* main thread has a handler? */
|
||||
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
|
||||
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
|
||||
}
|
||||
else { /* no handler at all; abort */
|
||||
if (g->panic) { /* panic function? */
|
||||
seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
|
||||
if (L->ci->top < L->top)
|
||||
L->ci->top = L->top; /* pushing msg. can break this invariant */
|
||||
lua_unlock(L);
|
||||
g->panic(L); /* call panic function (last chance to jump out) */
|
||||
}
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
unsigned short oldnCcalls = L->nCcalls;
|
||||
struct lua_longjmp lj;
|
||||
lj.status = LUA_OK;
|
||||
lj.previous = L->errorJmp; /* chain new error handler */
|
||||
L->errorJmp = &lj;
|
||||
LUAI_TRY(L, &lj,
|
||||
(*f)(L, ud);
|
||||
);
|
||||
L->errorJmp = lj.previous; /* restore old error handler */
|
||||
L->nCcalls = oldnCcalls;
|
||||
return lj.status;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Stack reallocation
|
||||
** ===================================================================
|
||||
*/
|
||||
static void correctstack (lua_State *L, TValue *oldstack) {
|
||||
CallInfo *ci;
|
||||
UpVal *up;
|
||||
L->top = (L->top - oldstack) + L->stack;
|
||||
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
||||
up->v = (up->v - oldstack) + L->stack;
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
ci->top = (ci->top - oldstack) + L->stack;
|
||||
ci->func = (ci->func - oldstack) + L->stack;
|
||||
if (isLua(ci))
|
||||
ci->u.l.base = (ci->u.l.base - oldstack) + L->stack;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* some space for error handling */
|
||||
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
||||
|
||||
|
||||
void luaD_reallocstack (lua_State *L, int newsize) {
|
||||
TValue *oldstack = L->stack;
|
||||
int lim = L->stacksize;
|
||||
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
||||
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
|
||||
luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue);
|
||||
for (; lim < newsize; lim++)
|
||||
setnilvalue(L->stack + lim); /* erase new segment */
|
||||
L->stacksize = newsize;
|
||||
L->stack_last = L->stack + newsize - EXTRA_STACK;
|
||||
correctstack(L, oldstack);
|
||||
}
|
||||
|
||||
|
||||
void luaD_growstack (lua_State *L, int n) {
|
||||
int size = L->stacksize;
|
||||
if (size > LUAI_MAXSTACK) /* error after extra size? */
|
||||
luaD_throw(L, LUA_ERRERR);
|
||||
else {
|
||||
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
|
||||
int newsize = 2 * size;
|
||||
if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK;
|
||||
if (newsize < needed) newsize = needed;
|
||||
if (newsize > LUAI_MAXSTACK) { /* stack overflow? */
|
||||
luaD_reallocstack(L, ERRORSTACKSIZE);
|
||||
luaG_runerror(L, "stack overflow");
|
||||
}
|
||||
else
|
||||
luaD_reallocstack(L, newsize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int stackinuse (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
StkId lim = L->top;
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
if (lim < ci->top) lim = ci->top;
|
||||
}
|
||||
lua_assert(lim <= L->stack_last);
|
||||
return cast_int(lim - L->stack) + 1; /* part of stack in use */
|
||||
}
|
||||
|
||||
|
||||
void luaD_shrinkstack (lua_State *L) {
|
||||
int inuse = stackinuse(L);
|
||||
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK;
|
||||
if (goodsize > LUAI_MAXSTACK)
|
||||
goodsize = LUAI_MAXSTACK; /* respect stack limit */
|
||||
if (L->stacksize > LUAI_MAXSTACK) /* had been handling stack overflow? */
|
||||
luaE_freeCI(L); /* free all CIs (list grew because of an error) */
|
||||
else
|
||||
luaE_shrinkCI(L); /* shrink list */
|
||||
/* if thread is currently not handling a stack overflow and its
|
||||
good size is smaller than current size, shrink its stack */
|
||||
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) &&
|
||||
goodsize < L->stacksize)
|
||||
luaD_reallocstack(L, goodsize);
|
||||
else /* don't change stack */
|
||||
condmovestack(L,{},{}); /* (change only for debugging) */
|
||||
}
|
||||
|
||||
|
||||
void luaD_inctop (lua_State *L) {
|
||||
luaD_checkstack(L, 1);
|
||||
L->top++;
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** Call a hook for the given event. Make sure there is a hook to be
|
||||
** called. (Both 'L->hook' and 'L->hookmask', which triggers this
|
||||
** function, can be changed asynchronously by signals.)
|
||||
*/
|
||||
void luaD_hook (lua_State *L, int event, int line) {
|
||||
lua_Hook hook = L->hook;
|
||||
if (hook && L->allowhook) { /* make sure there is a hook */
|
||||
CallInfo *ci = L->ci;
|
||||
ptrdiff_t top = savestack(L, L->top);
|
||||
ptrdiff_t ci_top = savestack(L, ci->top);
|
||||
lua_Debug ar;
|
||||
ar.event = event;
|
||||
ar.currentline = line;
|
||||
ar.i_ci = ci;
|
||||
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
L->allowhook = 0; /* cannot call hooks inside a hook */
|
||||
ci->callstatus |= CIST_HOOKED;
|
||||
lua_unlock(L);
|
||||
(*hook)(L, &ar);
|
||||
lua_lock(L);
|
||||
lua_assert(!L->allowhook);
|
||||
L->allowhook = 1;
|
||||
ci->top = restorestack(L, ci_top);
|
||||
L->top = restorestack(L, top);
|
||||
ci->callstatus &= ~CIST_HOOKED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void callhook (lua_State *L, CallInfo *ci) {
|
||||
int hook = LUA_HOOKCALL;
|
||||
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
||||
if (isLua(ci->previous) &&
|
||||
GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) {
|
||||
ci->callstatus |= CIST_TAIL;
|
||||
hook = LUA_HOOKTAILCALL;
|
||||
}
|
||||
luaD_hook(L, hook, -1);
|
||||
ci->u.l.savedpc--; /* correct 'pc' */
|
||||
}
|
||||
|
||||
|
||||
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
|
||||
int i;
|
||||
int nfixargs = p->numparams;
|
||||
StkId base, fixed;
|
||||
/* move fixed parameters to final position */
|
||||
fixed = L->top - actual; /* first fixed argument */
|
||||
base = L->top; /* final position of first argument */
|
||||
for (i = 0; i < nfixargs && i < actual; i++) {
|
||||
setobjs2s(L, L->top++, fixed + i);
|
||||
setnilvalue(fixed + i); /* erase original copy (for GC) */
|
||||
}
|
||||
for (; i < nfixargs; i++)
|
||||
setnilvalue(L->top++); /* complete missing arguments */
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether __call metafield of 'func' is a function. If so, put
|
||||
** it in stack below original 'func' so that 'luaD_precall' can call
|
||||
** it. Raise an error if __call metafield is not a function.
|
||||
*/
|
||||
static void tryfuncTM (lua_State *L, StkId func) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
|
||||
StkId p;
|
||||
if (!ttisfunction(tm))
|
||||
luaG_typeerror(L, func, "call");
|
||||
/* Open a hole inside the stack at 'func' */
|
||||
for (p = L->top; p > func; p--)
|
||||
setobjs2s(L, p, p-1);
|
||||
L->top++; /* slot ensured by caller */
|
||||
setobj2s(L, func, tm); /* tag method is the new function to be called */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'.
|
||||
** Handle most typical cases (zero results for commands, one result for
|
||||
** expressions, multiple results for tail calls/single parameters)
|
||||
** separated.
|
||||
*/
|
||||
static int moveresults (lua_State *L, const TValue *firstResult, StkId res,
|
||||
int nres, int wanted) {
|
||||
switch (wanted) { /* handle typical cases separately */
|
||||
case 0: break; /* nothing to move */
|
||||
case 1: { /* one result needed */
|
||||
if (nres == 0) /* no results? */
|
||||
firstResult = luaO_nilobject; /* adjust with nil */
|
||||
setobjs2s(L, res, firstResult); /* move it to proper place */
|
||||
break;
|
||||
}
|
||||
case LUA_MULTRET: {
|
||||
int i;
|
||||
for (i = 0; i < nres; i++) /* move all results to correct place */
|
||||
setobjs2s(L, res + i, firstResult + i);
|
||||
L->top = res + nres;
|
||||
return 0; /* wanted == LUA_MULTRET */
|
||||
}
|
||||
default: {
|
||||
int i;
|
||||
if (wanted <= nres) { /* enough results? */
|
||||
for (i = 0; i < wanted; i++) /* move wanted results to correct place */
|
||||
setobjs2s(L, res + i, firstResult + i);
|
||||
}
|
||||
else { /* not enough results; use all of them plus nils */
|
||||
for (i = 0; i < nres; i++) /* move all results to correct place */
|
||||
setobjs2s(L, res + i, firstResult + i);
|
||||
for (; i < wanted; i++) /* complete wanted number of results */
|
||||
setnilvalue(res + i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
L->top = res + wanted; /* top points after the last result */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Finishes a function call: calls hook if necessary, removes CallInfo,
|
||||
** moves current number of results to proper place; returns 0 iff call
|
||||
** wanted multiple (variable number of) results.
|
||||
*/
|
||||
int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) {
|
||||
StkId res;
|
||||
int wanted = ci->nresults;
|
||||
if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) {
|
||||
if (L->hookmask & LUA_MASKRET) {
|
||||
ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */
|
||||
luaD_hook(L, LUA_HOOKRET, -1);
|
||||
firstResult = restorestack(L, fr);
|
||||
}
|
||||
L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */
|
||||
}
|
||||
res = ci->func; /* res == final position of 1st result */
|
||||
L->ci = ci->previous; /* back to caller */
|
||||
/* move results to proper place */
|
||||
return moveresults(L, firstResult, res, nres, wanted);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
|
||||
|
||||
|
||||
/* macro to check stack size, preserving 'p' */
|
||||
#define checkstackp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
||||
luaC_checkGC(L), /* stack grow uses memory */ \
|
||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
||||
|
||||
|
||||
/*
|
||||
** Prepares a function call: checks the stack, creates a new CallInfo
|
||||
** entry, fills in the relevant information, calls hook if needed.
|
||||
** If function is a C function, does the call, too. (Otherwise, leave
|
||||
** the execution ('luaV_execute') to the caller, to allow stackless
|
||||
** calls.) Returns true iff function has been executed (C function).
|
||||
*/
|
||||
int luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||
lua_CFunction f;
|
||||
CallInfo *ci;
|
||||
switch (ttype(func)) {
|
||||
case LUA_TCCL: /* C closure */
|
||||
f = clCvalue(func)->f;
|
||||
goto Cfunc;
|
||||
case LUA_TLCF: /* light C function */
|
||||
f = fvalue(func);
|
||||
Cfunc: {
|
||||
int n; /* number of returns */
|
||||
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
|
||||
ci = next_ci(L); /* now 'enter' new function */
|
||||
ci->nresults = nresults;
|
||||
ci->func = func;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
ci->callstatus = 0;
|
||||
if (L->hookmask & LUA_MASKCALL)
|
||||
luaD_hook(L, LUA_HOOKCALL, -1);
|
||||
lua_unlock(L);
|
||||
n = (*f)(L); /* do the actual call */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
luaD_poscall(L, ci, L->top - n, n);
|
||||
return 1;
|
||||
}
|
||||
case LUA_TLCL: { /* Lua function: prepare its call */
|
||||
StkId base;
|
||||
Proto *p = clLvalue(func)->p;
|
||||
int n = cast_int(L->top - func) - 1; /* number of real arguments */
|
||||
int fsize = p->maxstacksize; /* frame size */
|
||||
checkstackp(L, fsize, func);
|
||||
if (p->is_vararg)
|
||||
base = adjust_varargs(L, p, n);
|
||||
else { /* non vararg function */
|
||||
for (; n < p->numparams; n++)
|
||||
setnilvalue(L->top++); /* complete missing arguments */
|
||||
base = func + 1;
|
||||
}
|
||||
ci = next_ci(L); /* now 'enter' new function */
|
||||
ci->nresults = nresults;
|
||||
ci->func = func;
|
||||
ci->u.l.base = base;
|
||||
L->top = ci->top = base + fsize;
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
ci->callstatus = CIST_LUA;
|
||||
if (L->hookmask & LUA_MASKCALL)
|
||||
callhook(L, ci);
|
||||
return 0;
|
||||
}
|
||||
default: { /* not a function */
|
||||
checkstackp(L, 1, func); /* ensure space for metamethod */
|
||||
tryfuncTM(L, func); /* try to get '__call' metamethod */
|
||||
return luaD_precall(L, func, nresults); /* now it must be a function */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check appropriate error for stack overflow ("regular" overflow or
|
||||
** overflow while handling stack overflow). If 'nCalls' is larger than
|
||||
** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but
|
||||
** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to
|
||||
** allow overflow handling to work)
|
||||
*/
|
||||
static void stackerror (lua_State *L) {
|
||||
if (L->nCcalls == LUAI_MAXCCALLS)
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call a function (C or Lua). The function to be called is at *func.
|
||||
** The arguments are on the stack, right after the function.
|
||||
** When returns, all the results are on the stack, starting at the original
|
||||
** function position.
|
||||
*/
|
||||
void luaD_call (lua_State *L, StkId func, int nResults) {
|
||||
if (++L->nCcalls >= LUAI_MAXCCALLS)
|
||||
stackerror(L);
|
||||
if (!luaD_precall(L, func, nResults)) /* is a Lua function? */
|
||||
luaV_execute(L); /* call it */
|
||||
L->nCcalls--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Similar to 'luaD_call', but does not allow yields during the call
|
||||
*/
|
||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||
L->nny++;
|
||||
luaD_call(L, func, nResults);
|
||||
L->nny--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Completes the execution of an interrupted C function, calling its
|
||||
** continuation function.
|
||||
*/
|
||||
static void finishCcall (lua_State *L, int status) {
|
||||
CallInfo *ci = L->ci;
|
||||
int n;
|
||||
/* must have a continuation and must be able to call it */
|
||||
lua_assert(ci->u.c.k != NULL && L->nny == 0);
|
||||
/* error status can only happen in a protected call */
|
||||
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
|
||||
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
|
||||
ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */
|
||||
L->errfunc = ci->u.c.old_errfunc; /* with the same error function */
|
||||
}
|
||||
/* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already
|
||||
handled */
|
||||
adjustresults(L, ci->nresults);
|
||||
lua_unlock(L);
|
||||
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
luaD_poscall(L, ci, L->top - n, n); /* finish 'luaD_precall' */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Executes "full continuation" (everything in the stack) of a
|
||||
** previously interrupted coroutine until the stack is empty (or another
|
||||
** interruption long-jumps out of the loop). If the coroutine is
|
||||
** recovering from an error, 'ud' points to the error status, which must
|
||||
** be passed to the first continuation function (otherwise the default
|
||||
** status is LUA_YIELD).
|
||||
*/
|
||||
static void unroll (lua_State *L, void *ud) {
|
||||
if (ud != NULL) /* error status? */
|
||||
finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */
|
||||
while (L->ci != &L->base_ci) { /* something in the stack */
|
||||
if (!isLua(L->ci)) /* C function? */
|
||||
finishCcall(L, LUA_YIELD); /* complete its execution */
|
||||
else { /* Lua function */
|
||||
luaV_finishOp(L); /* finish interrupted instruction */
|
||||
luaV_execute(L); /* execute down to higher C 'boundary' */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a suspended protected call (a "recover point") for the
|
||||
** given thread.
|
||||
*/
|
||||
static CallInfo *findpcall (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */
|
||||
if (ci->callstatus & CIST_YPCALL)
|
||||
return ci;
|
||||
}
|
||||
return NULL; /* no pending pcall */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Recovers from an error in a coroutine. Finds a recover point (if
|
||||
** there is one) and completes the execution of the interrupted
|
||||
** 'luaD_pcall'. If there is no recover point, returns zero.
|
||||
*/
|
||||
static int recover (lua_State *L, int status) {
|
||||
StkId oldtop;
|
||||
CallInfo *ci = findpcall(L);
|
||||
if (ci == NULL) return 0; /* no recovery point */
|
||||
/* "finish" luaD_pcall */
|
||||
oldtop = restorestack(L, ci->extra);
|
||||
luaF_close(L, oldtop);
|
||||
seterrorobj(L, status, oldtop);
|
||||
L->ci = ci;
|
||||
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
|
||||
L->nny = 0; /* should be zero to be yieldable */
|
||||
luaD_shrinkstack(L);
|
||||
L->errfunc = ci->u.c.old_errfunc;
|
||||
return 1; /* continue running the coroutine */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Signal an error in the call to 'lua_resume', not in the execution
|
||||
** of the coroutine itself. (Such errors should not be handled by any
|
||||
** coroutine error handler and should not kill the coroutine.)
|
||||
*/
|
||||
static int resume_error (lua_State *L, const char *msg, int narg) {
|
||||
L->top -= narg; /* remove args from the stack */
|
||||
setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return LUA_ERRRUN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do the work for 'lua_resume' in protected mode. Most of the work
|
||||
** depends on the status of the coroutine: initial state, suspended
|
||||
** inside a hook, or regularly suspended (optionally with a continuation
|
||||
** function), plus erroneous cases: non-suspended coroutine or dead
|
||||
** coroutine.
|
||||
*/
|
||||
static void resume (lua_State *L, void *ud) {
|
||||
int n = *(cast(int*, ud)); /* number of arguments */
|
||||
StkId firstArg = L->top - n; /* first argument */
|
||||
CallInfo *ci = L->ci;
|
||||
if (L->status == LUA_OK) { /* starting a coroutine? */
|
||||
if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */
|
||||
luaV_execute(L); /* call it */
|
||||
}
|
||||
else { /* resuming from previous yield */
|
||||
lua_assert(L->status == LUA_YIELD);
|
||||
L->status = LUA_OK; /* mark that it is running (again) */
|
||||
ci->func = restorestack(L, ci->extra);
|
||||
if (isLua(ci)) /* yielded inside a hook? */
|
||||
luaV_execute(L); /* just continue running Lua code */
|
||||
else { /* 'common' yield */
|
||||
if (ci->u.c.k != NULL) { /* does it have a continuation function? */
|
||||
lua_unlock(L);
|
||||
n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
firstArg = L->top - n; /* yield results come from continuation */
|
||||
}
|
||||
luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_precall' */
|
||||
}
|
||||
unroll(L, NULL); /* run continuation */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) {
|
||||
int status;
|
||||
unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */
|
||||
lua_lock(L);
|
||||
if (L->status == LUA_OK) { /* may be starting a coroutine */
|
||||
if (L->ci != &L->base_ci) /* not in base level? */
|
||||
return resume_error(L, "cannot resume non-suspended coroutine", nargs);
|
||||
}
|
||||
else if (L->status != LUA_YIELD)
|
||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||
L->nCcalls = (from) ? from->nCcalls + 1 : 1;
|
||||
if (L->nCcalls >= LUAI_MAXCCALLS)
|
||||
return resume_error(L, "C stack overflow", nargs);
|
||||
luai_userstateresume(L, nargs);
|
||||
L->nny = 0; /* allow yields */
|
||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||
status = luaD_rawrunprotected(L, resume, &nargs);
|
||||
if (status == -1) /* error calling 'lua_resume'? */
|
||||
status = LUA_ERRRUN;
|
||||
else { /* continue running after recoverable errors */
|
||||
while (errorstatus(status) && recover(L, status)) {
|
||||
/* unroll continuation */
|
||||
status = luaD_rawrunprotected(L, unroll, &status);
|
||||
}
|
||||
if (errorstatus(status)) { /* unrecoverable error? */
|
||||
L->status = cast_byte(status); /* mark thread as 'dead' */
|
||||
seterrorobj(L, status, L->top); /* push error message */
|
||||
L->ci->top = L->top;
|
||||
}
|
||||
else lua_assert(status == L->status); /* normal end or yield */
|
||||
}
|
||||
L->nny = oldnny; /* restore 'nny' */
|
||||
L->nCcalls--;
|
||||
lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0));
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_isyieldable (lua_State *L) {
|
||||
return (L->nny == 0);
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
|
||||
lua_KFunction k) {
|
||||
CallInfo *ci = L->ci;
|
||||
luai_userstateyield(L, nresults);
|
||||
lua_lock(L);
|
||||
api_checknelems(L, nresults);
|
||||
if (L->nny > 0) {
|
||||
if (L != G(L)->mainthread)
|
||||
luaG_runerror(L, "attempt to yield across a C-call boundary");
|
||||
else
|
||||
luaG_runerror(L, "attempt to yield from outside a coroutine");
|
||||
}
|
||||
L->status = LUA_YIELD;
|
||||
ci->extra = savestack(L, ci->func); /* save current 'func' */
|
||||
if (isLua(ci)) { /* inside a hook? */
|
||||
api_check(L, k == NULL, "hooks cannot continue after yielding");
|
||||
}
|
||||
else {
|
||||
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
|
||||
ci->u.c.ctx = ctx; /* save context */
|
||||
ci->func = L->top - nresults - 1; /* protect stack below results */
|
||||
luaD_throw(L, LUA_YIELD);
|
||||
}
|
||||
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
|
||||
lua_unlock(L);
|
||||
return 0; /* return to 'luaD_hook' */
|
||||
}
|
||||
|
||||
|
||||
int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t old_top, ptrdiff_t ef) {
|
||||
int status;
|
||||
CallInfo *old_ci = L->ci;
|
||||
lu_byte old_allowhooks = L->allowhook;
|
||||
unsigned short old_nny = L->nny;
|
||||
ptrdiff_t old_errfunc = L->errfunc;
|
||||
L->errfunc = ef;
|
||||
status = luaD_rawrunprotected(L, func, u);
|
||||
if (status != LUA_OK) { /* an error occurred? */
|
||||
StkId oldtop = restorestack(L, old_top);
|
||||
luaF_close(L, oldtop); /* close possible pending closures */
|
||||
seterrorobj(L, status, oldtop);
|
||||
L->ci = old_ci;
|
||||
L->allowhook = old_allowhooks;
|
||||
L->nny = old_nny;
|
||||
luaD_shrinkstack(L);
|
||||
}
|
||||
L->errfunc = old_errfunc;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Execute a protected parser.
|
||||
*/
|
||||
struct SParser { /* data to 'f_parser' */
|
||||
ZIO *z;
|
||||
Mbuffer buff; /* dynamic structure used by the scanner */
|
||||
Dyndata dyd; /* dynamic structures used by the parser */
|
||||
const char *mode;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
|
||||
static void checkmode (lua_State *L, const char *mode, const char *x) {
|
||||
if (mode && strchr(mode, x[0]) == NULL) {
|
||||
luaO_pushfstring(L,
|
||||
"attempt to load a %s chunk (mode is '%s')", x, mode);
|
||||
luaD_throw(L, LUA_ERRSYNTAX);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void f_parser (lua_State *L, void *ud) {
|
||||
LClosure *cl;
|
||||
struct SParser *p = cast(struct SParser *, ud);
|
||||
int c = zgetc(p->z); /* read first character */
|
||||
if (c == LUA_SIGNATURE[0]) {
|
||||
checkmode(L, p->mode, "binary");
|
||||
cl = luaU_undump(L, p->z, p->name);
|
||||
}
|
||||
else {
|
||||
checkmode(L, p->mode, "text");
|
||||
cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
|
||||
}
|
||||
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
||||
luaF_initupvals(L, cl);
|
||||
}
|
||||
|
||||
|
||||
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
||||
const char *mode) {
|
||||
struct SParser p;
|
||||
int status;
|
||||
L->nny++; /* cannot yield during parsing */
|
||||
p.z = z; p.name = name; p.mode = mode;
|
||||
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
|
||||
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
|
||||
p.dyd.label.arr = NULL; p.dyd.label.size = 0;
|
||||
luaZ_initbuffer(L, &p.buff);
|
||||
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
|
||||
luaZ_freebuffer(L, &p.buff);
|
||||
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
|
||||
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
|
||||
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
|
||||
L->nny--;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
** $Id: ldo.h,v 2.29.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Stack and Call structure of Lua
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef ldo_h
|
||||
#define ldo_h
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
/*
|
||||
** Macro to check stack size and grow stack if needed. Parameters
|
||||
** 'pre'/'pos' allow the macro to preserve a pointer into the
|
||||
** stack across reallocations, doing the work only when needed.
|
||||
** 'condmovestack' is used in heavy tests to force a stack reallocation
|
||||
** at every check.
|
||||
*/
|
||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||
if (L->stack_last - L->top <= (n)) \
|
||||
{ pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); }
|
||||
|
||||
/* In general, 'pre'/'pos' are empty (nothing to save) */
|
||||
#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
|
||||
|
||||
|
||||
|
||||
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
|
||||
#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
|
||||
|
||||
|
||||
/* type of protected functions, to be ran by 'runprotected' */
|
||||
typedef void (*Pfunc) (lua_State *L, void *ud);
|
||||
|
||||
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
||||
const char *mode);
|
||||
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line);
|
||||
LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
|
||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
||||
LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult,
|
||||
int nres);
|
||||
LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
|
||||
LUAI_FUNC void luaD_growstack (lua_State *L, int n);
|
||||
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
|
||||
LUAI_FUNC void luaD_inctop (lua_State *L);
|
||||
|
||||
LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
|
||||
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
** $Id: ldump.c,v 2.37.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** save precompiled Lua chunks
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define ldump_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lundump.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
lua_State *L;
|
||||
lua_Writer writer;
|
||||
void *data;
|
||||
int strip;
|
||||
int status;
|
||||
} DumpState;
|
||||
|
||||
|
||||
/*
|
||||
** All high-level dumps go through DumpVector; you can change it to
|
||||
** change the endianness of the result
|
||||
*/
|
||||
#define DumpVector(v,n,D) DumpBlock(v,(n)*sizeof((v)[0]),D)
|
||||
|
||||
#define DumpLiteral(s,D) DumpBlock(s, sizeof(s) - sizeof(char), D)
|
||||
|
||||
|
||||
static void DumpBlock (const void *b, size_t size, DumpState *D) {
|
||||
if (D->status == 0 && size > 0) {
|
||||
lua_unlock(D->L);
|
||||
D->status = (*D->writer)(D->L, b, size, D->data);
|
||||
lua_lock(D->L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define DumpVar(x,D) DumpVector(&x,1,D)
|
||||
|
||||
|
||||
static void DumpByte (int y, DumpState *D) {
|
||||
lu_byte x = (lu_byte)y;
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpInt (int x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpNumber (lua_Number x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpInteger (lua_Integer x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpString (const TString *s, DumpState *D) {
|
||||
if (s == NULL)
|
||||
DumpByte(0, D);
|
||||
else {
|
||||
size_t size = tsslen(s) + 1; /* include trailing '\0' */
|
||||
const char *str = getstr(s);
|
||||
if (size < 0xFF)
|
||||
DumpByte(cast_int(size), D);
|
||||
else {
|
||||
DumpByte(0xFF, D);
|
||||
DumpVar(size, D);
|
||||
}
|
||||
DumpVector(str, size - 1, D); /* no need to save '\0' */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpCode (const Proto *f, DumpState *D) {
|
||||
DumpInt(f->sizecode, D);
|
||||
DumpVector(f->code, f->sizecode, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpFunction(const Proto *f, TString *psource, DumpState *D);
|
||||
|
||||
static void DumpConstants (const Proto *f, DumpState *D) {
|
||||
int i;
|
||||
int n = f->sizek;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
const TValue *o = &f->k[i];
|
||||
DumpByte(ttype(o), D);
|
||||
switch (ttype(o)) {
|
||||
case LUA_TNIL:
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
DumpByte(bvalue(o), D);
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
DumpNumber(fltvalue(o), D);
|
||||
break;
|
||||
case LUA_TNUMINT:
|
||||
DumpInteger(ivalue(o), D);
|
||||
break;
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR:
|
||||
DumpString(tsvalue(o), D);
|
||||
break;
|
||||
default:
|
||||
lua_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpProtos (const Proto *f, DumpState *D) {
|
||||
int i;
|
||||
int n = f->sizep;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpFunction(f->p[i], f->source, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpUpvalues (const Proto *f, DumpState *D) {
|
||||
int i, n = f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpByte(f->upvalues[i].instack, D);
|
||||
DumpByte(f->upvalues[i].idx, D);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpDebug (const Proto *f, DumpState *D) {
|
||||
int i, n;
|
||||
n = (D->strip) ? 0 : f->sizelineinfo;
|
||||
DumpInt(n, D);
|
||||
DumpVector(f->lineinfo, n, D);
|
||||
n = (D->strip) ? 0 : f->sizelocvars;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpString(f->locvars[i].varname, D);
|
||||
DumpInt(f->locvars[i].startpc, D);
|
||||
DumpInt(f->locvars[i].endpc, D);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpString(f->upvalues[i].name, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpFunction (const Proto *f, TString *psource, DumpState *D) {
|
||||
if (D->strip || f->source == psource)
|
||||
DumpString(NULL, D); /* no debug info or same source as its parent */
|
||||
else
|
||||
DumpString(f->source, D);
|
||||
DumpInt(f->linedefined, D);
|
||||
DumpInt(f->lastlinedefined, D);
|
||||
DumpByte(f->numparams, D);
|
||||
DumpByte(f->is_vararg, D);
|
||||
DumpByte(f->maxstacksize, D);
|
||||
DumpCode(f, D);
|
||||
DumpConstants(f, D);
|
||||
DumpUpvalues(f, D);
|
||||
DumpProtos(f, D);
|
||||
DumpDebug(f, D);
|
||||
}
|
||||
|
||||
|
||||
static void DumpHeader (DumpState *D) {
|
||||
DumpLiteral(LUA_SIGNATURE, D);
|
||||
DumpByte(LUAC_VERSION, D);
|
||||
DumpByte(LUAC_FORMAT, D);
|
||||
DumpLiteral(LUAC_DATA, D);
|
||||
DumpByte(sizeof(int), D);
|
||||
DumpByte(sizeof(size_t), D);
|
||||
DumpByte(sizeof(Instruction), D);
|
||||
DumpByte(sizeof(lua_Integer), D);
|
||||
DumpByte(sizeof(lua_Number), D);
|
||||
DumpInteger(LUAC_INT, D);
|
||||
DumpNumber(LUAC_NUM, D);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** dump Lua function as precompiled chunk
|
||||
*/
|
||||
int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
|
||||
int strip) {
|
||||
DumpState D;
|
||||
D.L = L;
|
||||
D.writer = w;
|
||||
D.data = data;
|
||||
D.strip = strip;
|
||||
D.status = 0;
|
||||
DumpHeader(&D);
|
||||
DumpByte(f->sizeupvalues, &D);
|
||||
DumpFunction(f, NULL, &D);
|
||||
return D.status;
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
** $Id: lfunc.c,v 2.45.1.1 2017/04/19 17:39:34 roberto Exp $
|
||||
** Auxiliary functions to manipulate prototypes and closures
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lfunc_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lfunc.h"
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
|
||||
CClosure *luaF_newCclosure (lua_State *L, int n) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TCCL, sizeCclosure(n));
|
||||
CClosure *c = gco2ccl(o);
|
||||
c->nupvalues = cast_byte(n);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
LClosure *luaF_newLclosure (lua_State *L, int n) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TLCL, sizeLclosure(n));
|
||||
LClosure *c = gco2lcl(o);
|
||||
c->p = NULL;
|
||||
c->nupvalues = cast_byte(n);
|
||||
while (n--) c->upvals[n] = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
** fill a closure with new closed upvalues
|
||||
*/
|
||||
void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
int i;
|
||||
for (i = 0; i < cl->nupvalues; i++) {
|
||||
UpVal *uv = luaM_new(L, UpVal);
|
||||
uv->refcount = 1;
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
cl->upvals[i] = uv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
UpVal **pp = &L->openupval;
|
||||
UpVal *p;
|
||||
UpVal *uv;
|
||||
lua_assert(isintwups(L) || L->openupval == NULL);
|
||||
while (*pp != NULL && (p = *pp)->v >= level) {
|
||||
lua_assert(upisopen(p));
|
||||
if (p->v == level) /* found a corresponding upvalue? */
|
||||
return p; /* return it */
|
||||
pp = &p->u.open.next;
|
||||
}
|
||||
/* not found: create a new upvalue */
|
||||
uv = luaM_new(L, UpVal);
|
||||
uv->refcount = 0;
|
||||
uv->u.open.next = *pp; /* link it to list of open upvalues */
|
||||
uv->u.open.touched = 1;
|
||||
*pp = uv;
|
||||
uv->v = level; /* current value lives in the stack */
|
||||
if (!isintwups(L)) { /* thread not in list of threads with upvalues? */
|
||||
L->twups = G(L)->twups; /* link it to the list */
|
||||
G(L)->twups = L;
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
|
||||
|
||||
void luaF_close (lua_State *L, StkId level) {
|
||||
UpVal *uv;
|
||||
while (L->openupval != NULL && (uv = L->openupval)->v >= level) {
|
||||
lua_assert(upisopen(uv));
|
||||
L->openupval = uv->u.open.next; /* remove from 'open' list */
|
||||
if (uv->refcount == 0) /* no references? */
|
||||
luaM_free(L, uv); /* free upvalue */
|
||||
else {
|
||||
setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */
|
||||
uv->v = &uv->u.value; /* now current value lives here */
|
||||
luaC_upvalbarrier(L, uv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Proto *luaF_newproto (lua_State *L) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TPROTO, sizeof(Proto));
|
||||
Proto *f = gco2p(o);
|
||||
f->k = NULL;
|
||||
f->sizek = 0;
|
||||
f->p = NULL;
|
||||
f->sizep = 0;
|
||||
f->code = NULL;
|
||||
f->cache = NULL;
|
||||
f->sizecode = 0;
|
||||
f->lineinfo = NULL;
|
||||
f->sizelineinfo = 0;
|
||||
f->upvalues = NULL;
|
||||
f->sizeupvalues = 0;
|
||||
f->numparams = 0;
|
||||
f->is_vararg = 0;
|
||||
f->maxstacksize = 0;
|
||||
f->locvars = NULL;
|
||||
f->sizelocvars = 0;
|
||||
f->linedefined = 0;
|
||||
f->lastlinedefined = 0;
|
||||
f->source = NULL;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
void luaF_freeproto (lua_State *L, Proto *f) {
|
||||
luaM_freearray(L, f->code, f->sizecode);
|
||||
luaM_freearray(L, f->p, f->sizep);
|
||||
luaM_freearray(L, f->k, f->sizek);
|
||||
luaM_freearray(L, f->lineinfo, f->sizelineinfo);
|
||||
luaM_freearray(L, f->locvars, f->sizelocvars);
|
||||
luaM_freearray(L, f->upvalues, f->sizeupvalues);
|
||||
luaM_free(L, f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Look for n-th local variable at line 'line' in function 'func'.
|
||||
** Returns NULL if not found.
|
||||
*/
|
||||
const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
|
||||
int i;
|
||||
for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
|
||||
if (pc < f->locvars[i].endpc) { /* is variable active? */
|
||||
local_number--;
|
||||
if (local_number == 0)
|
||||
return getstr(f->locvars[i].varname);
|
||||
}
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
** $Id: lfunc.h,v 2.15.1.1 2017/04/19 17:39:34 roberto Exp $
|
||||
** Auxiliary functions to manipulate prototypes and closures
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lfunc_h
|
||||
#define lfunc_h
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
|
||||
|
||||
#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
|
||||
cast(int, sizeof(TValue)*((n)-1)))
|
||||
|
||||
#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
|
||||
cast(int, sizeof(TValue *)*((n)-1)))
|
||||
|
||||
|
||||
/* test whether thread is in 'twups' list */
|
||||
#define isintwups(L) (L->twups != L)
|
||||
|
||||
|
||||
/*
|
||||
** maximum number of upvalues in a closure (both C and Lua). (Value
|
||||
** must fit in a VM register.)
|
||||
*/
|
||||
#define MAXUPVAL 255
|
||||
|
||||
|
||||
/*
|
||||
** Upvalues for Lua closures
|
||||
*/
|
||||
struct UpVal {
|
||||
TValue *v; /* points to stack or to its own value */
|
||||
lu_mem refcount; /* reference counter */
|
||||
union {
|
||||
struct { /* (when open) */
|
||||
UpVal *next; /* linked list */
|
||||
int touched; /* mark to avoid cycles with dead threads */
|
||||
} open;
|
||||
TValue value; /* the value (when closed) */
|
||||
} u;
|
||||
};
|
||||
|
||||
#define upisopen(up) ((up)->v != &(up)->u.value)
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
|
||||
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
|
||||
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
|
||||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_close (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||
int pc);
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
** $Id: lgc.h,v 2.91.1.1 2017/04/19 17:39:34 roberto Exp $
|
||||
** Garbage Collector
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lgc_h
|
||||
#define lgc_h
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
/*
|
||||
** Collectable objects may have one of three colors: white, which
|
||||
** means the object is not marked; gray, which means the
|
||||
** object is marked, but its references may be not marked; and
|
||||
** black, which means that the object and all its references are marked.
|
||||
** The main invariant of the garbage collector, while marking objects,
|
||||
** is that a black object can never point to a white one. Moreover,
|
||||
** any gray object must be in a "gray list" (gray, grayagain, weak,
|
||||
** allweak, ephemeron) so that it can be visited again before finishing
|
||||
** the collection cycle. These lists have no meaning when the invariant
|
||||
** is not being enforced (e.g., sweep phase).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* how much to allocate before next GC step */
|
||||
#if !defined(GCSTEPSIZE)
|
||||
/* ~100 small strings */
|
||||
#define GCSTEPSIZE (cast_int(100 * sizeof(TString)))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Possible states of the Garbage Collector
|
||||
*/
|
||||
#define GCSpropagate 0
|
||||
#define GCSatomic 1
|
||||
#define GCSswpallgc 2
|
||||
#define GCSswpfinobj 3
|
||||
#define GCSswptobefnz 4
|
||||
#define GCSswpend 5
|
||||
#define GCScallfin 6
|
||||
#define GCSpause 7
|
||||
|
||||
|
||||
#define issweepphase(g) \
|
||||
(GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend)
|
||||
|
||||
|
||||
/*
|
||||
** macro to tell when main invariant (white objects cannot point to black
|
||||
** ones) must be kept. During a collection, the sweep
|
||||
** phase may break the invariant, as objects turned white may point to
|
||||
** still-black objects. The invariant is restored when sweep ends and
|
||||
** all objects are white again.
|
||||
*/
|
||||
|
||||
#define keepinvariant(g) ((g)->gcstate <= GCSatomic)
|
||||
|
||||
|
||||
/*
|
||||
** some useful bit tricks
|
||||
*/
|
||||
#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
|
||||
#define setbits(x,m) ((x) |= (m))
|
||||
#define testbits(x,m) ((x) & (m))
|
||||
#define bitmask(b) (1<<(b))
|
||||
#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
|
||||
#define l_setbit(x,b) setbits(x, bitmask(b))
|
||||
#define resetbit(x,b) resetbits(x, bitmask(b))
|
||||
#define testbit(x,b) testbits(x, bitmask(b))
|
||||
|
||||
|
||||
/* Layout for bit use in 'marked' field: */
|
||||
#define WHITE0BIT 0 /* object is white (type 0) */
|
||||
#define WHITE1BIT 1 /* object is white (type 1) */
|
||||
#define BLACKBIT 2 /* object is black */
|
||||
#define FINALIZEDBIT 3 /* object has been marked for finalization */
|
||||
/* bit 7 is currently used by tests (luaL_checkmemory) */
|
||||
|
||||
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
|
||||
|
||||
|
||||
#define iswhite(x) testbits((x)->marked, WHITEBITS)
|
||||
#define isblack(x) testbit((x)->marked, BLACKBIT)
|
||||
#define isgray(x) /* neither white nor black */ \
|
||||
(!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
|
||||
|
||||
#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT)
|
||||
|
||||
#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS)
|
||||
#define isdeadm(ow,m) (!(((m) ^ WHITEBITS) & (ow)))
|
||||
#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
|
||||
|
||||
#define changewhite(x) ((x)->marked ^= WHITEBITS)
|
||||
#define gray2black(x) l_setbit((x)->marked, BLACKBIT)
|
||||
|
||||
#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
|
||||
|
||||
|
||||
/*
|
||||
** Does one step of collection when debt becomes positive. 'pre'/'pos'
|
||||
** allows some adjustments to be done only when needed. macro
|
||||
** 'condchangemem' is used only for heavy tests (forcing a full
|
||||
** GC cycle on every opportunity)
|
||||
*/
|
||||
#define luaC_condGC(L,pre,pos) \
|
||||
{ if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
|
||||
condchangemem(L,pre,pos); }
|
||||
|
||||
/* more often than not, 'pre'/'pos' are empty */
|
||||
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
|
||||
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrier(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? \
|
||||
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
|
||||
|
||||
#define luaC_upvalbarrier(L,uv) ( \
|
||||
(iscollectable((uv)->v) && !upisopen(uv)) ? \
|
||||
luaC_upvalbarrier_(L,uv) : cast_void(0))
|
||||
|
||||
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
|
||||
LUAI_FUNC void luaC_step (lua_State *L);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
|
||||
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
|
||||
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
|
||||
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
|
||||
LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o);
|
||||
LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv);
|
||||
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
|
||||
LUAI_FUNC void luaC_upvdeccount (lua_State *L, UpVal *uv);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
** $Id: linit.c,v 1.39.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Initialization of libraries for lua.c and other clients
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
|
||||
#define linit_c
|
||||
#define LUA_LIB
|
||||
|
||||
/*
|
||||
** If you embed Lua in your program and need to open the standard
|
||||
** libraries, call luaL_openlibs in your program. If you need a
|
||||
** different set of libraries, copy this file to your project and edit
|
||||
** it to suit your needs.
|
||||
**
|
||||
** You can also *preload* libraries, so that a later 'require' can
|
||||
** open the library, which is already linked to the application.
|
||||
** For that, do the following code:
|
||||
**
|
||||
** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
** lua_pushcfunction(L, luaopen_modname);
|
||||
** lua_setfield(L, -2, modname);
|
||||
** lua_pop(L, 1); // remove PRELOAD table
|
||||
*/
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
|
||||
/*
|
||||
** these libs are loaded by lua.c and are readily available to any Lua
|
||||
** program
|
||||
*/
|
||||
static const luaL_Reg loadedlibs[] = {
|
||||
{"_G", luaopen_base},
|
||||
{LUA_LOADLIBNAME, luaopen_package},
|
||||
{LUA_COLIBNAME, luaopen_coroutine},
|
||||
{LUA_TABLIBNAME, luaopen_table},
|
||||
{LUA_IOLIBNAME, luaopen_io},
|
||||
{LUA_OSLIBNAME, luaopen_os},
|
||||
{LUA_STRLIBNAME, luaopen_string},
|
||||
{LUA_MATHLIBNAME, luaopen_math},
|
||||
{LUA_UTF8LIBNAME, luaopen_utf8},
|
||||
{LUA_DBLIBNAME, luaopen_debug},
|
||||
#if defined(LUA_COMPAT_BITLIB)
|
||||
{LUA_BITLIBNAME, luaopen_bit32},
|
||||
#endif
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API void luaL_openlibs (lua_State *L) {
|
||||
const luaL_Reg *lib;
|
||||
/* "require" functions from 'loadedlibs' and set results to global table */
|
||||
for (lib = loadedlibs; lib->func; lib++) {
|
||||
luaL_requiref(L, lib->name, lib->func, 1);
|
||||
lua_pop(L, 1); /* remove lib */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,778 @@
|
|||
/*
|
||||
** $Id: liolib.c,v 2.151.1.1 2017/04/19 17:29:57 roberto Exp $
|
||||
** Standard I/O (and system) library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define liolib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Change this macro to accept other modes for 'fopen' besides
|
||||
** the standard ones.
|
||||
*/
|
||||
#if !defined(l_checkmode)
|
||||
|
||||
/* accepted extensions to 'mode' in 'fopen' */
|
||||
#if !defined(L_MODEEXT)
|
||||
#define L_MODEEXT "b"
|
||||
#endif
|
||||
|
||||
/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
|
||||
static int l_checkmode (const char *mode) {
|
||||
return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
|
||||
(*mode != '+' || (++mode, 1)) && /* skip if char is '+' */
|
||||
(strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** l_popen spawns a new process connected to the current
|
||||
** one through the file streams.
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_popen) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#define l_popen(L,c,m) (fflush(NULL), popen(c,m))
|
||||
#define l_pclose(L,file) (pclose(file))
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) /* }{ */
|
||||
|
||||
#define l_popen(L,c,m) (_popen(c,m))
|
||||
#define l_pclose(L,file) (_pclose(file))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
#define l_popen(L,c,m) \
|
||||
((void)((void)c, m), \
|
||||
luaL_error(L, "'popen' not supported"), \
|
||||
(FILE*)0)
|
||||
#define l_pclose(L,file) ((void)L, (void)file, -1)
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
#if !defined(l_getc) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX)
|
||||
#define l_getc(f) getc_unlocked(f)
|
||||
#define l_lockfile(f) flockfile(f)
|
||||
#define l_unlockfile(f) funlockfile(f)
|
||||
#else
|
||||
#define l_getc(f) getc(f)
|
||||
#define l_lockfile(f) ((void)0)
|
||||
#define l_unlockfile(f) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** l_fseek: configuration for longer offsets
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_fseek) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define l_fseek(f,o,w) fseeko(f,o,w)
|
||||
#define l_ftell(f) ftello(f)
|
||||
#define l_seeknum off_t
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \
|
||||
&& defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */
|
||||
|
||||
/* Windows (but not DDK) and Visual C++ 2005 or higher */
|
||||
#define l_fseek(f,o,w) _fseeki64(f,o,w)
|
||||
#define l_ftell(f) _ftelli64(f)
|
||||
#define l_seeknum __int64
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
#define l_fseek(f,o,w) fseek(f,o,w)
|
||||
#define l_ftell(f) ftell(f)
|
||||
#define l_seeknum long
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
#define IO_PREFIX "_IO_"
|
||||
#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
|
||||
#define IO_INPUT (IO_PREFIX "input")
|
||||
#define IO_OUTPUT (IO_PREFIX "output")
|
||||
|
||||
|
||||
typedef luaL_Stream LStream;
|
||||
|
||||
|
||||
#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
|
||||
|
||||
#define isclosed(p) ((p)->closef == NULL)
|
||||
|
||||
|
||||
static int io_type (lua_State *L) {
|
||||
LStream *p;
|
||||
luaL_checkany(L, 1);
|
||||
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
|
||||
if (p == NULL)
|
||||
lua_pushnil(L); /* not a file */
|
||||
else if (isclosed(p))
|
||||
lua_pushliteral(L, "closed file");
|
||||
else
|
||||
lua_pushliteral(L, "file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_tostring (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (isclosed(p))
|
||||
lua_pushliteral(L, "file (closed)");
|
||||
else
|
||||
lua_pushfstring(L, "file (%p)", p->f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static FILE *tofile (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (isclosed(p))
|
||||
luaL_error(L, "attempt to use a closed file");
|
||||
lua_assert(p->f);
|
||||
return p->f;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** When creating file handles, always creates a 'closed' file handle
|
||||
** before opening the actual file; so, if there is a memory error, the
|
||||
** handle is in a consistent state.
|
||||
*/
|
||||
static LStream *newprefile (lua_State *L) {
|
||||
LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));
|
||||
p->closef = NULL; /* mark file handle as 'closed' */
|
||||
luaL_setmetatable(L, LUA_FILEHANDLE);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls the 'close' function from a file handle. The 'volatile' avoids
|
||||
** a bug in some versions of the Clang compiler (e.g., clang 3.0 for
|
||||
** 32 bits).
|
||||
*/
|
||||
static int aux_close (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
volatile lua_CFunction cf = p->closef;
|
||||
p->closef = NULL; /* mark stream as closed */
|
||||
return (*cf)(L); /* close it */
|
||||
}
|
||||
|
||||
|
||||
static int f_close (lua_State *L) {
|
||||
tofile(L); /* make sure argument is an open stream */
|
||||
return aux_close(L);
|
||||
}
|
||||
|
||||
|
||||
static int io_close (lua_State *L) {
|
||||
if (lua_isnone(L, 1)) /* no argument? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */
|
||||
return f_close(L);
|
||||
}
|
||||
|
||||
|
||||
static int f_gc (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (!isclosed(p) && p->f != NULL)
|
||||
aux_close(L); /* ignore closed and incompletely open files */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** function to close regular files
|
||||
*/
|
||||
static int io_fclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
int res = fclose(p->f);
|
||||
return luaL_fileresult(L, (res == 0), NULL);
|
||||
}
|
||||
|
||||
|
||||
static LStream *newfile (lua_State *L) {
|
||||
LStream *p = newprefile(L);
|
||||
p->f = NULL;
|
||||
p->closef = &io_fclose;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static void opencheck (lua_State *L, const char *fname, const char *mode) {
|
||||
LStream *p = newfile(L);
|
||||
p->f = fopen(fname, mode);
|
||||
if (p->f == NULL)
|
||||
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
static int io_open (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
LStream *p = newfile(L);
|
||||
const char *md = mode; /* to traverse/check mode */
|
||||
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
|
||||
p->f = fopen(filename, mode);
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** function to close 'popen' files
|
||||
*/
|
||||
static int io_pclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
return luaL_execresult(L, l_pclose(L, p->f));
|
||||
}
|
||||
|
||||
|
||||
static int io_popen (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
LStream *p = newprefile(L);
|
||||
luaL_argcheck(L, ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '\0'),
|
||||
2, "invalid mode");
|
||||
p->f = l_popen(L, filename, mode);
|
||||
p->closef = &io_pclose;
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_tmpfile (lua_State *L) {
|
||||
LStream *p = newfile(L);
|
||||
p->f = tmpfile();
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
|
||||
}
|
||||
|
||||
|
||||
static FILE *getiofile (lua_State *L, const char *findex) {
|
||||
LStream *p;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
||||
p = (LStream *)lua_touserdata(L, -1);
|
||||
if (isclosed(p))
|
||||
luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN);
|
||||
return p->f;
|
||||
}
|
||||
|
||||
|
||||
static int g_iofile (lua_State *L, const char *f, const char *mode) {
|
||||
if (!lua_isnoneornil(L, 1)) {
|
||||
const char *filename = lua_tostring(L, 1);
|
||||
if (filename)
|
||||
opencheck(L, filename, mode);
|
||||
else {
|
||||
tofile(L); /* check that it's a valid file handle */
|
||||
lua_pushvalue(L, 1);
|
||||
}
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, f);
|
||||
}
|
||||
/* return current value */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_input (lua_State *L) {
|
||||
return g_iofile(L, IO_INPUT, "r");
|
||||
}
|
||||
|
||||
|
||||
static int io_output (lua_State *L) {
|
||||
return g_iofile(L, IO_OUTPUT, "w");
|
||||
}
|
||||
|
||||
|
||||
static int io_readline (lua_State *L);
|
||||
|
||||
|
||||
/*
|
||||
** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
|
||||
** in the limit for upvalues of a closure)
|
||||
*/
|
||||
#define MAXARGLINE 250
|
||||
|
||||
static void aux_lines (lua_State *L, int toclose) {
|
||||
int n = lua_gettop(L) - 1; /* number of arguments to read */
|
||||
luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
|
||||
lua_pushinteger(L, n); /* number of arguments to read */
|
||||
lua_pushboolean(L, toclose); /* close/not close file when finished */
|
||||
lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */
|
||||
lua_pushcclosure(L, io_readline, 3 + n);
|
||||
}
|
||||
|
||||
|
||||
static int f_lines (lua_State *L) {
|
||||
tofile(L); /* check that it's a valid file handle */
|
||||
aux_lines(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int io_lines (lua_State *L) {
|
||||
int toclose;
|
||||
if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
|
||||
if (lua_isnil(L, 1)) { /* no file name? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
|
||||
lua_replace(L, 1); /* put it at index 1 */
|
||||
tofile(L); /* check that it's a valid file handle */
|
||||
toclose = 0; /* do not close it after iteration */
|
||||
}
|
||||
else { /* open a new file */
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
opencheck(L, filename, "r");
|
||||
lua_replace(L, 1); /* put file at index 1 */
|
||||
toclose = 1; /* close it after iteration */
|
||||
}
|
||||
aux_lines(L, toclose);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** READ
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
/* maximum length of a numeral */
|
||||
#if !defined (L_MAXLENNUM)
|
||||
#define L_MAXLENNUM 200
|
||||
#endif
|
||||
|
||||
|
||||
/* auxiliary structure used by 'read_number' */
|
||||
typedef struct {
|
||||
FILE *f; /* file being read */
|
||||
int c; /* current character (look ahead) */
|
||||
int n; /* number of elements in buffer 'buff' */
|
||||
char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */
|
||||
} RN;
|
||||
|
||||
|
||||
/*
|
||||
** Add current char to buffer (if not out of space) and read next one
|
||||
*/
|
||||
static int nextc (RN *rn) {
|
||||
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
|
||||
rn->buff[0] = '\0'; /* invalidate result */
|
||||
return 0; /* fail */
|
||||
}
|
||||
else {
|
||||
rn->buff[rn->n++] = rn->c; /* save current char */
|
||||
rn->c = l_getc(rn->f); /* read next one */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Accept current char if it is in 'set' (of size 2)
|
||||
*/
|
||||
static int test2 (RN *rn, const char *set) {
|
||||
if (rn->c == set[0] || rn->c == set[1])
|
||||
return nextc(rn);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a sequence of (hex)digits
|
||||
*/
|
||||
static int readdigits (RN *rn, int hex) {
|
||||
int count = 0;
|
||||
while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a number: first reads a valid prefix of a numeral into a buffer.
|
||||
** Then it calls 'lua_stringtonumber' to check whether the format is
|
||||
** correct and to convert it to a Lua number
|
||||
*/
|
||||
static int read_number (lua_State *L, FILE *f) {
|
||||
RN rn;
|
||||
int count = 0;
|
||||
int hex = 0;
|
||||
char decp[2];
|
||||
rn.f = f; rn.n = 0;
|
||||
decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */
|
||||
decp[1] = '.'; /* always accept a dot */
|
||||
l_lockfile(rn.f);
|
||||
do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */
|
||||
test2(&rn, "-+"); /* optional signal */
|
||||
if (test2(&rn, "00")) {
|
||||
if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */
|
||||
else count = 1; /* count initial '0' as a valid digit */
|
||||
}
|
||||
count += readdigits(&rn, hex); /* integral part */
|
||||
if (test2(&rn, decp)) /* decimal point? */
|
||||
count += readdigits(&rn, hex); /* fractional part */
|
||||
if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */
|
||||
test2(&rn, "-+"); /* exponent signal */
|
||||
readdigits(&rn, 0); /* exponent digits */
|
||||
}
|
||||
ungetc(rn.c, rn.f); /* unread look-ahead char */
|
||||
l_unlockfile(rn.f);
|
||||
rn.buff[rn.n] = '\0'; /* finish string */
|
||||
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
|
||||
return 1; /* ok */
|
||||
else { /* invalid format */
|
||||
lua_pushnil(L); /* "result" to be removed */
|
||||
return 0; /* read fails */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int test_eof (lua_State *L, FILE *f) {
|
||||
int c = getc(f);
|
||||
ungetc(c, f); /* no-op when c == EOF */
|
||||
lua_pushliteral(L, "");
|
||||
return (c != EOF);
|
||||
}
|
||||
|
||||
|
||||
static int read_line (lua_State *L, FILE *f, int chop) {
|
||||
luaL_Buffer b;
|
||||
int c = '\0';
|
||||
luaL_buffinit(L, &b);
|
||||
while (c != EOF && c != '\n') { /* repeat until end of line */
|
||||
char *buff = luaL_prepbuffer(&b); /* preallocate buffer */
|
||||
int i = 0;
|
||||
l_lockfile(f); /* no memory errors can happen inside the lock */
|
||||
while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n')
|
||||
buff[i++] = c;
|
||||
l_unlockfile(f);
|
||||
luaL_addsize(&b, i);
|
||||
}
|
||||
if (!chop && c == '\n') /* want a newline and have one? */
|
||||
luaL_addchar(&b, c); /* add ending newline to result */
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
/* return ok if read something (either a newline or something else) */
|
||||
return (c == '\n' || lua_rawlen(L, -1) > 0);
|
||||
}
|
||||
|
||||
|
||||
static void read_all (lua_State *L, FILE *f) {
|
||||
size_t nr;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
|
||||
char *p = luaL_prepbuffer(&b);
|
||||
nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f);
|
||||
luaL_addsize(&b, nr);
|
||||
} while (nr == LUAL_BUFFERSIZE);
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
}
|
||||
|
||||
|
||||
static int read_chars (lua_State *L, FILE *f, size_t n) {
|
||||
size_t nr; /* number of chars actually read */
|
||||
char *p;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
|
||||
nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
|
||||
luaL_addsize(&b, nr);
|
||||
luaL_pushresult(&b); /* close buffer */
|
||||
return (nr > 0); /* true iff read something */
|
||||
}
|
||||
|
||||
|
||||
static int g_read (lua_State *L, FILE *f, int first) {
|
||||
int nargs = lua_gettop(L) - 1;
|
||||
int success;
|
||||
int n;
|
||||
clearerr(f);
|
||||
if (nargs == 0) { /* no arguments? */
|
||||
success = read_line(L, f, 1);
|
||||
n = first+1; /* to return 1 result */
|
||||
}
|
||||
else { /* ensure stack space for all results and for auxlib's buffer */
|
||||
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
|
||||
success = 1;
|
||||
for (n = first; nargs-- && success; n++) {
|
||||
if (lua_type(L, n) == LUA_TNUMBER) {
|
||||
size_t l = (size_t)luaL_checkinteger(L, n);
|
||||
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
|
||||
}
|
||||
else {
|
||||
const char *p = luaL_checkstring(L, n);
|
||||
if (*p == '*') p++; /* skip optional '*' (for compatibility) */
|
||||
switch (*p) {
|
||||
case 'n': /* number */
|
||||
success = read_number(L, f);
|
||||
break;
|
||||
case 'l': /* line */
|
||||
success = read_line(L, f, 1);
|
||||
break;
|
||||
case 'L': /* line with end-of-line */
|
||||
success = read_line(L, f, 0);
|
||||
break;
|
||||
case 'a': /* file */
|
||||
read_all(L, f); /* read entire file */
|
||||
success = 1; /* always success */
|
||||
break;
|
||||
default:
|
||||
return luaL_argerror(L, n, "invalid format");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ferror(f))
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
if (!success) {
|
||||
lua_pop(L, 1); /* remove last result */
|
||||
lua_pushnil(L); /* push nil instead */
|
||||
}
|
||||
return n - first;
|
||||
}
|
||||
|
||||
|
||||
static int io_read (lua_State *L) {
|
||||
return g_read(L, getiofile(L, IO_INPUT), 1);
|
||||
}
|
||||
|
||||
|
||||
static int f_read (lua_State *L) {
|
||||
return g_read(L, tofile(L), 2);
|
||||
}
|
||||
|
||||
|
||||
static int io_readline (lua_State *L) {
|
||||
LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
|
||||
int i;
|
||||
int n = (int)lua_tointeger(L, lua_upvalueindex(2));
|
||||
if (isclosed(p)) /* file is already closed? */
|
||||
return luaL_error(L, "file is already closed");
|
||||
lua_settop(L , 1);
|
||||
luaL_checkstack(L, n, "too many arguments");
|
||||
for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
|
||||
lua_pushvalue(L, lua_upvalueindex(3 + i));
|
||||
n = g_read(L, p->f, 2); /* 'n' is number of results */
|
||||
lua_assert(n > 0); /* should return at least a nil */
|
||||
if (lua_toboolean(L, -n)) /* read at least one value? */
|
||||
return n; /* return them */
|
||||
else { /* first result is nil: EOF or error */
|
||||
if (n > 1) { /* is there error information? */
|
||||
/* 2nd result is error message */
|
||||
return luaL_error(L, "%s", lua_tostring(L, -n + 1));
|
||||
}
|
||||
if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
|
||||
lua_settop(L, 0);
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
aux_close(L); /* close it */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
int nargs = lua_gettop(L) - arg;
|
||||
int status = 1;
|
||||
for (; nargs--; arg++) {
|
||||
if (lua_type(L, arg) == LUA_TNUMBER) {
|
||||
/* optimization: could be done exactly as for strings */
|
||||
int len = lua_isinteger(L, arg)
|
||||
? fprintf(f, LUA_INTEGER_FMT,
|
||||
(LUAI_UACINT)lua_tointeger(L, arg))
|
||||
: fprintf(f, LUA_NUMBER_FMT,
|
||||
(LUAI_UACNUMBER)lua_tonumber(L, arg));
|
||||
status = status && (len > 0);
|
||||
}
|
||||
else {
|
||||
size_t l;
|
||||
const char *s = luaL_checklstring(L, arg, &l);
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
}
|
||||
}
|
||||
if (status) return 1; /* file handle already on stack top */
|
||||
else return luaL_fileresult(L, status, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int io_write (lua_State *L) {
|
||||
return g_write(L, getiofile(L, IO_OUTPUT), 1);
|
||||
}
|
||||
|
||||
|
||||
static int f_write (lua_State *L) {
|
||||
FILE *f = tofile(L);
|
||||
lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
|
||||
return g_write(L, f, 2);
|
||||
}
|
||||
|
||||
|
||||
static int f_seek (lua_State *L) {
|
||||
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
|
||||
static const char *const modenames[] = {"set", "cur", "end", NULL};
|
||||
FILE *f = tofile(L);
|
||||
int op = luaL_checkoption(L, 2, "cur", modenames);
|
||||
lua_Integer p3 = luaL_optinteger(L, 3, 0);
|
||||
l_seeknum offset = (l_seeknum)p3;
|
||||
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
|
||||
"not an integer in proper range");
|
||||
op = l_fseek(f, offset, mode[op]);
|
||||
if (op)
|
||||
return luaL_fileresult(L, 0, NULL); /* error */
|
||||
else {
|
||||
lua_pushinteger(L, (lua_Integer)l_ftell(f));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int f_setvbuf (lua_State *L) {
|
||||
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
|
||||
static const char *const modenames[] = {"no", "full", "line", NULL};
|
||||
FILE *f = tofile(L);
|
||||
int op = luaL_checkoption(L, 2, NULL, modenames);
|
||||
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
|
||||
int res = setvbuf(f, NULL, mode[op], (size_t)sz);
|
||||
return luaL_fileresult(L, res == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int io_flush (lua_State *L) {
|
||||
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int f_flush (lua_State *L) {
|
||||
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** functions for 'io' library
|
||||
*/
|
||||
static const luaL_Reg iolib[] = {
|
||||
{"close", io_close},
|
||||
{"flush", io_flush},
|
||||
{"input", io_input},
|
||||
{"lines", io_lines},
|
||||
{"open", io_open},
|
||||
{"output", io_output},
|
||||
{"popen", io_popen},
|
||||
{"read", io_read},
|
||||
{"tmpfile", io_tmpfile},
|
||||
{"type", io_type},
|
||||
{"write", io_write},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** methods for file handles
|
||||
*/
|
||||
static const luaL_Reg flib[] = {
|
||||
{"close", f_close},
|
||||
{"flush", f_flush},
|
||||
{"lines", f_lines},
|
||||
{"read", f_read},
|
||||
{"seek", f_seek},
|
||||
{"setvbuf", f_setvbuf},
|
||||
{"write", f_write},
|
||||
{"__gc", f_gc},
|
||||
{"__tostring", f_tostring},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
static void createmeta (lua_State *L) {
|
||||
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
|
||||
lua_pushvalue(L, -1); /* push metatable */
|
||||
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
|
||||
luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
|
||||
lua_pop(L, 1); /* pop new metatable */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** function to (not) close the standard files stdin, stdout, and stderr
|
||||
*/
|
||||
static int io_noclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
p->closef = &io_noclose; /* keep file opened */
|
||||
lua_pushnil(L);
|
||||
lua_pushliteral(L, "cannot close standard file");
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static void createstdfile (lua_State *L, FILE *f, const char *k,
|
||||
const char *fname) {
|
||||
LStream *p = newprefile(L);
|
||||
p->f = f;
|
||||
p->closef = &io_noclose;
|
||||
if (k != NULL) {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
|
||||
}
|
||||
lua_setfield(L, -2, fname); /* add file to module */
|
||||
}
|
||||
|
||||
|
||||
LUAMOD_API int luaopen_io (lua_State *L) {
|
||||
luaL_newlib(L, iolib); /* new module */
|
||||
createmeta(L);
|
||||
/* create (and set) default files */
|
||||
createstdfile(L, stdin, IO_INPUT, "stdin");
|
||||
createstdfile(L, stdout, IO_OUTPUT, "stdout");
|
||||
createstdfile(L, stderr, NULL, "stderr");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,568 @@
|
|||
/*
|
||||
** $Id: llex.c,v 2.96.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define llex_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lctype.h"
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lgc.h"
|
||||
#include "llex.h"
|
||||
#include "lobject.h"
|
||||
#include "lparser.h"
|
||||
#include "lstate.h"
|
||||
#include "lstring.h"
|
||||
#include "ltable.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
|
||||
#define next(ls) (ls->current = zgetc(ls->z))
|
||||
|
||||
|
||||
|
||||
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
|
||||
|
||||
|
||||
/* ORDER RESERVED */
|
||||
static const char *const luaX_tokens [] = {
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "goto", "if",
|
||||
"in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while",
|
||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||
"<<", ">>", "::", "<eof>",
|
||||
"<number>", "<integer>", "<name>", "<string>"
|
||||
};
|
||||
|
||||
|
||||
#define save_and_next(ls) (save(ls, ls->current), next(ls))
|
||||
|
||||
|
||||
static l_noret lexerror (LexState *ls, const char *msg, int token);
|
||||
|
||||
|
||||
static void save (LexState *ls, int c) {
|
||||
Mbuffer *b = ls->buff;
|
||||
if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) {
|
||||
size_t newsize;
|
||||
if (luaZ_sizebuffer(b) >= MAX_SIZE/2)
|
||||
lexerror(ls, "lexical element too long", 0);
|
||||
newsize = luaZ_sizebuffer(b) * 2;
|
||||
luaZ_resizebuffer(ls->L, b, newsize);
|
||||
}
|
||||
b->buffer[luaZ_bufflen(b)++] = cast(char, c);
|
||||
}
|
||||
|
||||
|
||||
void luaX_init (lua_State *L) {
|
||||
int i;
|
||||
TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */
|
||||
luaC_fix(L, obj2gco(e)); /* never collect this name */
|
||||
for (i=0; i<NUM_RESERVED; i++) {
|
||||
TString *ts = luaS_new(L, luaX_tokens[i]);
|
||||
luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */
|
||||
ts->extra = cast_byte(i+1); /* reserved word */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *luaX_token2str (LexState *ls, int token) {
|
||||
if (token < FIRST_RESERVED) { /* single-byte symbols? */
|
||||
lua_assert(token == cast_uchar(token));
|
||||
return luaO_pushfstring(ls->L, "'%c'", token);
|
||||
}
|
||||
else {
|
||||
const char *s = luaX_tokens[token - FIRST_RESERVED];
|
||||
if (token < TK_EOS) /* fixed format (symbols and reserved words)? */
|
||||
return luaO_pushfstring(ls->L, "'%s'", s);
|
||||
else /* names, strings, and numerals */
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *txtToken (LexState *ls, int token) {
|
||||
switch (token) {
|
||||
case TK_NAME: case TK_STRING:
|
||||
case TK_FLT: case TK_INT:
|
||||
save(ls, '\0');
|
||||
return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff));
|
||||
default:
|
||||
return luaX_token2str(ls, token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static l_noret lexerror (LexState *ls, const char *msg, int token) {
|
||||
msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber);
|
||||
if (token)
|
||||
luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token));
|
||||
luaD_throw(ls->L, LUA_ERRSYNTAX);
|
||||
}
|
||||
|
||||
|
||||
l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
|
||||
lexerror(ls, msg, ls->t.token);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** creates a new string and anchors it in scanner's table so that
|
||||
** it will not be collected until the end of the compilation
|
||||
** (by that time it should be anchored somewhere)
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
lua_State *L = ls->L;
|
||||
TValue *o; /* entry for 'str' */
|
||||
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
||||
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
|
||||
o = luaH_set(L, ls->h, L->top - 1);
|
||||
if (ttisnil(o)) { /* not in use yet? */
|
||||
/* boolean value does not need GC barrier;
|
||||
table has no metatable, so it does not need to invalidate cache */
|
||||
setbvalue(o, 1); /* t[string] = true */
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
else { /* string already present */
|
||||
ts = tsvalue(keyfromval(o)); /* re-use value previously stored */
|
||||
}
|
||||
L->top--; /* remove string from stack */
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** increment line number and skips newline sequence (any of
|
||||
** \n, \r, \n\r, or \r\n)
|
||||
*/
|
||||
static void inclinenumber (LexState *ls) {
|
||||
int old = ls->current;
|
||||
lua_assert(currIsNewline(ls));
|
||||
next(ls); /* skip '\n' or '\r' */
|
||||
if (currIsNewline(ls) && ls->current != old)
|
||||
next(ls); /* skip '\n\r' or '\r\n' */
|
||||
if (++ls->linenumber >= MAX_INT)
|
||||
lexerror(ls, "chunk has too many lines", 0);
|
||||
}
|
||||
|
||||
|
||||
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
|
||||
int firstchar) {
|
||||
ls->t.token = 0;
|
||||
ls->L = L;
|
||||
ls->current = firstchar;
|
||||
ls->lookahead.token = TK_EOS; /* no look-ahead token */
|
||||
ls->z = z;
|
||||
ls->fs = NULL;
|
||||
ls->linenumber = 1;
|
||||
ls->lastline = 1;
|
||||
ls->source = source;
|
||||
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */
|
||||
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** =======================================================
|
||||
** LEXICAL ANALYZER
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
static int check_next1 (LexState *ls, int c) {
|
||||
if (ls->current == c) {
|
||||
next(ls);
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether current char is in set 'set' (with two chars) and
|
||||
** saves it
|
||||
*/
|
||||
static int check_next2 (LexState *ls, const char *set) {
|
||||
lua_assert(set[2] == '\0');
|
||||
if (ls->current == set[0] || ls->current == set[1]) {
|
||||
save_and_next(ls);
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
/* LUA_NUMBER */
|
||||
/*
|
||||
** this function is quite liberal in what it accepts, as 'luaO_str2num'
|
||||
** will reject ill-formed numerals.
|
||||
*/
|
||||
static int read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
TValue obj;
|
||||
const char *expo = "Ee";
|
||||
int first = ls->current;
|
||||
lua_assert(lisdigit(ls->current));
|
||||
save_and_next(ls);
|
||||
if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
|
||||
expo = "Pp";
|
||||
for (;;) {
|
||||
if (check_next2(ls, expo)) /* exponent part? */
|
||||
check_next2(ls, "-+"); /* optional exponent sign */
|
||||
if (lisxdigit(ls->current))
|
||||
save_and_next(ls);
|
||||
else if (ls->current == '.')
|
||||
save_and_next(ls);
|
||||
else break;
|
||||
}
|
||||
save(ls, '\0');
|
||||
if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */
|
||||
lexerror(ls, "malformed number", TK_FLT);
|
||||
if (ttisinteger(&obj)) {
|
||||
seminfo->i = ivalue(&obj);
|
||||
return TK_INT;
|
||||
}
|
||||
else {
|
||||
lua_assert(ttisfloat(&obj));
|
||||
seminfo->r = fltvalue(&obj);
|
||||
return TK_FLT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
|
||||
** If sequence is well formed, return its number of '='s + 2; otherwise,
|
||||
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
|
||||
*/
|
||||
static size_t skip_sep (LexState *ls) {
|
||||
size_t count = 0;
|
||||
int s = ls->current;
|
||||
lua_assert(s == '[' || s == ']');
|
||||
save_and_next(ls);
|
||||
while (ls->current == '=') {
|
||||
save_and_next(ls);
|
||||
count++;
|
||||
}
|
||||
return (ls->current == s) ? count + 2
|
||||
: (count == 0) ? 1
|
||||
: 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
|
||||
int line = ls->linenumber; /* initial line (for error message) */
|
||||
save_and_next(ls); /* skip 2nd '[' */
|
||||
if (currIsNewline(ls)) /* string starts with a newline? */
|
||||
inclinenumber(ls); /* skip it */
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case EOZ: { /* error */
|
||||
const char *what = (seminfo ? "string" : "comment");
|
||||
const char *msg = luaO_pushfstring(ls->L,
|
||||
"unfinished long %s (starting at line %d)", what, line);
|
||||
lexerror(ls, msg, TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
}
|
||||
case ']': {
|
||||
if (skip_sep(ls) == sep) {
|
||||
save_and_next(ls); /* skip 2nd ']' */
|
||||
goto endloop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '\n': case '\r': {
|
||||
save(ls, '\n');
|
||||
inclinenumber(ls);
|
||||
if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (seminfo) save_and_next(ls);
|
||||
else next(ls);
|
||||
}
|
||||
}
|
||||
} endloop:
|
||||
if (seminfo)
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
|
||||
luaZ_bufflen(ls->buff) - 2 * sep);
|
||||
}
|
||||
|
||||
|
||||
static void esccheck (LexState *ls, int c, const char *msg) {
|
||||
if (!c) {
|
||||
if (ls->current != EOZ)
|
||||
save_and_next(ls); /* add current to buffer for error message */
|
||||
lexerror(ls, msg, TK_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int gethexa (LexState *ls) {
|
||||
save_and_next(ls);
|
||||
esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected");
|
||||
return luaO_hexavalue(ls->current);
|
||||
}
|
||||
|
||||
|
||||
static int readhexaesc (LexState *ls) {
|
||||
int r = gethexa(ls);
|
||||
r = (r << 4) + gethexa(ls);
|
||||
luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long readutf8esc (LexState *ls) {
|
||||
unsigned long r;
|
||||
int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
|
||||
save_and_next(ls); /* skip 'u' */
|
||||
esccheck(ls, ls->current == '{', "missing '{'");
|
||||
r = gethexa(ls); /* must have at least one digit */
|
||||
while ((save_and_next(ls), lisxdigit(ls->current))) {
|
||||
i++;
|
||||
r = (r << 4) + luaO_hexavalue(ls->current);
|
||||
esccheck(ls, r <= 0x10FFFF, "UTF-8 value too large");
|
||||
}
|
||||
esccheck(ls, ls->current == '}', "missing '}'");
|
||||
next(ls); /* skip '}' */
|
||||
luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void utf8esc (LexState *ls) {
|
||||
char buff[UTF8BUFFSZ];
|
||||
int n = luaO_utf8esc(buff, readutf8esc(ls));
|
||||
for (; n > 0; n--) /* add 'buff' to string */
|
||||
save(ls, buff[UTF8BUFFSZ - n]);
|
||||
}
|
||||
|
||||
|
||||
static int readdecesc (LexState *ls) {
|
||||
int i;
|
||||
int r = 0; /* result accumulator */
|
||||
for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */
|
||||
r = 10*r + ls->current - '0';
|
||||
save_and_next(ls);
|
||||
}
|
||||
esccheck(ls, r <= UCHAR_MAX, "decimal escape too large");
|
||||
luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
|
||||
save_and_next(ls); /* keep delimiter (for error messages) */
|
||||
while (ls->current != del) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
lexerror(ls, "unfinished string", TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
case '\n':
|
||||
case '\r':
|
||||
lexerror(ls, "unfinished string", TK_STRING);
|
||||
break; /* to avoid warnings */
|
||||
case '\\': { /* escape sequences */
|
||||
int c; /* final character to be saved */
|
||||
save_and_next(ls); /* keep '\\' for error messages */
|
||||
switch (ls->current) {
|
||||
case 'a': c = '\a'; goto read_save;
|
||||
case 'b': c = '\b'; goto read_save;
|
||||
case 'f': c = '\f'; goto read_save;
|
||||
case 'n': c = '\n'; goto read_save;
|
||||
case 'r': c = '\r'; goto read_save;
|
||||
case 't': c = '\t'; goto read_save;
|
||||
case 'v': c = '\v'; goto read_save;
|
||||
case 'x': c = readhexaesc(ls); goto read_save;
|
||||
case 'u': utf8esc(ls); goto no_save;
|
||||
case '\n': case '\r':
|
||||
inclinenumber(ls); c = '\n'; goto only_save;
|
||||
case '\\': case '\"': case '\'':
|
||||
c = ls->current; goto read_save;
|
||||
case EOZ: goto no_save; /* will raise an error next loop */
|
||||
case 'z': { /* zap following span of spaces */
|
||||
luaZ_buffremove(ls->buff, 1); /* remove '\\' */
|
||||
next(ls); /* skip the 'z' */
|
||||
while (lisspace(ls->current)) {
|
||||
if (currIsNewline(ls)) inclinenumber(ls);
|
||||
else next(ls);
|
||||
}
|
||||
goto no_save;
|
||||
}
|
||||
default: {
|
||||
esccheck(ls, lisdigit(ls->current), "invalid escape sequence");
|
||||
c = readdecesc(ls); /* digital escape '\ddd' */
|
||||
goto only_save;
|
||||
}
|
||||
}
|
||||
read_save:
|
||||
next(ls);
|
||||
/* go through */
|
||||
only_save:
|
||||
luaZ_buffremove(ls->buff, 1); /* remove '\\' */
|
||||
save(ls, c);
|
||||
/* go through */
|
||||
no_save: break;
|
||||
}
|
||||
default:
|
||||
save_and_next(ls);
|
||||
}
|
||||
}
|
||||
save_and_next(ls); /* skip delimiter */
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
|
||||
luaZ_bufflen(ls->buff) - 2);
|
||||
}
|
||||
|
||||
|
||||
static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
luaZ_resetbuffer(ls->buff);
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case '\n': case '\r': { /* line breaks */
|
||||
inclinenumber(ls);
|
||||
break;
|
||||
}
|
||||
case ' ': case '\f': case '\t': case '\v': { /* spaces */
|
||||
next(ls);
|
||||
break;
|
||||
}
|
||||
case '-': { /* '-' or '--' (comment) */
|
||||
next(ls);
|
||||
if (ls->current != '-') return '-';
|
||||
/* else is a comment */
|
||||
next(ls);
|
||||
if (ls->current == '[') { /* long comment? */
|
||||
size_t sep = skip_sep(ls);
|
||||
luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */
|
||||
if (sep >= 2) {
|
||||
read_long_string(ls, NULL, sep); /* skip long comment */
|
||||
luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else short comment */
|
||||
while (!currIsNewline(ls) && ls->current != EOZ)
|
||||
next(ls); /* skip until end of line (or end of file) */
|
||||
break;
|
||||
}
|
||||
case '[': { /* long string or simply '[' */
|
||||
size_t sep = skip_sep(ls);
|
||||
if (sep >= 2) {
|
||||
read_long_string(ls, seminfo, sep);
|
||||
return TK_STRING;
|
||||
}
|
||||
else if (sep == 0) /* '[=...' missing second bracket */
|
||||
lexerror(ls, "invalid long string delimiter", TK_STRING);
|
||||
return '[';
|
||||
}
|
||||
case '=': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_EQ;
|
||||
else return '=';
|
||||
}
|
||||
case '<': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_LE;
|
||||
else if (check_next1(ls, '<')) return TK_SHL;
|
||||
else return '<';
|
||||
}
|
||||
case '>': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_GE;
|
||||
else if (check_next1(ls, '>')) return TK_SHR;
|
||||
else return '>';
|
||||
}
|
||||
case '/': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '/')) return TK_IDIV;
|
||||
else return '/';
|
||||
}
|
||||
case '~': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_NE;
|
||||
else return '~';
|
||||
}
|
||||
case ':': {
|
||||
next(ls);
|
||||
if (check_next1(ls, ':')) return TK_DBCOLON;
|
||||
else return ':';
|
||||
}
|
||||
case '"': case '\'': { /* short literal strings */
|
||||
read_string(ls, ls->current, seminfo);
|
||||
return TK_STRING;
|
||||
}
|
||||
case '.': { /* '.', '..', '...', or number */
|
||||
save_and_next(ls);
|
||||
if (check_next1(ls, '.')) {
|
||||
if (check_next1(ls, '.'))
|
||||
return TK_DOTS; /* '...' */
|
||||
else return TK_CONCAT; /* '..' */
|
||||
}
|
||||
else if (!lisdigit(ls->current)) return '.';
|
||||
else return read_numeral(ls, seminfo);
|
||||
}
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
return read_numeral(ls, seminfo);
|
||||
}
|
||||
case EOZ: {
|
||||
return TK_EOS;
|
||||
}
|
||||
default: {
|
||||
if (lislalpha(ls->current)) { /* identifier or reserved word? */
|
||||
TString *ts;
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (lislalnum(ls->current));
|
||||
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
seminfo->ts = ts;
|
||||
if (isreserved(ts)) /* reserved word? */
|
||||
return ts->extra - 1 + FIRST_RESERVED;
|
||||
else {
|
||||
return TK_NAME;
|
||||
}
|
||||
}
|
||||
else { /* single-char tokens (+ - / ...) */
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void luaX_next (LexState *ls) {
|
||||
ls->lastline = ls->linenumber;
|
||||
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
|
||||
ls->t = ls->lookahead; /* use this one */
|
||||
ls->lookahead.token = TK_EOS; /* and discharge it */
|
||||
}
|
||||
else
|
||||
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
|
||||
}
|
||||
|
||||
|
||||
int luaX_lookahead (LexState *ls) {
|
||||
lua_assert(ls->lookahead.token == TK_EOS);
|
||||
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
|
||||
return ls->lookahead.token;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
** $Id: llex.h,v 1.79.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Lexical Analyzer
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef llex_h
|
||||
#define llex_h
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
#define FIRST_RESERVED 257
|
||||
|
||||
|
||||
#if !defined(LUA_ENV)
|
||||
#define LUA_ENV "_ENV"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* WARNING: if you change the order of this enumeration,
|
||||
* grep "ORDER RESERVED"
|
||||
*/
|
||||
enum RESERVED {
|
||||
/* terminal symbols denoted by reserved words */
|
||||
TK_AND = FIRST_RESERVED, TK_BREAK,
|
||||
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
|
||||
TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
|
||||
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
/* other terminal symbols */
|
||||
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
|
||||
TK_SHL, TK_SHR,
|
||||
TK_DBCOLON, TK_EOS,
|
||||
TK_FLT, TK_INT, TK_NAME, TK_STRING
|
||||
};
|
||||
|
||||
/* number of reserved words */
|
||||
#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))
|
||||
|
||||
|
||||
typedef union {
|
||||
lua_Number r;
|
||||
lua_Integer i;
|
||||
TString *ts;
|
||||
} SemInfo; /* semantics information */
|
||||
|
||||
|
||||
typedef struct Token {
|
||||
int token;
|
||||
SemInfo seminfo;
|
||||
} Token;
|
||||
|
||||
|
||||
/* state of the lexer plus state of the parser when shared by all
|
||||
functions */
|
||||
typedef struct LexState {
|
||||
int current; /* current character (charint) */
|
||||
int linenumber; /* input line counter */
|
||||
int lastline; /* line of last token 'consumed' */
|
||||
Token t; /* current token */
|
||||
Token lookahead; /* look ahead token */
|
||||
struct FuncState *fs; /* current function (parser) */
|
||||
struct lua_State *L;
|
||||
ZIO *z; /* input stream */
|
||||
Mbuffer *buff; /* buffer for tokens */
|
||||
Table *h; /* to avoid collection/reuse strings */
|
||||
struct Dyndata *dyd; /* dynamic structures used by the parser */
|
||||
TString *source; /* current source name */
|
||||
TString *envn; /* environment variable name */
|
||||
} LexState;
|
||||
|
||||
|
||||
LUAI_FUNC void luaX_init (lua_State *L);
|
||||
LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
|
||||
TString *source, int firstchar);
|
||||
LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
|
||||
LUAI_FUNC void luaX_next (LexState *ls);
|
||||
LUAI_FUNC int luaX_lookahead (LexState *ls);
|
||||
LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s);
|
||||
LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
** $Id: llimits.h,v 1.141.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Limits, basic types, and some other 'installation-dependent' definitions
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef llimits_h
|
||||
#define llimits_h
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
/*
|
||||
** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count
|
||||
** the total memory used by Lua (in bytes). Usually, 'size_t' and
|
||||
** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
|
||||
*/
|
||||
#if defined(LUAI_MEM) /* { external definitions? */
|
||||
typedef LUAI_UMEM lu_mem;
|
||||
typedef LUAI_MEM l_mem;
|
||||
#elif LUAI_BITSINT >= 32 /* }{ */
|
||||
typedef size_t lu_mem;
|
||||
typedef ptrdiff_t l_mem;
|
||||
#else /* 16-bit ints */ /* }{ */
|
||||
typedef unsigned long lu_mem;
|
||||
typedef long l_mem;
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/* chars used as small naturals (so that 'char' is reserved for characters) */
|
||||
typedef unsigned char lu_byte;
|
||||
|
||||
|
||||
/* maximum value for size_t */
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0))
|
||||
|
||||
/* maximum size visible for Lua (must be representable in a lua_Integer */
|
||||
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
|
||||
: (size_t)(LUA_MAXINTEGER))
|
||||
|
||||
|
||||
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0))
|
||||
|
||||
#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1))
|
||||
|
||||
|
||||
#define MAX_INT INT_MAX /* maximum value of an int */
|
||||
|
||||
|
||||
/*
|
||||
** conversion of pointer to unsigned integer:
|
||||
** this is for hashing only; there is no problem if the integer
|
||||
** cannot hold the whole pointer value
|
||||
*/
|
||||
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX))
|
||||
|
||||
|
||||
|
||||
/* type to ensure maximum alignment */
|
||||
#if defined(LUAI_USER_ALIGNMENT_T)
|
||||
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
|
||||
#else
|
||||
typedef union {
|
||||
lua_Number n;
|
||||
double u;
|
||||
void *s;
|
||||
lua_Integer i;
|
||||
long l;
|
||||
} L_Umaxalign;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* types of 'usual argument conversions' for lua_Number and lua_Integer */
|
||||
typedef LUAI_UACNUMBER l_uacNumber;
|
||||
typedef LUAI_UACINT l_uacInt;
|
||||
|
||||
|
||||
/* internal assertions for in-house debugging */
|
||||
#if defined(lua_assert)
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
/* to avoid problems with conditions too long */
|
||||
#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0))
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#define check_exp(c,e) (e)
|
||||
#define lua_longassert(c) ((void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** assertion for checking API calls
|
||||
*/
|
||||
#if !defined(luai_apicheck)
|
||||
#define luai_apicheck(l,e) lua_assert(e)
|
||||
#endif
|
||||
|
||||
#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
|
||||
|
||||
|
||||
/* macro to avoid warnings about unused variables */
|
||||
#if !defined(UNUSED)
|
||||
#define UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
|
||||
/* type casts (a macro highlights casts in the code) */
|
||||
#define cast(t, exp) ((t)(exp))
|
||||
|
||||
#define cast_void(i) cast(void, (i))
|
||||
#define cast_byte(i) cast(lu_byte, (i))
|
||||
#define cast_num(i) cast(lua_Number, (i))
|
||||
#define cast_int(i) cast(int, (i))
|
||||
#define cast_uchar(i) cast(unsigned char, (i))
|
||||
|
||||
|
||||
/* cast a signed lua_Integer to lua_Unsigned */
|
||||
#if !defined(l_castS2U)
|
||||
#define l_castS2U(i) ((lua_Unsigned)(i))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** cast a lua_Unsigned to a signed lua_Integer; this cast is
|
||||
** not strict ISO C, but two-complement architectures should
|
||||
** work fine.
|
||||
*/
|
||||
#if !defined(l_castU2S)
|
||||
#define l_castU2S(i) ((lua_Integer)(i))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** non-return type
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#define l_noret void __attribute__((noreturn))
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1200
|
||||
#define l_noret void __declspec(noreturn)
|
||||
#else
|
||||
#define l_noret void
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** maximum depth for nested C calls and syntactical nested non-terminals
|
||||
** in a program. (Value must fit in an unsigned short int.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXCCALLS)
|
||||
#define LUAI_MAXCCALLS 200
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** type for virtual-machine instructions;
|
||||
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
|
||||
*/
|
||||
#if LUAI_BITSINT >= 32
|
||||
typedef unsigned int Instruction;
|
||||
#else
|
||||
typedef unsigned long Instruction;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Maximum length for short strings, that is, strings that are
|
||||
** internalized. (Cannot be smaller than reserved words or tags for
|
||||
** metamethods, as these strings must be internalized;
|
||||
** #("function") = 8, #("__newindex") = 10.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXSHORTLEN)
|
||||
#define LUAI_MAXSHORTLEN 40
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Initial size for the string table (must be power of 2).
|
||||
** The Lua core alone registers ~50 strings (reserved words +
|
||||
** metaevent keys + a few others). Libraries would typically add
|
||||
** a few dozens more.
|
||||
*/
|
||||
#if !defined(MINSTRTABSIZE)
|
||||
#define MINSTRTABSIZE 128
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Size of cache for strings in the API. 'N' is the number of
|
||||
** sets (better be a prime) and "M" is the size of each set (M == 1
|
||||
** makes a direct cache.)
|
||||
*/
|
||||
#if !defined(STRCACHE_N)
|
||||
#define STRCACHE_N 53
|
||||
#define STRCACHE_M 2
|
||||
#endif
|
||||
|
||||
|
||||
/* minimum size for string buffer */
|
||||
#if !defined(LUA_MINBUFFER)
|
||||
#define LUA_MINBUFFER 32
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** macros that are executed whenever program enters the Lua core
|
||||
** ('lua_lock') and leaves the core ('lua_unlock')
|
||||
*/
|
||||
#if !defined(lua_lock)
|
||||
#define lua_lock(L) ((void) 0)
|
||||
#define lua_unlock(L) ((void) 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** macro executed during Lua functions at points where the
|
||||
** function can yield.
|
||||
*/
|
||||
#if !defined(luai_threadyield)
|
||||
#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** these macros allow user-specific actions on threads when you defined
|
||||
** LUAI_EXTRASPACE and need to do something extra when a thread is
|
||||
** created/deleted/resumed/yielded.
|
||||
*/
|
||||
#if !defined(luai_userstateopen)
|
||||
#define luai_userstateopen(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateclose)
|
||||
#define luai_userstateclose(L) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatethread)
|
||||
#define luai_userstatethread(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstatefree)
|
||||
#define luai_userstatefree(L,L1) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateresume)
|
||||
#define luai_userstateresume(L,n) ((void)L)
|
||||
#endif
|
||||
|
||||
#if !defined(luai_userstateyield)
|
||||
#define luai_userstateyield(L,n) ((void)L)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The luai_num* macros define the primitive operations over numbers.
|
||||
*/
|
||||
|
||||
/* floor division (defined as 'floor(a/b)') */
|
||||
#if !defined(luai_numidiv)
|
||||
#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b)))
|
||||
#endif
|
||||
|
||||
/* float division */
|
||||
#if !defined(luai_numdiv)
|
||||
#define luai_numdiv(L,a,b) ((a)/(b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** modulo: defined as 'a - floor(a/b)*b'; this definition gives NaN when
|
||||
** 'b' is huge, but the result should be 'a'. 'fmod' gives the result of
|
||||
** 'a - trunc(a/b)*b', and therefore must be corrected when 'trunc(a/b)
|
||||
** ~= floor(a/b)'. That happens when the division has a non-integer
|
||||
** negative result, which is equivalent to the test below.
|
||||
*/
|
||||
#if !defined(luai_nummod)
|
||||
#define luai_nummod(L,a,b,m) \
|
||||
{ (m) = l_mathop(fmod)(a,b); if ((m)*(b) < 0) (m) += (b); }
|
||||
#endif
|
||||
|
||||
/* exponentiation */
|
||||
#if !defined(luai_numpow)
|
||||
#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b))
|
||||
#endif
|
||||
|
||||
/* the others are quite standard operations */
|
||||
#if !defined(luai_numadd)
|
||||
#define luai_numadd(L,a,b) ((a)+(b))
|
||||
#define luai_numsub(L,a,b) ((a)-(b))
|
||||
#define luai_nummul(L,a,b) ((a)*(b))
|
||||
#define luai_numunm(L,a) (-(a))
|
||||
#define luai_numeq(a,b) ((a)==(b))
|
||||
#define luai_numlt(a,b) ((a)<(b))
|
||||
#define luai_numle(a,b) ((a)<=(b))
|
||||
#define luai_numisnan(a) (!luai_numeq((a), (a)))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** macro to control inclusion of some hard tests on stack reallocation
|
||||
*/
|
||||
#if !defined(HARDSTACKTESTS)
|
||||
#define condmovestack(L,pre,pos) ((void)0)
|
||||
#else
|
||||
/* realloc stack keeping its size */
|
||||
#define condmovestack(L,pre,pos) \
|
||||
{ int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_); pos; }
|
||||
#endif
|
||||
|
||||
#if !defined(HARDMEMTESTS)
|
||||
#define condchangemem(L,pre,pos) ((void)0)
|
||||
#else
|
||||
#define condchangemem(L,pre,pos) \
|
||||
{ if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } }
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
** $Id: lmathlib.c,v 1.119.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Standard mathematical library
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lmathlib_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
#undef PI
|
||||
#define PI (l_mathop(3.141592653589793238462643383279502884))
|
||||
|
||||
|
||||
#if !defined(l_rand) /* { */
|
||||
#if defined(LUA_USE_POSIX)
|
||||
#define l_rand() random()
|
||||
#define l_srand(x) srandom(x)
|
||||
#define L_RANDMAX 2147483647 /* (2^31 - 1), following POSIX */
|
||||
#else
|
||||
#define l_rand() rand()
|
||||
#define l_srand(x) srand(x)
|
||||
#define L_RANDMAX RAND_MAX
|
||||
#endif
|
||||
#endif /* } */
|
||||
|
||||
|
||||
static int math_abs (lua_State *L) {
|
||||
if (lua_isinteger(L, 1)) {
|
||||
lua_Integer n = lua_tointeger(L, 1);
|
||||
if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n);
|
||||
lua_pushinteger(L, n);
|
||||
}
|
||||
else
|
||||
lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_sin (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_cos (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_tan (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_asin (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_acos (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_atan (lua_State *L) {
|
||||
lua_Number y = luaL_checknumber(L, 1);
|
||||
lua_Number x = luaL_optnumber(L, 2, 1);
|
||||
lua_pushnumber(L, l_mathop(atan2)(y, x));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_toint (lua_State *L) {
|
||||
int valid;
|
||||
lua_Integer n = lua_tointegerx(L, 1, &valid);
|
||||
if (valid)
|
||||
lua_pushinteger(L, n);
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushnil(L); /* value is not convertible to integer */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void pushnumint (lua_State *L, lua_Number d) {
|
||||
lua_Integer n;
|
||||
if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */
|
||||
lua_pushinteger(L, n); /* result is integer */
|
||||
else
|
||||
lua_pushnumber(L, d); /* result is float */
|
||||
}
|
||||
|
||||
|
||||
static int math_floor (lua_State *L) {
|
||||
if (lua_isinteger(L, 1))
|
||||
lua_settop(L, 1); /* integer is its own floor */
|
||||
else {
|
||||
lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1));
|
||||
pushnumint(L, d);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_ceil (lua_State *L) {
|
||||
if (lua_isinteger(L, 1))
|
||||
lua_settop(L, 1); /* integer is its own ceil */
|
||||
else {
|
||||
lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1));
|
||||
pushnumint(L, d);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_fmod (lua_State *L) {
|
||||
if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) {
|
||||
lua_Integer d = lua_tointeger(L, 2);
|
||||
if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */
|
||||
luaL_argcheck(L, d != 0, 2, "zero");
|
||||
lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */
|
||||
}
|
||||
else
|
||||
lua_pushinteger(L, lua_tointeger(L, 1) % d);
|
||||
}
|
||||
else
|
||||
lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1),
|
||||
luaL_checknumber(L, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** next function does not use 'modf', avoiding problems with 'double*'
|
||||
** (which is not compatible with 'float*') when lua_Number is not
|
||||
** 'double'.
|
||||
*/
|
||||
static int math_modf (lua_State *L) {
|
||||
if (lua_isinteger(L ,1)) {
|
||||
lua_settop(L, 1); /* number is its own integer part */
|
||||
lua_pushnumber(L, 0); /* no fractional part */
|
||||
}
|
||||
else {
|
||||
lua_Number n = luaL_checknumber(L, 1);
|
||||
/* integer part (rounds toward zero) */
|
||||
lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n);
|
||||
pushnumint(L, ip);
|
||||
/* fractional part (test needed for inf/-inf) */
|
||||
lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip));
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int math_sqrt (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_ult (lua_State *L) {
|
||||
lua_Integer a = luaL_checkinteger(L, 1);
|
||||
lua_Integer b = luaL_checkinteger(L, 2);
|
||||
lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_log (lua_State *L) {
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
lua_Number res;
|
||||
if (lua_isnoneornil(L, 2))
|
||||
res = l_mathop(log)(x);
|
||||
else {
|
||||
lua_Number base = luaL_checknumber(L, 2);
|
||||
#if !defined(LUA_USE_C89)
|
||||
if (base == l_mathop(2.0))
|
||||
res = l_mathop(log2)(x); else
|
||||
#endif
|
||||
if (base == l_mathop(10.0))
|
||||
res = l_mathop(log10)(x);
|
||||
else
|
||||
res = l_mathop(log)(x)/l_mathop(log)(base);
|
||||
}
|
||||
lua_pushnumber(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_exp (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_deg (lua_State *L) {
|
||||
lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_rad (lua_State *L) {
|
||||
lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_min (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int imin = 1; /* index of current minimum value */
|
||||
int i;
|
||||
luaL_argcheck(L, n >= 1, 1, "value expected");
|
||||
for (i = 2; i <= n; i++) {
|
||||
if (lua_compare(L, i, imin, LUA_OPLT))
|
||||
imin = i;
|
||||
}
|
||||
lua_pushvalue(L, imin);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_max (lua_State *L) {
|
||||
int n = lua_gettop(L); /* number of arguments */
|
||||
int imax = 1; /* index of current maximum value */
|
||||
int i;
|
||||
luaL_argcheck(L, n >= 1, 1, "value expected");
|
||||
for (i = 2; i <= n; i++) {
|
||||
if (lua_compare(L, imax, i, LUA_OPLT))
|
||||
imax = i;
|
||||
}
|
||||
lua_pushvalue(L, imax);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function uses 'double' (instead of 'lua_Number') to ensure that
|
||||
** all bits from 'l_rand' can be represented, and that 'RANDMAX + 1.0'
|
||||
** will keep full precision (ensuring that 'r' is always less than 1.0.)
|
||||
*/
|
||||
static int math_random (lua_State *L) {
|
||||
lua_Integer low, up;
|
||||
double r = (double)l_rand() * (1.0 / ((double)L_RANDMAX + 1.0));
|
||||
switch (lua_gettop(L)) { /* check number of arguments */
|
||||
case 0: { /* no arguments */
|
||||
lua_pushnumber(L, (lua_Number)r); /* Number between 0 and 1 */
|
||||
return 1;
|
||||
}
|
||||
case 1: { /* only upper limit */
|
||||
low = 1;
|
||||
up = luaL_checkinteger(L, 1);
|
||||
break;
|
||||
}
|
||||
case 2: { /* lower and upper limits */
|
||||
low = luaL_checkinteger(L, 1);
|
||||
up = luaL_checkinteger(L, 2);
|
||||
break;
|
||||
}
|
||||
default: return luaL_error(L, "wrong number of arguments");
|
||||
}
|
||||
/* random integer in the interval [low, up] */
|
||||
luaL_argcheck(L, low <= up, 1, "interval is empty");
|
||||
luaL_argcheck(L, low >= 0 || up <= LUA_MAXINTEGER + low, 1,
|
||||
"interval too large");
|
||||
r *= (double)(up - low) + 1.0;
|
||||
lua_pushinteger(L, (lua_Integer)r + low);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int math_randomseed (lua_State *L) {
|
||||
l_srand((unsigned int)(lua_Integer)luaL_checknumber(L, 1));
|
||||
(void)l_rand(); /* discard first value to avoid undesirable correlations */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int math_type (lua_State *L) {
|
||||
if (lua_type(L, 1) == LUA_TNUMBER) {
|
||||
if (lua_isinteger(L, 1))
|
||||
lua_pushliteral(L, "integer");
|
||||
else
|
||||
lua_pushliteral(L, "float");
|
||||
}
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushnil(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Deprecated functions (for compatibility only)
|
||||
** ===================================================================
|
||||
*/
|
||||
#if defined(LUA_COMPAT_MATHLIB)
|
||||
|
||||
static int math_cosh (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_sinh (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_tanh (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_pow (lua_State *L) {
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
lua_Number y = luaL_checknumber(L, 2);
|
||||
lua_pushnumber(L, l_mathop(pow)(x, y));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_frexp (lua_State *L) {
|
||||
int e;
|
||||
lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e));
|
||||
lua_pushinteger(L, e);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int math_ldexp (lua_State *L) {
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
int ep = (int)luaL_checkinteger(L, 2);
|
||||
lua_pushnumber(L, l_mathop(ldexp)(x, ep));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int math_log10 (lua_State *L) {
|
||||
lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
|
||||
static const luaL_Reg mathlib[] = {
|
||||
{"abs", math_abs},
|
||||
{"acos", math_acos},
|
||||
{"asin", math_asin},
|
||||
{"atan", math_atan},
|
||||
{"ceil", math_ceil},
|
||||
{"cos", math_cos},
|
||||
{"deg", math_deg},
|
||||
{"exp", math_exp},
|
||||
{"tointeger", math_toint},
|
||||
{"floor", math_floor},
|
||||
{"fmod", math_fmod},
|
||||
{"ult", math_ult},
|
||||
{"log", math_log},
|
||||
{"max", math_max},
|
||||
{"min", math_min},
|
||||
{"modf", math_modf},
|
||||
{"rad", math_rad},
|
||||
{"random", math_random},
|
||||
{"randomseed", math_randomseed},
|
||||
{"sin", math_sin},
|
||||
{"sqrt", math_sqrt},
|
||||
{"tan", math_tan},
|
||||
{"type", math_type},
|
||||
#if defined(LUA_COMPAT_MATHLIB)
|
||||
{"atan2", math_atan},
|
||||
{"cosh", math_cosh},
|
||||
{"sinh", math_sinh},
|
||||
{"tanh", math_tanh},
|
||||
{"pow", math_pow},
|
||||
{"frexp", math_frexp},
|
||||
{"ldexp", math_ldexp},
|
||||
{"log10", math_log10},
|
||||
#endif
|
||||
/* placeholders */
|
||||
{"pi", NULL},
|
||||
{"huge", NULL},
|
||||
{"maxinteger", NULL},
|
||||
{"mininteger", NULL},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Open math library
|
||||
*/
|
||||
LUAMOD_API int luaopen_math (lua_State *L) {
|
||||
luaL_newlib(L, mathlib);
|
||||
lua_pushnumber(L, PI);
|
||||
lua_setfield(L, -2, "pi");
|
||||
lua_pushnumber(L, (lua_Number)HUGE_VAL);
|
||||
lua_setfield(L, -2, "huge");
|
||||
lua_pushinteger(L, LUA_MAXINTEGER);
|
||||
lua_setfield(L, -2, "maxinteger");
|
||||
lua_pushinteger(L, LUA_MININTEGER);
|
||||
lua_setfield(L, -2, "mininteger");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
** $Id: lmem.c,v 1.91.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Interface to Memory Manager
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lmem_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "ldebug.h"
|
||||
#include "ldo.h"
|
||||
#include "lgc.h"
|
||||
#include "lmem.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About the realloc function:
|
||||
** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
** ('osize' is the old size, 'nsize' is the new size)
|
||||
**
|
||||
** * frealloc(ud, NULL, x, s) creates a new block of size 's' (no
|
||||
** matter 'x').
|
||||
**
|
||||
** * frealloc(ud, p, x, 0) frees the block 'p'
|
||||
** (in this specific case, frealloc must return NULL);
|
||||
** particularly, frealloc(ud, NULL, 0, 0) does nothing
|
||||
** (which is equivalent to free(NULL) in ISO C)
|
||||
**
|
||||
** frealloc returns NULL if it cannot create or reallocate the area
|
||||
** (any reallocation to an equal or smaller size cannot fail!)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define MINSIZEARRAY 4
|
||||
|
||||
|
||||
void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
|
||||
int limit, const char *what) {
|
||||
void *newblock;
|
||||
int newsize;
|
||||
if (*size >= limit/2) { /* cannot double it? */
|
||||
if (*size >= limit) /* cannot grow even a little? */
|
||||
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
|
||||
newsize = limit; /* still have at least one free place */
|
||||
}
|
||||
else {
|
||||
newsize = (*size)*2;
|
||||
if (newsize < MINSIZEARRAY)
|
||||
newsize = MINSIZEARRAY; /* minimum size */
|
||||
}
|
||||
newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
|
||||
*size = newsize; /* update only when everything else is OK */
|
||||
return newblock;
|
||||
}
|
||||
|
||||
|
||||
l_noret luaM_toobig (lua_State *L) {
|
||||
luaG_runerror(L, "memory allocation error: block too big");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** generic allocation routine.
|
||||
*/
|
||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
void *newblock;
|
||||
global_State *g = G(L);
|
||||
size_t realosize = (block) ? osize : 0;
|
||||
lua_assert((realosize == 0) == (block == NULL));
|
||||
#if defined(HARDMEMTESTS)
|
||||
if (nsize > realosize && g->gcrunning)
|
||||
luaC_fullgc(L, 1); /* force a GC whenever possible */
|
||||
#endif
|
||||
newblock = (*g->frealloc)(g->ud, block, osize, nsize);
|
||||
if (newblock == NULL && nsize > 0) {
|
||||
lua_assert(nsize > realosize); /* cannot fail when shrinking a block */
|
||||
if (g->version) { /* is state fully built? */
|
||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||
newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||
}
|
||||
if (newblock == NULL)
|
||||
luaD_throw(L, LUA_ERRMEM);
|
||||
}
|
||||
lua_assert((nsize == 0) == (newblock == NULL));
|
||||
g->GCdebt = (g->GCdebt + nsize) - realosize;
|
||||
return newblock;
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
** $Id: lmem.h,v 1.43.1.1 2017/04/19 17:20:42 roberto Exp $
|
||||
** Interface to Memory Manager
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#ifndef lmem_h
|
||||
#define lmem_h
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/*
|
||||
** This macro reallocs a vector 'b' from 'on' to 'n' elements, where
|
||||
** each element has size 'e'. In case of arithmetic overflow of the
|
||||
** product 'n'*'e', it raises an error (calling 'luaM_toobig'). Because
|
||||
** 'e' is always constant, it avoids the runtime division MAX_SIZET/(e).
|
||||
**
|
||||
** (The macro is somewhat complex to avoid warnings: The 'sizeof'
|
||||
** comparison avoids a runtime comparison when overflow cannot occur.
|
||||
** The compiler should be able to optimize the real test by itself, but
|
||||
** when it does it, it may give a warning about "comparison is always
|
||||
** false due to limited range of data type"; the +1 tricks the compiler,
|
||||
** avoiding this warning but also this optimization.)
|
||||
*/
|
||||
#define luaM_reallocv(L,b,on,n,e) \
|
||||
(((sizeof(n) >= sizeof(size_t) && cast(size_t, (n)) + 1 > MAX_SIZET/(e)) \
|
||||
? luaM_toobig(L) : cast_void(0)) , \
|
||||
luaM_realloc_(L, (b), (on)*(e), (n)*(e)))
|
||||
|
||||
/*
|
||||
** Arrays of chars do not need any test
|
||||
*/
|
||||
#define luaM_reallocvchar(L,b,on,n) \
|
||||
cast(char *, luaM_realloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char)))
|
||||
|
||||
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
|
||||
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
|
||||
#define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0)
|
||||
|
||||
#define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s))
|
||||
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
|
||||
#define luaM_newvector(L,n,t) \
|
||||
cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
|
||||
|
||||
#define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s))
|
||||
|
||||
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
|
||||
if ((nelems)+1 > (size)) \
|
||||
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
|
||||
|
||||
#define luaM_reallocvector(L, v,oldn,n,t) \
|
||||
((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
|
||||
|
||||
LUAI_FUNC l_noret luaM_toobig (lua_State *L);
|
||||
|
||||
/* not to be called directly */
|
||||
LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
|
||||
size_t size);
|
||||
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
|
||||
size_t size_elem, int limit,
|
||||
const char *what);
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue