1
0
Files
otyaSMILEBASIC/SMILEBASIC/petitcomputer.d

1885 lines
63 KiB
D

module otya.smilebasic.petitcomputer;
import derelict.sdl2.sdl;
import derelict.sdl2.image;
import derelict.opengl3.gl;
import std.net.curl;
import std.file;
import std.stdio;
import std.conv;
import std.string;
import std.range;
static import std.algorithm;
import core.stdc.stdio;
import core.sync.mutex;
import core.sync.condition;
import core.thread;
static import core.stdc.string;
import otya.smilebasic.sprite;
import otya.smilebasic.error;
import otya.smilebasic.bg;
import otya.smilebasic.parser;
import otya.smilebasic.project;
import otya.smilebasic.console;
import otya.smilebasic.graphic;
import otya.smilebasic.dialog;
import otya.smilebasic.program;
import otya.smilebasic.random;
import otya.smilebasic.fade;
static import otya.smilebasic.token;
const static rot_test_deg = 45f;
const static rot_test_x = 0f;
const static rot_test_y = 1f;
const static rot_test_z = 1f;
enum Button
{
NONE = 0,
UP = 1,
DOWN = 2,
LEFT = 4,
RIGHT = 8,
A = 16,
B = 32,
X = 64,
Y = 128,
L = 256,
R = 512,
UNUSED = 1024,
ZR = 2048,
ZL = 4096,
START = 8192,
}
class GraphicPage
{
static GLenum textureScaleMode = GL_NEAREST;
SDL_Surface* surface;
this(string filename)
{
SDL_RWops* stream = SDL_RWFromFile(toStringz(filename), toStringz("rb"));
auto src = IMG_Load_RW(stream, 0);
scope(exit)
{
SDL_RWclose(stream);
}
this(src);
}
this(SDL_Surface* s)
{
surface = s;
GLenum texture_format;
GLint nOfColors;
nOfColors = surface.format.BytesPerPixel;
if (nOfColors == 4) // contains an alpha channel
{
if (surface.format.Rmask == 0x000000ff)
texture_format = GL_RGBA;
else
texture_format = GL_BGRA;
} else if (nOfColors == 3) // no alpha channel
{
if (surface.format.Rmask == 0x000000ff)
texture_format = GL_RGB;
else
texture_format = GL_BGR;
} else {
//printf("warning: the image is not truecolor.. this will probably break\n");
// this error should not go unhandled
}
textureFormat = texture_format;
}
GLuint glTexture;
GLenum textureFormat;
void createTexture(SDL_Renderer* renderer)
{
createTexture(renderer, GraphicPage.textureScaleMode);
}
void createTexture(SDL_Renderer* renderer, GLenum textureScaleMode)
{
ubyte r,g,b,a;
SDL_GetRGBA(*cast(uint*)surface.pixels, surface.format, &r, &g, &b, &a);
GLenum texture_format;
GLint nOfColors;
nOfColors = surface.format.BytesPerPixel;
if (nOfColors == 4) // contains an alpha channel
{
if (surface.format.Rmask == 0x000000ff)
texture_format = GL_RGBA;
else
texture_format = GL_BGRA;
} else if (nOfColors == 3) // no alpha channel
{
if (surface.format.Rmask == 0x000000ff)
texture_format = GL_RGB;
else
texture_format = GL_BGR;
} else {
//printf("warning: the image is not truecolor.. this will probably break\n");
// this error should not go unhandled
}
// Have OpenGL generate a texture object handle for us
glGenTextures( 1, &glTexture );
// Bind the texture object
glBindTexture( GL_TEXTURE_2D, glTexture );
// Set the texture's stretching properties
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, textureScaleMode );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, textureScaleMode );
// Edit the texture object's image data using the information SDL_Surface gives us
glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface.w, surface.h, 0,
texture_format, GL_UNSIGNED_BYTE, surface.pixels );
textureFormat = texture_format;
}
GLuint render;
GLuint buffer;
void createBuffer()
{
GLint old;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old);
glBindTexture(GL_TEXTURE_2D, glTexture);
glGenRenderbuffersEXT(1, &render);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render);
//glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, surface.w, surface.h);
glGenFramebuffersEXT(1, &buffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, buffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, glTexture, 0);
//glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
writeln("!?");
}
glBindFramebufferEXT(GL_FRAMEBUFFER, old);
}
void deleteGL()
{
if (buffer)
glDeleteFramebuffersEXT(1, &buffer);
if (render)
glDeleteRenderbuffersEXT(1, &render);
glDeleteTextures(1, &glTexture);
buffer = render = glTexture = 0;
}
void deleteSDL()
{
SDL_FreeSurface(surface);
surface = null;
}
}
version(Windows)
{
extern (Windows) static void* LoadLibraryA(in char*);
extern (Windows) static bool FreeLibrary(void*);
extern (Windows) static void* GetProcAddress(void*, in char*);
alias extern (Windows) void* function(void*, void*) ImmAssociateContext;
alias extern (Windows) int function(int) ImmDisableIME;
}
import std.container;
struct Size
{
int width, height;
}
struct Display
{
SDL_Rect[] rect;
Size windowSize;
int yoffset;
int count()
{
return cast(int)rect.length;
}
}
class RingBuffer(T)
{
T[] buffer;
this(size_t size)
{
start = -1;
end = -1;
buffer = new T[size];
}
sizediff_t end;
sizediff_t start;
void put(T val)
{
if (end < start)
{
start++;
if (start == buffer.length)
{
start = 0;
}
}
end++;
if (end == buffer.length)
{
end = 0;
start = 1;
}
buffer[end] = val;
return;
}
size_t length()
{
return start == -1 ? end - start : buffer.length;
}
T opIndex(size_t index)
{
return buffer[(start != -1 ? start + index : index) % buffer.length];
}
}
enum Hardware
{
threeDS = 0,
new3DS = 1,
wiiU = 2,
}
class PetitComputer
{
this()
{
//new Test();
}
static const string resourceDirName = "resources";
static const string resourcePath = "./resources";
static const string fontFile = resourcePath ~ "/font.png";
static const string spriteFile = resourcePath ~ "/defsp.png";
static const string BGFile = resourcePath ~ "/defbg.png";
static const string fontTableFile = "./fonttable.txt";
int screenWidth;
int screenHeight;
int screenWidthDisplay1;
int screenHeightDisplay1;
uint toGLColor(uint color)
{
//ARGB -> ABGR
return (color & 0xFF00FF00) | (color >> 16 & 0xFF) | ((color & 0xFF) << 16);
}
uint fromGLColor(uint color)
{
//ARGB <- ABGR
return (color & 0xFF00FF00) | (color >> 16 & 0xFF) | ((color & 0xFF) << 16);
}
struct ConsoleCharacter
{
wchar character;
int foreColor;
int backColor;
byte attr;
int z;
}
SDL_Color PetitColor(ubyte r, ubyte g, ubyte b, ubyte a)
{
return SDL_Color(r >> 5 << 5, g >> 5 << 5, b >> 5 << 5, a == 255 ? 255 : 0);
}
Button button;
struct Point
{
int x, y;
this(int x, int y)
{
this.x = x;
this.y = y;
}
}
int[2] bgpage;
Projects project;
bool redirectConsole;
int displayCount;
void init()
{
random = new Random();
project = new Projects(".");
// DerelictGL.load();
if(!exists(resourcePath))
{
writeln("create ./resources");
mkdir(resourceDirName);
}
if(!exists(fontFile))
{
writeln("download font");
download("http://smilebasic.com/wordpress/wp-content/themes/smilebasic/publishimage/appendix/res_font_table-320.png",
fontFile);
}
if(!exists(spriteFile))
{
writeln("download sprite");
download("http://dengekionline.com/elem/000/000/927/927124/petitcom_16_cs1w1_512x512.jpg",
spriteFile);
}
if(!exists(BGFile))
{
writeln("download BG");
download("http://dengekionline.com/elem/000/000/927/927125/petitcom_17_cs1w1_512x512.jpg",
BGFile);
}
screenWidth = 400;
screenHeight = 240;
screenWidthDisplay1 = 320;
screenHeightDisplay1 = 240;
currentDisplay = Display([SDL_Rect(0, 0, screenWidth, screenHeight)]);
displayCount = 2;
console = redirectConsole ? new NativeConsole(std.stdio.stdout, this) : new Console(this);
if(!exists(fontTableFile))
{
writeln("create font table");
//HTMLなんて解析したくないから適当
console.createFontTable();
}
else
{
console.loadFontTable();
}
functionKey = ["FILES", "LOAD\"", "SAVE\"", "LIST ERR", "RUN\n"];
writeln("OK");
}
int currentScreenWidth;
int currentScreenHeight;
void display(int number)
{
import std.exception : enforce;
enforce(number >= 0);
currentScreenHeight = currentDisplay.rect[number].h;
currentScreenWidth = currentDisplay.rect[number].w;
displaynum = number;
console.display(number);
graphic.display(number);
}
SDL_Renderer* renderer;
int vsyncFrame;
int vsyncCount;
int prevVSyncCount;
bool isVSync;
bool waitFlag;
void vsync(int f)
{
waitFlag = true;
isVSync = true;
vsyncFrame = f;
}
void wait(int f)
{
waitFlag = true;
isVSync = false;
vsyncFrame = f;
}
Graphic graphic;
Mutex keybuffermutex;
int keybufferpos;
int keybufferlen;
enum KeyOp
{
KEY,
COPY,
PASTE,
UNDO,
REDO,
}
struct Key
{
wchar key;
KeyOp op;
this(wchar k)
{
key = k;
op = KeyOp.KEY;
}
this(KeyOp o)
{
op = o;
}
}
//解析した結果キー入力のバッファは127くらい
Key[] keybuffer;// = new wchar[128];//Linuxだとこうやって確保した場合書き込むとSEGV
void sendKey(wchar key)
{
sendKey(Key(key));
}
void sendKey(Key key)
{
keybuffermutex.lock();
scope (exit)
keybuffermutex.unlock();
keybuffer[keybufferpos] = key;
keybufferlen++;
if(keybufferlen > keybuffer.length)
keybufferlen = cast(int)keybuffer.length;
keybufferpos = (keybufferpos + 1) % cast(int)keybuffer.length;
}
Mutex grpmutex;
int displaynum;
int renderstartpos;
//フォントの都合で縮小するとまともに見られないので非推奨
float scaleX = 1;
float scaleY = 1;
void glViewport2(int x, int y, int w, int h)
{
glViewport(cast(int)(x * scaleX), cast(int)(y * scaleY), cast(int)(w * scaleX), cast(int)(h * scaleY));
}
void chScreen2(int x, int y, int w, int h)
{
glViewport2(x, currentDisplay.yoffset - h - y, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, h, 0, 1024, -2048);
}
void chRenderingDisplay(int i)
{
chScreen2(currentDisplay.rect[i].x, currentDisplay.rect[i].y, currentDisplay.rect[i].w, currentDisplay.rect[i].h);
}
void chRenderingDisplay(int i, int x, int y, int w, int h)
{
glViewport2(x + currentDisplay.rect[i].x, currentDisplay.yoffset - h - y - currentDisplay.rect[i].y, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(x, x + w, y + h, y, 1024, -2048);
}
Button[] buttonTable;
Button[] controllerButtonTable;
Sprite sprite;
Size[7] resolutionTable = [Size(256, 192), Size(320, 200), Size(320, 240), Size(400, 240), Size(640, 400), Size(640, 480), Size(854, 480)];
int xscreenmode = 0;
int bgmax;
void xscreen(int mode, int tv, int sprite, int bg)
{
xscreen(mode, tv, -1, sprite, bg);
}
void xscreen(int mode, int sprite, int bg)
{
xscreen(mode, -1, -1, sprite, bg);
}
Display currentDisplay;
void xscreen(int mode, int tv, int gamepad, int sprite, int bg)
{
int mode2 = mode / 2;
synchronized(renderSync)
{
this.sprite.spclr();
this.sprite.spmax = sprite;
this.bgmax = bg;
//BG
xscreenmode = mode2;
if(mode2 == 0)
{
currentDisplay = Display([SDL_Rect(0, 0, screenWidth, screenHeight)], Size(400, 240));
}
if(mode2 == 1)
{
currentDisplay = Display([SDL_Rect(0, 0, screenWidth, screenHeight), SDL_Rect((screenWidth - screenWidthDisplay1) / 2, screenHeight, screenWidthDisplay1, screenHeightDisplay1)], Size(400, 480));
}
if(mode == 4)
{
currentDisplay = Display([SDL_Rect(0, 0, screenWidthDisplay1, screenHeight + screenHeightDisplay1)], Size(320, 480));
}
if (mode == 5)
{
currentDisplay = Display([SDL_Rect(0, 0, resolutionTable[tv].width, resolutionTable[tv].height)], Size(resolutionTable[tv].width, resolutionTable[tv].height));
xscreenmode = 3;
}
if (mode == 6)
{
auto display0 = SDL_Rect(0, 0, resolutionTable[tv].width, resolutionTable[tv].height);
auto display1 = SDL_Rect(0, resolutionTable[tv].height, resolutionTable[gamepad].width, resolutionTable[gamepad].height);
if (resolutionTable[tv].width > resolutionTable[gamepad].width)
{
display1.x = (resolutionTable[tv].width - resolutionTable[gamepad].width) / 2;
}
else
{
display0.x = (resolutionTable[gamepad].width - resolutionTable[tv].width) / 2;
}
currentDisplay = Display([display0, display1], Size(std.algorithm.max(resolutionTable[gamepad].width, resolutionTable[tv].width), resolutionTable[gamepad].height + resolutionTable[tv].height));
xscreenmode = 4;
}
currentDisplay.yoffset = currentDisplay.windowSize.height;
int grpw, grph;
graphic.getSize(grpw, grph);
//1024*1024?
if (mode == 5 || mode == 6)
{
if (grpw != 1024 || grph != 1024)
{
graphic.setSize(1024, 1024);
graphic.initGraphicPages();
}
}
else if (grpw != 512 || grph != 512)
{
graphic.setSize(512, 512);
graphic.initGraphicPages();
}
displaynum = 0;
console.changeDisplay(currentDisplay);
for(int i = 0; i < bgmax; i++)
{
this.bg[i].display = 0;
this.bg[i].clip;
}
if (currentDisplay.rect.length > 1)
{
for(int i = bgmax; i < this.bg.length; i++)
{
this.bg[i].display = 1;
this.bg[i].clip;
}
}
for (int i = cast(int)currentDisplay.rect.length - 1; i >= 0; i--)
{
display(i);
graphic.clip(false);
graphic.clip(true);
this.sprite.spclip;
}
}
updateWindowSize();
}
//do not call in synchronized(renderSync)
void updateWindowSize()
{
SDL_SetWindowSize(window, cast(int)(currentDisplay.windowSize.width * scaleX), cast(int)(currentDisplay.windowSize.height * scaleY));
}
bool isValidLayer(int layer)
{
if (layer < 0)
return false;
if(displaynum)
{
return layer < bg.length - bgmax;
}
return layer < bgmax;
}
BG getBG(int layer)
{
if(displaynum)
{
return bg[layer + bgmax];
}
return bg[layer];
}
BG[] allBG()
{
return bg;
}
//TODO:extract class
bool[2] BGvisibles = [true, true];
bool BGvisible()
{
return BGvisibles[displaynum];
}
void BGvisible(bool value)
{
BGvisibles[displaynum] = value;
}
protected BG[4] bg;
bool quit;
Condition renderCondition;
struct Touch
{
int tm;
int x;
int y;
//WiiU
int gamepadX;
int gamepadY;
int display1X;
int display1Y;
};
Object touchSync = new Object();
Touch m_touch;
Touch touchPosition()
{
synchronized(touchSync)
{
return m_touch;
}
}
void touchPosition(Touch to)
{
synchronized(touchSync)
{
m_touch = to;
}
}
int petitcomBackcolor;
void backcolor(int color)
{
petitcomBackcolor = color;
}
int backcolor()
{
return petitcomBackcolor;
}
void updateTouch(int mousex, int mousey)
{
int ww, wh;
SDL_GetWindowSize(window, &ww, &wh);
mousex = cast(int)(mousex / scaleX);
mousey = cast(int)(mousey / scaleY) - (wh - currentDisplay.yoffset);
import std.algorithm.comparison : clamp;
auto old = touchPosition;
if (x.mode != XMode.WIIU)
{
if (mousey >= screenHeight)
{
mousey -= screenHeight;
mousex -= (screenWidth - screenWidthDisplay1) / 2;
}
mousex = mousex.clamp(5, 314);
mousey = mousey.clamp(5, 234);
touchPosition = Touch(old.tm + 1, mousex, mousey, 0, 0, mousex, mousey);
}
else
{
//!!WiiU
if (xscreenmode == 2 || xscreenmode == 0 || xscreenmode == 3)//XSCREEN 5
{
auto w = currentDisplay.rect[0].w;
auto h = currentDisplay.rect[0].h;
auto x3DS = cast(int)(mousex * (screenWidthDisplay1 / cast(float)w));
auto y3DS = cast(int)(mousey * (screenHeightDisplay1 / cast(float)h));
auto gamepadx = cast(int)(mousex * (854 / cast(float)w));
auto gamepady = cast(int)(mousey * (480 / cast(float)h));
touchPosition = Touch(old.tm + 1,
x3DS.clamp(5, 314), y3DS.clamp(5, 234),
gamepadx.clamp(8, 854 - 9), gamepady.clamp(8, 480 - 9),
mousex.clamp(8, w - 13), mousey.clamp(8, h - 9));
}
else
{
auto w = currentDisplay.rect[1].w;
auto h = currentDisplay.rect[1].h;
mousex -= currentDisplay.rect[1].x;
mousey -= currentDisplay.rect[1].y;
if (mousex >= 0 && mousey >= 0)
{
auto x3DS = cast(int)(mousex * (screenWidthDisplay1 / cast(float)w));
auto y3DS = cast(int)(mousey * (screenHeightDisplay1 / cast(float)h));
auto gamepadx = cast(int)(mousex * (854 / cast(float)w));
auto gamepady = cast(int)(mousey * (480 / cast(float)h));
touchPosition = Touch(old.tm + 1,
x3DS.clamp(5, 314), y3DS.clamp(5, 234),
gamepadx.clamp(8, 854 - 9), gamepady.clamp(8, 480 - 9),
mousex.clamp(12, w - 13), mousey.clamp(8, h - 9));
}
}
}
}
//https://miiverse.nintendo.net/replies/AYMHAAADAAB2V0f0v9-s2g
struct Stick
{
int internalX;//-105~105
int internalY;//-105~105
static const int max = 105;
double x()
{
return internalX / 120.0;
}
double y()
{
return internalY / 120.0;
}
}
private Object stickSync = new Object();
private Stick[2] sticks;//0:stick, 1:stickex
public Stick stick(int id)
{
synchronized (stickSync)
return sticks[id];
}
public void stick(int id, Stick stick)
{
synchronized (stickSync)
sticks[id] = stick;
}
private void updateStick(SDL_GameController* controller, int id, SDL_GameControllerAxis x, SDL_GameControllerAxis y)
{
Stick s;
if (!activeController)
{
stick(id, s);
return;
}
import std.algorithm : clamp, min, max;
short stickx = SDL_GameControllerGetAxis(controller, x).clamp(cast(short)-short.max, short.max);
short sticky = SDL_GameControllerGetAxis(controller, y).clamp(cast(short)-short.max, short.max);
s.internalX = stickx / (short.max / Stick.max);
s.internalY = -sticky / (short.max / Stick.max);
stick(id, s);
}
private void updateSticks()
{
updateStick(activeController, 0, SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY);
updateStick(activeController, 1, SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY);
}
SDL_GLContext context;
SDL_GLContext contextVM;
Object renderSync = new Object();
Button[int] controllerTable;
void render()
{
try
{
import derelict.sdl2.ttf;
DerelictSDL2.load();
DerelictSDL2Image.load();
DerelictSDL2ttf.load();
TTF_Init();
sprite.sppage[] = 4;
bgpage[] = 5;
SDL_Init(SDL_INIT_VIDEO);
SDL_Init(SDL_INIT_GAMECONTROLLER);
DerelictGL.load();
SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
for (int i = 0; i < SDL_NumJoysticks(); i++)
{
AddController(i);
}
}
catch(Throwable t)
{
writeln(t);
}
bool renderprofile = false;
try
{
/*version(Windows)
{
auto imm32 = LoadLibraryA("imm32.dll".toStringz);
ImmDisableIME ImmDisableIME = cast(ImmDisableIME)GetProcAddress(imm32, "ImmDisableIME".toStringz);
if(ImmDisableIME) ImmDisableIME(0);
}*/
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
window = SDL_CreateWindow("SMILEBASIC", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, 400, 240,
SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
buttonTable = new Button[SDL_SCANCODE_SLEEP + 1];
buttonTable[SDL_SCANCODE_UP] = Button.UP;
buttonTable[SDL_SCANCODE_DOWN] = Button.DOWN;
buttonTable[SDL_SCANCODE_LEFT] = Button.LEFT;
buttonTable[SDL_SCANCODE_RIGHT] = Button.RIGHT;
buttonTable[SDL_SCANCODE_RETURN] = Button.A;
buttonTable[SDL_SCANCODE_X] = Button.A;
buttonTable[SDL_SCANCODE_ESCAPE] = Button.B;
buttonTable[SDL_SCANCODE_B] = Button.B;
buttonTable[SDL_SCANCODE_A] = Button.X;
buttonTable[SDL_SCANCODE_S] = Button.Y;
buttonTable[SDL_SCANCODE_Q] = Button.L;
buttonTable[SDL_SCANCODE_W] = Button.R;
controllerButtonTable = new Button[SDL_SCANCODE_SLEEP + 1];
controllerButtonTable[SDL_SCANCODE_UP] = Button.UP;
controllerButtonTable[SDL_SCANCODE_DOWN] = Button.DOWN;
controllerButtonTable[SDL_SCANCODE_LEFT] = Button.LEFT;
controllerButtonTable[SDL_SCANCODE_RIGHT] = Button.RIGHT;
controllerTable[SDL_CONTROLLER_BUTTON_DPAD_UP] = Button.UP;
controllerTable[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = Button.DOWN;
controllerTable[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = Button.LEFT;
controllerTable[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = Button.RIGHT;
controllerTable[SDL_CONTROLLER_BUTTON_A] = Button.A;
controllerTable[SDL_CONTROLLER_BUTTON_B] = Button.B;
controllerTable[SDL_CONTROLLER_BUTTON_X] = Button.X;
controllerTable[SDL_CONTROLLER_BUTTON_Y] = Button.Y;
controllerTable[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = Button.L;
controllerTable[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = Button.R;
controllerTable[SDL_CONTROLLER_BUTTON_START] = Button.START;
if(!window)
{
write("can't create window: ");
writeln(SDL_GetError.to!string);
return;
}
bool fullscreen = false;
scope(exit)
{
quit = true;
SDL_DestroyWindow(window);
SDL_Quit();
}
renderer = SDL_CreateRenderer(window, -1, 0);
if(!renderer)
{
write("can't create renderer: ");
writeln(SDL_GetError.to!string);
return;
}
SDL_Event event;
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
contextVM = SDL_GL_CreateContext(window);
context = SDL_GL_CreateContext(window);
if(!context || !contextVM)
{
write("can't create OpenGL context: ");
writeln(SDL_GetError.to!string);
return;
}
SDL_GL_MakeCurrent(window, context);
console.initGRPF();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_DEPTH_TEST);
/*version(Windows)
{
SDL_SysWMinfo wm;
if(SDL_GetWindowWMInfo(window, &wm))
{
ImmAssociateContext ImmAssociateContext = cast(ImmAssociateContext)GetProcAddress(imm32, "ImmAssociateContext".toStringz);
if(ImmAssociateContext)
{
auto c = ImmAssociateContext(wm.info.win.window, null);
}
}
}*/
int loopcnt;
graphic.initGraphicPages();
dialogResource = new DialogResources(renderer, textureScaleMode);
//3graphic.initGLGraphicPages();
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glAlphaFunc(GL_GEQUAL, 0.5);
//glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 0.1f);
glEnable(GL_ALPHA_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
{
renderCondition.mutex.lock;
scope (exit)
renderCondition.mutex.unlock;
renderCondition.notify();
}
while(!quit)
{
auto profile = SDL_GetTicks();
if(console.showCursor)
{
loopcnt++;
//30フレームに一回
if(loopcnt >= 30)
{
console.animationCursor = !console.animationCursor;
loopcnt = 0;
}
}
synchronized(renderSync)
{
glLoadIdentity();
{
ubyte r, g, b, a;
RGBRead(petitcomBackcolor, r, g, b, a);
glClearColor(r / 255f, g / 255f, b / 255f, 1);
}
//描画の順位
//sprite>GRP>console>BG
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
version(test) glLoadIdentity();
version(test) glRotatef(rot_test_deg, rot_test_x, rot_test_y, rot_test_z);
console.render();
graphic.render();
version(test) glLoadIdentity();
version(test) glRotatef(rot_test_deg, rot_test_x, rot_test_y, rot_test_z);
if (BGvisibles[0])
{
chRenderingDisplay(0);
glBindTexture(GL_TEXTURE_2D, graphic.GRP[bgpage[0]].glTexture);
for(int i = 0; i < bgmax; i++)
{
bg[i].render(0, currentDisplay.rect[0].w, currentDisplay.rect[0].h);
}
}
if(currentDisplay.rect.length > 1 && BGvisibles[1])
{
chRenderingDisplay(1);
glBindTexture(GL_TEXTURE_2D, graphic.GRP[bgpage[1]].glTexture);
for(int i = bgmax; i < bg.length; i++)
{
bg[i].render(1, currentDisplay.rect[1].w, currentDisplay.rect[1].h);
}
}
glMatrixMode(GL_MODELVIEW);
version(test) glLoadIdentity();
version(test) glRotatef(rot_test_deg, rot_test_x, rot_test_y, rot_test_z);
//http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20081122 みたいな方法もあるけどとりあえず
//とりあえず一番楽な方法main
//これだと同一Zでスプライトが一番下に来てしまうのでZ値を補正する必要がある->やった
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);//スプライト同士でのZバッファは半透明だと邪魔なので無効
sprite.render();//Zソートすべき->やった->プチコンの挙動的に安定ソートか非安定ソートか
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
for (int i = 0; i < currentDisplay.rect.length; i++)
{
fades[i].render();
}
if (dialog)
{
dialog.render();
}
SDL_GL_SwapWindow(window);
auto renderticks = (SDL_GetTicks() - profile);
if(renderprofile) writeln(renderticks);
int mousex, mousey;
if (SDL_GetMouseState(&mousex, &mousey) & SDL_BUTTON_LMASK)
{
updateTouch(mousex, mousey);
}
else
{
if (touchPosition.tm)
{
auto old = touchPosition;
old.tm = 0;
touchPosition = old;
}
}
}
maincntRender++;
updateSticks();
bool isReturnKeyPressed;//for IM
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
return;
case SDL_KEYUP:
synchronized (buttonLock)
{
auto key = event.key.keysym.sym;
if (controllers.length == 0)
button &= ~buttonTable[event.key.keysym.scancode];
else
button &= ~controllerButtonTable[event.key.keysym.scancode];
}
break;
case SDL_KEYDOWN:
synchronized (buttonLock)
{
auto key = event.key.keysym.sym;
if (event.key.keysym.mod & KMOD_ALT)
{
if (key == SDLK_RETURN)
{
fullscreen = !fullscreen;
if (fullscreen)
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
else
SDL_SetWindowFullscreen(window, 0);
break;
}
}
if (controllers.length == 0)
button |= buttonTable[event.key.keysym.scancode];
else
button |= controllerButtonTable[event.key.keysym.scancode];
if(key == SDLK_v)
{
auto mod = event.key.keysym.mod;
if((mod & 0xC0))
{
sendKey(Key(KeyOp.PASTE));
}
}
//TODO:mod key support
if(event.key.keysym.scancode == SDL_SCANCODE_C && (event.key.keysym.mod & 0xC0) || Button.START & buttonTable[event.key.keysym.scancode])
{
stop();
}
if (event.key.keysym.scancode <= SDL_SCANCODE_F5 && event.key.keysym.scancode >= SDL_SCANCODE_F1)
{
foreach(wchar k; functionKey[event.key.keysym.scancode - SDL_SCANCODE_F1])
{
sendKey(k);
}
}
if(key == SDLK_BACKSPACE)
{
sendKey('\u0008');
}
if(key == SDLK_RETURN)
{
isReturnKeyPressed = true;
}
}
break;
case SDL_TEXTEDITING:
{
auto text = event.edit.text[0..event.text.text.indexOf('\0')].to!wstring;
console.setIMEditInfo(text, event.edit.start, event.edit.length);
break;
}
case SDL_TEXTINPUT:
{
console.setIMEditInfo("", 0, 0);
auto text = event.text.text[0..event.text.text.indexOf('\0')].to!wstring;
foreach(wchar key; text)
{
sendKey(key);
}
isReturnKeyPressed = false;
}
break;
case SDL_MOUSEWHEEL:
synchronized (renderSync)
{
currentDisplay.yoffset -= event.wheel.y * 8;
}
break;
case SDL_CONTROLLERDEVICEADDED:
{
AddController(event.cdevice.which);
}
break;
case SDL_CONTROLLERDEVICEREMOVED:
{
RemoveController(event.cdevice.which);
}
break;
case SDL_CONTROLLERBUTTONDOWN:
{
auto jbtn = event.jbutton.button;
if (jbtn in controllerTable)
{
button |= controllerTable[event.jbutton.button];
}
}
break;
case SDL_CONTROLLERBUTTONUP:
{
auto jbtn = event.jbutton.button;
if (jbtn in controllerTable)
{
button &= ~controllerTable[event.jbutton.button];
}
}
break;
case SDL_FINGERDOWN:
{
auto f = event.tfinger;
auto x = f.x * this.currentDisplay.windowSize.width;
auto y = f.y * this.currentDisplay.windowSize.height;
updateTouch(cast(int)x, cast(int)y);
}
break;
default:
break;
}
}
if (isReturnKeyPressed)
sendKey('\u000D');
long delay = (1000/60) - (cast(long)SDL_GetTicks() - profile);
if(delay > 0)
SDL_Delay(cast(uint)delay);
}
}
catch(Throwable t)
{
writeln(t);
readln();
}
}
SDL_GameController*[int] controllers;
SDL_GameController* activeController;
void AddController(int id)
{
if(SDL_IsGameController(id))
{
SDL_GameController* pad = SDL_GameControllerOpen(id);
if (pad)
{
controllers[id] = pad;
activeController = pad;
}
}
}
void RemoveController(int id)
{
if(SDL_IsGameController(id))
{
SDL_GameControllerClose(controllers.get(id, null));
controllers.remove(id);
if (controllers.length == 0)
activeController = null;
else
activeController = controllers.byValue.front;
}
}
SDL_Window* window;
otya.smilebasic.vm.VM vm;
int maincntRender;
bool running;
bool stopflg;
void stop()
{
running = false;
stopflg = true;
}
void printInformation()
{
console.print("otyaSMILEBASIC ver ", versionString, "\n");
console.print("(C)2015-2017 otya\n");
console.print("8327164 bytes free\n");
console.print("\n");
printPrompt();
}
void printPrompt()
{
if(project.currentProject.length) console.print("[", project.currentProject, "]");
console.print("OK\n");
}
Console console;
Object buttonLock;
bool isRunningDirectMode = false;
GLenum textureScaleMode;
int maincnt;
SmileBasicError lastError;
void run(bool nodirectmode = false, string inputfile = "", bool antialiasing = false, bool redirectConsole = false)
{
if (antialiasing)
{
textureScaleMode = GL_LINEAR;
}
else
{
textureScaleMode = GL_NEAREST;
}
this.redirectConsole = redirectConsole;
buttonLock = new Object();
program = new Program();
keybuffer = new Key[128];
init();
graphic = new Graphic2(this);
keybuffermutex = new Mutex();
grpmutex = new Mutex();
sprite = new Sprite(this);
for(int i = 0; i < bg.length; i++)
bg[i] = new BG(this);
fades = new Fade[displayCount];
for (int i = 0; i < displayCount; i++)
fades[i] = new Fade(this, i);
core.thread.Thread thread = new core.thread.Thread(&render);
renderCondition = new Condition(new Mutex());
thread.start();
{
renderCondition.mutex.lock;
scope (exit)
renderCondition.mutex.unlock;
renderCondition.wait();
}
graphic.initVM();
xscreen(0, 512, 4);
auto startTicks = SDL_GetTicks();
Parser parser;
otya.smilebasic.vm.VM vm;
bool directMode = false;
int oldpc;
void runSlot(int lot)
{
parser = new Parser(cast(wstring)program.slot[lot].text);
try
{
vm = parser.compile();
vm.init(this);
running = true;
}
catch(SmileBasicError sbe)
{
console.print(sbe.getErrorMessage, "\n");
if(sbe.getErrorMessage2.length) console.print(sbe.getErrorMessage2, "\n");
writeln(sbe.to!string);
writeln(sbe.getErrorMessage2);
lastError = sbe;
}
catch(Throwable t)
{
lastError = new InternalError(t);
writeln(t);
}
}
//デバッグ用
version(NDirectMode)
nodirectmode = true;
if (nodirectmode)
{
program.slot[0].load(inputfile.to!wstring, readText(inputfile).to!wstring);
runSlot(0);
}
else
{
printInformation();
directMode = true;
}
do
{
if(quit) return;
if(directMode)
{
if(!vm)
{
import otya.smilebasic.vm;
vm = (new Parser("")).compile;
vm.init(this);
}
else
{
printPrompt();
}
auto prg = input("", true, false);
if (!prg.empty && (inputHistory.length == 0 || inputHistory[inputHistory.length - 1] != prg))
{
inputHistory.put(prg);
}
auto lex = new Lexical(prg);
lex.popFront();
auto token = lex.front();
if(token.type == otya.smilebasic.token.TokenType.Iden && token.value.stringValue == "RUN")
{
bool empty = lex.empty();
int slot;
if(!empty)
{
lex.popFront();
token = lex.front();
if(token.type != otya.smilebasic.token.TokenType.Integer)
{
//TODO:error
console.print("Illegal function call", "\n");
continue;
}
else
{
slot = token.value.castInteger;
}
}
isRunningDirectMode = false;
runSlot(slot);
}
else if(token.type == otya.smilebasic.token.TokenType.Iden && token.value.stringValue == "CONT")
{
vm.pc = oldpc;
isRunningDirectMode = false;
directMode = false;
}
else
{
isRunningDirectMode = true;
parser = new Parser(prg);
try
{
auto cc = parser.compiler;
cc.compileDirectMode(vm);
}
catch(SmileBasicError sbe)
{
try
{
console.print(sbe.getErrorMessage, "\n");
if(sbe.getErrorMessage2.length) console.print(sbe.getErrorMessage2, "\n");
writeln(sbe.to!string);
writeln(sbe.getErrorMessage2);
auto loc = vm.currentLocation;
console.print(vm.currentSlotNumber, ":", loc.line, ":", loc.pos, ":", program.slot[vm.currentSlotNumber].getLine(loc));
}
catch(Throwable t)
{
writeln(t);
}
continue;
}
catch(Throwable t)
{
try
{
console.print(t.to!string);
console.print("\n");
writeln(t);
auto loc = vm.currentLocation;
console.print(vm.currentSlotNumber, ":", loc.line, ":", loc.pos, ":", program.slot[vm.currentSlotNumber].getLine(loc));
}
catch(Throwable t)
{
writeln(t);
}
continue;
}
}
running = true;
}
version (dumpVM)
vm.dump;
this.vm = vm;
int startcnt = SDL_GetTicks();
float frame = 1000f / 60f;
typeof(vm.currentLocation()) loc;
debug bool trace = false;
while (true)
{
uint elapse;
startTicks = SDL_GetTicks();
int oldmaincnt = maincntRender;
//do
{
try
{
for(int i = 0; !quit && maincntRender == oldmaincnt && !waitFlag && running; i++)
{
version (traceVM)
if (vm.pc < vm.currentSlot.code.length)
std.stdio.stderr.writefln("%04X:%s", vm.pc, vm.getCurrent.toString(vm));
version (dumpStackVM)
vm.dumpStack();
auto b = vm.runStep();
running = b && running;
debug if(trace && loc.line != vm.currentLocation.line)
{
loc = vm.currentLocation;
console.print(loc.line, ":", loc.pos, ":", parser.getLine(loc));
}
}
}
catch(SmileBasicError sbe)
{
running = false;
try
{
console.print(sbe.getErrorMessage, "\n");
if(sbe.getErrorMessage2.length) console.print(sbe.getErrorMessage2, "\n");
//print(sbe.to!string);
writeln(sbe.to!string);
writeln(sbe.getErrorMessage2);
writeln(sbe.func);
lastError = sbe;
loc = vm.currentLocation;
console.print(vm.currentSlotNumber, ":", loc.line, ":", loc.pos, ":", program.slot[vm.currentSlotNumber].getLine(loc));
}
catch(Throwable t)
{
lastError = new InternalError(t);
writeln(t);
}
}
catch(Throwable t)
{
lastError = new InternalError(t);
running = false;
try
{
console.print(t.to!string);
console.print("\n");
writeln(t);
loc = vm.currentLocation;
console.print(vm.currentSlotNumber, ":", loc.line, ":", loc.pos, ":", program.slot[vm.currentSlotNumber].getLine(loc));
}
catch(Throwable t)
{
writeln(t);
}
}
if(!running || stopflg)
{
if (nodirectmode)
quit = true;
if(stopflg)
{
loc = vm.currentLocation;
console.print("Break on ", vm.currentSlotNumber, ":", loc.line, "\n");
stopflg = false;
directMode = true;
if(!isRunningDirectMode)
oldpc = vm.pc;
}
if(directMode) break;
}
//elapse = SDL_GetTicks() - startTicks;
} //while(elapse <= 1000 / 60);
if (maincntRender != oldmaincnt)
{
maincnt++;
vsyncCount++;
}
if(waitFlag)
{
graphic.updateVM();
if (isVSync && prevVSyncCount)
{
vsyncFrame -= vsyncCount - prevVSyncCount;
}
auto endmaincnt = maincnt + vsyncFrame;
oldmaincnt = maincntRender;
while (maincnt < endmaincnt && !quit && running)
{
SDL_Delay(1);
if (maincntRender != oldmaincnt)
{
maincnt++;
oldmaincnt = maincntRender;
}
}
vsyncFrame = 0;
waitFlag = false;
isVSync = false;
prevVSyncCount = vsyncCount;
}
//maincnt = cast(int)((SDL_GetTicks() - startcnt) / frame);
graphic.updateVM();
if(quit)
{
break;
}
}
graphic.updateVM();
} while(directMode);
scope(exit)
{
writeln("quit");
}
}
void clearKeyBuffer()
{
keybuffermutex.lock();
scope(exit) keybuffermutex.unlock();
keybufferpos = 0;
keybufferlen = 0;
}
RingBuffer!wstring inputHistory = new RingBuffer!wstring(32/*ダイレクトモードでの履歴の数は32*/);
wstring input(wstring prompt, bool useClipBoard, bool isVM/*if this flag is true, enable start/select interrupt*/)
{
auto olddisplay = displaynum;
//displaynum = 0;
console.print(prompt);
clearKeyBuffer();
wstring buffer;
console.showCursor = true;
int oldCSRX = this.console.CSRX;
int oldCSRY = this.console.CSRY;
int pos;
int historyIndex = cast(int)inputHistory.length;
int oldmaincnt = maincntRender;
scope (exit)
{
maincnt += maincntRender - oldmaincnt;
vsyncCount += maincntRender - oldmaincnt;
}
void setText(wstring text)
{
console.CSRX = oldCSRX;
console.CSRY = oldCSRY;
for (int i = 0; i < buffer.length; i++)
{
console.print(" ");
}
buffer = text;
pos = cast(int)text.length;
console.CSRX = oldCSRX;
console.CSRY = oldCSRY;
console.print(text);
}
void left()
{
if (pos == 0)
{
return;
}
pos--;
if (console.CSRX == 0)
{
console.CSRY--;
console.CSRX = console.consoleWidthC - 1;
return;
}
console.CSRX--;
}
void right()
{
if (pos >= buffer.length)
{
return;
}
pos++;
console.CSRX++;
if (console.CSRX == console.consoleWidthC)
{
console.CSRY++;
console.CSRX = 0;
}
}
while(!quit && (!isVM || running))
{
auto oldpos = keybufferpos;
while(oldpos == keybufferpos && !quit && (!isVM || running))
{
Button old;
synchronized(buttonLock)
{
old = button;
}
SDL_Delay(16);//適当 ストレスを感じないくらい
Button button;
synchronized(buttonLock)
{
button = this.button;
}
button = button ^ old & button;
if (button & Button.UP)
{
if (historyIndex - 1 >= 0)
{
historyIndex--;
setText(inputHistory[historyIndex]);
}
}
if (button & Button.DOWN)
{
if (historyIndex + 1 < inputHistory.length)
{
historyIndex++;
setText(inputHistory[historyIndex]);
}
else
{
historyIndex++;
setText("");
}
}
if (button & Button.LEFT)
{
left();
}
if (button & Button.RIGHT)
{
right();
}
}
auto kbp = keybufferpos;
auto len = kbp - oldpos;
if(len < 0)
{
//arienai
kbp = oldpos;
}
wchar k;
//文字入力中はカーソルを表示する
console.animationCursor = true;
foreach(key1; keybuffer[oldpos..kbp])
{
wchar key = key1.key;
wstring ks;
wstring obf = buffer;
if(key1.op == KeyOp.KEY)
{
if(key == 8)
{
k = key;
if(!buffer.length) continue;
left();
auto odcsrx = console.CSRX;
auto odcsry = console.CSRY;
buffer = (pos ? buffer[0..pos] : "");
auto ocsrx = console.CSRX;
auto ocsry = console.CSRY;
if (obf.length > pos + 1)
{
buffer ~= obf[pos + 1..$];
console.print(obf[pos + 1..$]);
}
console.print(" ");
console.CSRX = odcsrx;
console.CSRY = odcsry;
continue;
}
if(key == '\r')
{
k = key;
console.print("\n");
break;
}
immutable(wchar[1]) a = key;
ks = a[];
}
else if(key1.op == KeyOp.PASTE)
{
if(SDL_HasClipboardText())
{
char* cl = SDL_GetClipboardText();
string an = cast(string)(cl[0..core.stdc.string.strlen(cl)]);
ks = an.to!wstring;
SDL_free(cl);
}
}
console.print(ks);
buffer = (pos ? buffer[0..pos] : "") ~ ks;
auto ocsrx = console.CSRX;
auto ocsry = console.CSRY;
if (obf.length > pos)
{
buffer ~= obf[pos..$];
console.print(obf[pos..$]);
}
console.CSRX = ocsrx;
console.CSRY = ocsry;
pos += ks.length;
}
clearKeyBuffer();
if(k == '\r')
{
break;
}
}
console.showCursor = false;
//display = olddisplay;
return buffer;
}
wstring inkey()
{
keybuffermutex.lock();
scope (exit)
keybuffermutex.unlock();
if (!keybufferlen)
{
return "";
}
auto result = keybuffer[(keybufferpos - keybufferlen) % keybuffer.length];
keybufferlen--;
return result.key.to!wstring;
}
public bool isValidGraphicPage(int page)
{
return page >= 0 && page < 6;
}
enum XMode
{
_3DS,
COMPAT,
WIIU,
}
struct X
{
bool motion;
bool expad;
bool mic;
XMode mode;
}
X x;
public void xoff(wstring func)
{
if (func == "MIC")
{
x.mic = false;
}
else if (func == "EXPAD")
{
x.expad = false;
}
else if (func == "MOTION")
{
x.motion = false;
}
else
{
std.stdio.stderr.writefln("Unknown XOFF %s", func);
}
}
public void xon(wstring func)
{
if (func == "WIIU")
{
x.mode = XMode.WIIU;
}
else if (func == "3DS")
{
x.mode = XMode._3DS;
}
else if (func == "COMPAT")
{
x.mode = XMode.COMPAT;
}
else if (func == "MIC")
{
x.mic = true;
}
else if (func == "EXPAD")
{
x.expad = true;
}
else if (func == "MOTION")
{
x.motion = true;
}
else
{
std.stdio.stderr.writefln("Unknown XON %s", func);
}
}
Hardware hardware = Hardware.wiiU;
wstring versionString = "3.5.0";
int version_ = 0x3050000;
wstring[5] functionKey;
DialogBase dialog;
DialogResources dialogResource;
SDL_Rect showTouchScreen()
{
if (currentDisplay.rect.length != 1)
{
return currentDisplay.rect[1];
}
SDL_Rect rect;
if (xscreenmode == 0)
{
rect = SDL_Rect((screenWidth - screenWidthDisplay1) / 2, screenHeight, screenWidthDisplay1, screenHeightDisplay1);
}
else
{
rect = currentDisplay.rect[0];
rect.y += rect.h;
}
currentDisplay.windowSize.height += rect.h;
currentDisplay.yoffset = currentDisplay.windowSize.height;
updateWindowSize();
return rect;
}
void hideTouchScreen()
{
if (currentDisplay.rect.length != 1)
{
return;
}
auto rect = currentDisplay.rect[0];
currentDisplay.windowSize.height -= rect.h;
currentDisplay.yoffset = currentDisplay.windowSize.height;
updateWindowSize();
}
Program program;
void acls(bool gclsAndSpriteImageBGImageInit, bool spdef, bool fontdef)
{
console.initConsole();
xscreen(0, 512, 4);
console.cls();
backcolor = 0;
if (gclsAndSpriteImageBGImageInit)
{
//TODO:sprite,bg
for (int i = 0; i <= 3; i++)
{
this.graphic.useGRP = i;
graphic.gcls(0, 0);
}
}
this.graphic.usePage = [0, 1];
this.graphic.showPage = [0, 1];
this.graphic.gprios = [512, 512];/*reference:GPRIO 1024*/
this.graphic.visibles = [1, 1];
this.sprite.sppage = [4, 4];
this.sprite.allclear();
this.sprite.visibles = [true, true];
this.bgpage = [5, 5];
this.BGvisibles = [true, true];
foreach (b; bg)
{
b.screen(25, 15);
b.clip();
}
if (spdef)
{
this.sprite.spdef();
}
if (fontdef)
{
this.console.initGRPF();
}
}
int clipboardSize = 1048576;
wstring clipboard()
{
if(SDL_HasClipboardText())
{
char* cl = SDL_GetClipboardText();
string an = cast(string)(cl[0..core.stdc.string.strlen(cl)]);
auto ret = an.to!wstring;
if (ret.length > clipboardSize)
ret = ret[0..clipboardSize];
SDL_free(cl);
return ret;
}
return "";
}
void clipboard(wstring ws)
{
if (ws.length > clipboardSize)
{
throw new StringTooLong("CLIPBOARD");
}
SDL_SetClipboardText(ws.to!string.toStringz);
}
bool callback;
int callidx = 0;
//プチコン内部表現はRGB5_A1
static uint toGLColor(GLenum format, ubyte r, ubyte g, ubyte b, ubyte a)
{
if(format == GL_BGRA)
{
return a << 24 | r << 16 | g << 8 | b;
}
if(format == GL_RGBA)
{
return a << 24 | b << 16 | g << 8 | r;
}
throw new Exception("unsuport enviroment");
}
static uint toGLColor(GLenum format, uint petitcolor)
{
if(format == GL_BGRA)
{
//ARGB->BGRA
return (petitcolor & 0xFF00FF00) | (petitcolor & 0xFF) << 16 | petitcolor >> 16 & 0xFF;
}
if(format == GL_RGBA)
{
//return a << 24 | b << 16 | g << 8 | r;
}
throw new Exception("unsuport enviroment");
}
//プチコンと違って[A,]R,G,Bじゃない
static pure nothrow void RGBRead(T)(uint color, out T r, out T g, out T b, out T a)
{
//エンディアン関係ない
a = color >> 24;
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color& 0xFF;
}
static pure nothrow uint RGB(ubyte r, ubyte g, ubyte b)
{
return 0xFF000000 | r << 16 | g << 8 | b;
}
static pure nothrow uint RGB(ubyte a, ubyte r, ubyte g, ubyte b)
{
return a << 24 | r << 16 | g << 8 | b;
}
public Random random;
Fade[] fades;
public Fade fade()
{
return fades[displaynum];
}
}
unittest
{
auto petitcomputer = new PetitComputer();
petitcomputer.run(true, "TEST.txt", false, true);
assert(petitcomputer.lastError is null);
}