Files
nixie-driver/firmware/nixitest1/testnix/rawdraw_sf.h

7414 lines
224 KiB
C

//This file was automatically generated by Makefile at https://github.com/cntools/rawdraw
//Generated from files git hash f90dd987fef2a5efde5ea05251f1d72e07e91a5d on Wed Feb 8 12:11:21 PST 2023 (This is not the git hash of this file)
// Copyright 2010-2021 <>< CNLohr, et. al. (Several other authors, many but not all mentioned)
// Licensed under the MIT/x11 or NewBSD License you choose.
//
// CN Foundational Graphics Main Header File. This is the main header you
// should include. See README.md for more details.
#ifndef _CNFG_H
#define _CNFG_H
#ifdef __cplusplus
extern "C" {
#endif
/* Rawdraw flags:
CNFG3D -> Enable the weird 3D functionality that rawdraw has to allow you to
write apps which emit basic rawdraw primitives but look 3D!
CNFG_USE_DOUBLE_FUNCTIONS -> Use double-precision floating point for CNFG3D.
CNFGOGL -> Use an OpenGL Backend for all rawdraw functionality.
->Caveat->If using CNFG_HAS_XSHAPE, then, we do something realy wacky.
CNFGRASTERIZER -> Software-rasterize the rawdraw calls, and, use
CNFGUpdateScreenWithBitmap to send video to webpage.
CNFGCONTEXTONLY -> Don't add any drawing functions, only opening a window to
get an OpenGL context.
CNFG_IMPLEMENTATION -> #define this and it will make _this_ the file where the cnfg
functions actually live.
Usually tested combinations:
* TCC On Windows and X11 (Linux) with:
- CNFGOGL on or CNFGOGL off. If CNFGOGL is off you can use
CNFG_WINDOWS_DISABLE_BATCH to disable all batching.
-or-
- CNFGRASTERIZER
NOTE: Sometimes you can also use CNFGOGL + CNFGRASTERIZER
* WASM driver supports both: CNFGRASTERIZER and without CNFGRASTERIZER (Recommended turn rasterizer off)
* ANDROID (But this automatically sets CNFGRASTERIZER OFF and CNFGOGL ON)
Unusual compiler flags:
* CNFGHTTP - Enable the HTTP server-version of rawdraw, where it renders to a website.
* CNFGHTTPSERVERONLY - if you want to use the HTTP server w/o rawdraw. You will need to implement:
- CloseEvent, HTTPCustomCallback, HTTPCustomStart, NewWebSocket, WebSocketData, WebSocketTick
* CNFG_DISABLE_HTTP_FILES - disable the HTTP file server.
*/
#include <stdint.h>
//Some per-platform logic.
#if defined( ANDROID ) || defined( __android__ )
#define CNFGOGL
#endif
#if ( defined( CNFGOGL ) || defined( __wasm__ ) ) && !defined(CNFG_HAS_XSHAPE)
#define CNFG_BATCH 8192 //131,072 bytes.
#if defined( ANDROID ) || defined( __android__ ) || defined( __wasm__ ) || defined( EGL_LEAN_AND_MEAN )
#define CNFGEWGL //EGL or WebGL
#else
#define CNFGDESKTOPGL
#endif
#endif
typedef struct {
short x, y;
} RDPoint;
extern int CNFGPenX, CNFGPenY;
extern uint32_t CNFGBGColor;
extern uint32_t CNFGLastColor;
extern uint32_t CNFGDialogColor; //Only used for DrawBox
//Draws text at CNFGPenX, CNFGPenY, with scale of `scale`.
void CNFGDrawText( const char * text, short scale );
//Determine how large a given test would be to draw.
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize );
//Draws a box, outline as whatever the last CNFGColor was set to but also draws
//a rectangle as a background as whatever CNFGDialogColor is set to.
void CNFGDrawBox( short x1, short y1, short x2, short y2 );
//To be provided by driver. Rawdraw uses colors in the format 0xRRGGBBAA
//Note that some backends do not support alpha of any kind.
//Some platforms also support alpha blending. So, be sure to set alpha to 0xFF
uint32_t CNFGColor( uint32_t RGBA );
//This both updates the screen, and flips, all as a single operation.
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h );
//This is only supported on a FEW architectures, but allows arbitrary
//image blitting. Note that the alpha channel behavior is different
//on different systems.
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h );
// Only supported with CNFGOGL
#ifdef CNFGOGL
void CNFGDeleteTex( unsigned int tex );
unsigned int CNFGTexImage( uint32_t *data, int w, int h );
void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h );
#endif
void CNFGTackPixel( short x1, short y1 );
void CNFGTackSegment( short x1, short y1, short x2, short y2 );
void CNFGTackRectangle( short x1, short y1, short x2, short y2 );
void CNFGTackPoly( RDPoint * points, int verts );
void CNFGClearFrame();
void CNFGSwapBuffers();
void CNFGGetDimensions( short * x, short * y );
//This will setup a window. Note that w and h have special meaning. On Windows
//and X11, for instance if you set w and h to be negative, then rawdraw will not
//show the window to the user. This is useful if you just need it for some
//off-screen-rendering purpose.
//
//Return value of 0 indicates success. Nonzero indicates error.
int CNFGSetup( const char * WindowName, int w, int h );
void CNFGSetupFullscreen( const char * WindowName, int screen_number );
int CNFGHandleInput();
//You must provide:
void HandleKey( int keycode, int bDown );
void HandleButton( int x, int y, int button, int bDown );
void HandleMotion( int x, int y, int mask );
void HandleDestroy();
//Internal function for resizing rasterizer for rasterizer-mode.
void CNFGInternalResize( short x, short y ); //don't call this.
//Not available on all systems. Use The OGL portion with care.
#ifdef CNFGOGL
void CNFGSetVSync( int vson );
void * CNFGGetExtension( const char * extname );
#endif
//Also not available on all systems. Transparency.
void CNFGPrepareForTransparency();
void CNFGDrawToTransparencyMode( int transp );
void CNFGClearTransparencyLevel();
//Only available on systems that support it.
void CNFGSetLineWidth( short width );
void CNFGChangeWindowTitle( const char * windowtitle );
void CNFGSetWindowIconData( int w, int h, uint32_t * data );
int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ );
//If you're using a batching renderer, for instance on Android or an OpenGL
//You will need to call this function inbetewen swtiching properties of drawing. This is usually
//only needed if you calling OpenGL / OGLES functions directly and outside of CNFG.
//
//Note that these are the functions that are used on the backends which support this
//sort of thing.
#ifdef CNFG_BATCH
//If you are not using the CNFGOGL driver, you will need to define these in your driver.
void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices );
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h );
//These need to be defined for the specific driver.
void CNFGClearFrame();
void CNFGSwapBuffers();
void CNFGFlushRender(); //Emit any geometry (lines, squares, polys) which are slated to be rendered.
void CNFGInternalResize( short x, short y ); //Driver calls this after resize happens.
void CNFGSetupBatchInternal(); //Driver calls this after setup is complete.
//Useful function for emitting a non-axis-aligned quad.
void CNFGEmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 );
extern int CNFGVertPlace;
extern float CNFGVertDataV[CNFG_BATCH*3];
extern uint32_t CNFGVertDataC[CNFG_BATCH];
#endif
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
#define CNFG_KEY_SHIFT 0x10
#define CNFG_KEY_BACKSPACE 0x08
#define CNFG_KEY_DELETE 0x2E
#define CNFG_KEY_LEFT_ARROW 0x25
#define CNFG_KEY_RIGHT_ARROW 0x27
#define CNFG_KEY_TOP_ARROW 0x26
#define CNFG_KEY_BOTTOM_ARROW 0x28
#define CNFG_KEY_ESCAPE 0x1B
#define CNFG_KEY_ENTER 0x0D
#elif defined( EGL_LEAN_AND_MEAN ) // doesn't have any keys
#elif defined( __android__ ) || defined( ANDROID ) // ^
#elif defined( __wasm__ )
#define CNFG_KEY_SHIFT 16
#define CNFG_KEY_BACKSPACE 8
#define CNFG_KEY_DELETE 46
#define CNFG_KEY_LEFT_ARROW 37
#define CNFG_KEY_RIGHT_ARROW 39
#define CNFG_KEY_TOP_ARROW 38
#define CNFG_KEY_BOTTOM_ARROW 40
#define CNFG_KEY_ESCAPE 27
#define CNFG_KEY_ENTER 13
#else // most likely x11
#define CNFG_KEY_SHIFT 65505
#define CNFG_KEY_BACKSPACE 65288
#define CNFG_KEY_DELETE 65535
#define CNFG_KEY_LEFT_ARROW 65361
#define CNFG_KEY_RIGHT_ARROW 65363
#define CNFG_KEY_TOP_ARROW 65362
#define CNFG_KEY_BOTTOM_ARROW 65364
#define CNFG_KEY_ESCAPE 65307
#define CNFG_KEY_ENTER 65293
#define CNFG_X11_EXPOSE 0xff00 //65280
#endif
#ifdef CNFG3D
#ifndef __wasm__
#include <math.h>
#endif
#ifdef CNFG_USE_DOUBLE_FUNCTIONS
#define tdCOS cos
#define tdSIN sin
#define tdTAN tan
#define tdSQRT sqrt
#else
#define tdCOS cosf
#define tdSIN sinf
#define tdTAN tanf
#define tdSQRT sqrtf
#endif
#ifdef __wasm__
void tdMATCOPY( float * x, const float * y ); //Copy y into x
#else
#define tdMATCOPY(x,y) memcpy( x, y, 16*sizeof(float))
#endif
#define tdQ_PI 3.141592653589
#define tdDEGRAD (tdQ_PI/180.)
#define tdRADDEG (180./tdQ_PI)
//General Matrix Functions
void tdIdentity( float * f );
void tdZero( float * f );
void tdTranslate( float * f, float x, float y, float z ); //Operates ON f
void tdScale( float * f, float x, float y, float z ); //Operates ON f
void tdRotateAA( float * f, float angle, float x, float y, float z ); //Operates ON f
void tdRotateQuat( float * f, float qw, float qx, float qy, float qz ); //Operates ON f
void tdRotateEA( float * f, float x, float y, float z ); //Operates ON f
void tdMultiply( float * fin1, float * fin2, float * fout ); //Operates ON f
void tdPrint( const float * f );
void tdTransposeSelf( float * f );
//Specialty Matrix Functions
void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out ); //Sets, NOT OPERATES. (FOVX=degrees)
void tdLookAt( float * m, float * eye, float * at, float * up ); //Operates ON m
//General point functions
#define tdPSet( f, x, y, z ) { f[0] = x; f[1] = y; f[2] = z; }
void tdPTransform( const float * pin, float * f, float * pout );
void tdVTransform( const float * vin, float * f, float * vout );
void td4Transform( float * kin, float * f, float * kout );
void td4RTransform( float * kin, float * f, float * kout );
void tdNormalizeSelf( float * vin );
void tdCross( float * va, float * vb, float * vout );
float tdDistance( float * va, float * vb );
float tdDot( float * va, float * vb );
#define tdPSub( x, y, z ) { (z)[0] = (x)[0] - (y)[0]; (z)[1] = (x)[1] - (y)[1]; (z)[2] = (x)[2] - (y)[2]; }
#define tdPAdd( x, y, z ) { (z)[0] = (x)[0] + (y)[0]; (z)[1] = (x)[1] + (y)[1]; (z)[2] = (x)[2] + (y)[2]; }
//Stack Functionality
#define tdMATRIXMAXDEPTH 32
extern float * gSMatrix;
void tdPush();
void tdPop();
void tdMode( int mode );
#define tdMODELVIEW 0
#define tdPROJECTION 1
//Final stage tools
void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy );
void tdFinalPoint( float * pin, float * pout );
float tdNoiseAt( int x, int y );
float tdFLerp( float a, float b, float t );
float tdPerlin2D( float x, float y );
#endif
extern const unsigned char RawdrawFontCharData[1405];
extern const unsigned short RawdrawFontCharMap[256];
#ifdef __cplusplus
};
#endif
#if defined( ANDROID ) || defined( __android__ )
#ifndef _CNFG_ANDROID_H
#define _CNFG_ANDROID_H
//This file contains the additional functions that are available on the Android platform.
//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID
// Tricky: Android headers are confused by c++ if linking statically.
#ifdef __cplusplus
extern "C" {
int __system_property_get(const char* __name, char* __value);
};
#endif
extern struct android_app * gapp;
void AndroidMakeFullscreen();
int AndroidHasPermissions(const char* perm_name);
void AndroidRequestAppPermissions(const char * perm);
void AndroidDisplayKeyboard(int pShow);
int AndroidGetUnicodeChar( int keyCode, int metaState );
void AndroidSendToBack( int param );
extern int android_sdk_version; //Derived at start from property ro.build.version.sdk
extern int android_width, android_height;
extern int UpdateScreenWithBitmapOffsetX;
extern int UpdateScreenWithBitmapOffsetY;
// If you need them, these are the names of raw EGL symbols.
//extern EGLDisplay egl_display;
//extern EGLSurface egl_surface;
//extern EGLContext egl_context;
//extern EGLConfig egl_config;
//You must implement these.
void HandleResume();
void HandleSuspend();
//Departures:
// HandleMotion's "mask" parameter is actually just an index, not a mask
// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation
// decoration is removed. Fullscreen means *full screen* To choose fullscreen
// or not fullscrene, modify, in your AndroidManifest.xml file, the application
// section to either contain or not contain:
// android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
#endif
#endif
#ifdef CNFG_IMPLEMENTATION
//Include this file to get all of rawdraw. You usually will not
//want to include this in your build, but instead, #include "CNFG.h"
//after #define CNFG_IMPLEMENTATION in one of your C files.
#if defined( CNFGHTTP )
//Copyright 2015-2021 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or
// ColorChord License. You Choose. This file mostly based on `cnhttp` from cntools.
#ifdef CNFGHTTP
//Pull from a buffer
#ifndef CNFGHTTP_LIVE_FS
#define USE_RAM_MFS
#endif
//single_file_http.c base from https://github.com/cntools/httptest.
//scroll to bottom for implementation.
#ifndef CUSTOM_HTTPHEADER_CODE
#define CUSTOM_HTTPHEADER_CODE PushString("Access-Control-Allow-Origin: *\r\n");
#endif
/* public api for steve reid's public domain SHA-1 implementation */
/* this file is in the public domain */
#include <stdint.h>
#include <malloc.h>
typedef struct {
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
} RD_SHA1_CTX;
#define RD_SHA1_DIGEST_SIZE 20
void static RD_SHA1_Init(RD_SHA1_CTX* context);
void static RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len);
void static RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context); //WARNINGThe parameters are flipped here. (CNL)
//Not to be confused with MFS for the AVR.
#ifndef _MFS_H
#define _MFS_H
#ifndef USE_RAM_MFS
#include <stdio.h>
#endif
#define MFS_SECTOR 256
#define MFS_FILENAMELEN 32-8
#define MFS_FILE_COMPRESSED_MEMORY (-2)
//Format:
// [FILE NAME (24)] [Start (4)] [Len (4)]
// NOTE: Filename must be null-terminated within the 24.
struct MFSFileEntry
{
char name[MFS_FILENAMELEN];
uint32_t start; //From beginning of mfs thing.
uint32_t len;
};
struct MFSFileInfo
{
uint32_t filelen;
#ifdef USE_RAM_MFS
uint32_t offset;
#else
FILE * file;
#endif
};
//Returns 0 on succses.
//Returns size of file if non-empty
//If positive, populates mfi.
//Returns -1 if can't find file or reached end of file list.
int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi );
int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ); //returns # of bytes left in file.
void MFSClose( struct MFSFileInfo * mfi );
#endif
#ifndef _CNHTTP_H
#define _CNHTTP_H
#include <stdint.h>
extern struct HTTPConnection * curhttp;
extern uint8_t * curdata;
extern uint16_t curlen;
extern uint8_t wsmask[4];
extern uint8_t wsmaskplace;
uint8_t WSPOPMASK();
#define HTTPPOP (*curdata++)
//You must provide this.
void HTTPCustomStart( );
void HTTPCustomCallback( ); //called when we can send more data
void WebSocketData( int len );
void WebSocketTick( );
void WebSocketNew();
void HTTPHandleInternalCallback( );
uint8_t hex2byte( const char * c );
void NewWebSocket();
void et_espconn_disconnect( int socket );
//Internal Functions
void HTTPTick( uint8_t timedtick );
int URLDecode( char * decodeinto, int maxlen, const char * buf );
void WebSocketGotData( uint8_t c );
void WebSocketTickInternal();
void WebSocketSend( uint8_t * data, int size );
//Host-level functions
void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data );
void Uint32To10Str( char * out, uint32_t dat );
void http_recvcb(int conn, char *pusrdata, unsigned short length);
void http_disconnetcb(int conn);
int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure
void DataStartPacket();
extern uint8_t * databuff_ptr;
void PushString( const char * data );
void PushByte( uint8_t c );
void PushBlob( const uint8_t * datam, int len );
int TCPCanSend( int socket, int size );
int TCPDoneSend( int socket );
int EndTCPWrite( int socket );
#define HTTP_CONNECTIONS 50
#ifndef MAX_HTTP_PATHLEN
#define MAX_HTTP_PATHLEN 80
#endif
#define HTTP_SERVER_TIMEOUT 500
#define HTTP_STATE_NONE 0
#define HTTP_STATE_WAIT_METHOD 1
#define HTTP_STATE_WAIT_PATH 2
#define HTTP_STATE_WAIT_PROTO 3
#define HTTP_STATE_WAIT_FLAG 4
#define HTTP_STATE_WAIT_INFLAG 5
#define HTTP_STATE_DATA_XFER 7
#define HTTP_STATE_DATA_WEBSOCKET 8
#define HTTP_WAIT_CLOSE 15
struct HTTPConnection
{
uint8_t state:4;
uint8_t state_deets;
//Provides path, i.e. "/index.html" but, for websockets, the last
//32 bytes of the buffer are used for the websockets key.
uint8_t pathbuffer[MAX_HTTP_PATHLEN];
uint8_t is_dynamic:1;
uint16_t timeout;
union data_t
{
struct MFSFileInfo filedescriptor;
struct UserData { uint16_t a, b, c; } user;
struct UserDataPtr { void * v; } userptr;
} data;
void * rcb;
void * rcbDat; //For websockets primarily.
void * ccb; //Close callback (used for websockets, primarily)
uint32_t bytesleft;
uint32_t bytessofar;
uint8_t is404:1;
uint8_t isdone:1;
uint8_t isfirst:1;
uint8_t keep_alive:1;
uint8_t need_resend:1;
uint8_t send_pending:1; //If we can send data, we should?
uint8_t is_gzip:1;
int socket;
uint8_t corked_data[4096];
int corked_data_place;
};
extern struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
#endif
#ifndef _HTTP_BSD_H
#define _HTTP_BSD_H
//Call this to start your webserver.
int RunHTTP( int port );
int TickHTTP(); //returns -1 if problem.
//For running on a BSD Sockets System
int htsend( int socket, uint8_t * data, int datact );
void et_espconn_disconnect( int socket );
void http_recvcb(int whichhttp, char *pusrdata, unsigned short length);
void http_disconnetcb(int whichhttp);
int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure
void DataStartPacket();
extern uint8_t * databuff_ptr;
void PushBlob( const uint8_t * data, int len );
void PushByte( uint8_t c );
void PushString( const char * data );
int TCPCanSend( int socket, int size );
int TCPDoneSend( int socket );
int EndTCPWrite( int socket );
void TermHTTPServer();
extern int cork_binary_rx;
#endif
//Copyright 2012-2016 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or ColorChord License. You Choose.
#include <string.h>
#include <stdio.h>
struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
#define HTDEBUG( x, ... ) printf( x, ##__VA_ARGS__ )
//#define HTDEBUG( x... )
//#define ISKEEPALIVE "keep-alive"
#define ISKEEPALIVE "close"
struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
struct HTTPConnection * curhttp;
uint8_t * curdata;
uint16_t curlen;
uint8_t wsmask[4];
uint8_t wsmaskplace;
void CloseEvent();
void InternalStartHTTP( );
void HTTPHandleInternalCallback( );
void HTTPClose( )
{
//This is dead code, but it is a testament to Charles.
//Do not do this here. Wait for the ESP to tell us the
//socket is successfully closed.
//curhttp->state = HTTP_STATE_NONE;
curhttp->state = HTTP_WAIT_CLOSE;
et_espconn_disconnect( curhttp->socket );
CloseEvent();
}
void HTTPGotData( )
{
uint8_t c;
curhttp->timeout = 0;
while( curlen-- )
{
c = HTTPPOP;
// sendhex2( h->state ); sendchr( ' ' );
switch( curhttp->state )
{
case HTTP_STATE_WAIT_METHOD:
if( c == ' ' )
{
curhttp->state = HTTP_STATE_WAIT_PATH;
curhttp->state_deets = 0;
}
break;
case HTTP_STATE_WAIT_PATH:
curhttp->pathbuffer[curhttp->state_deets++] = c;
if( curhttp->state_deets == MAX_HTTP_PATHLEN )
{
curhttp->state_deets--;
}
if( c == ' ' )
{
//Tricky: If we're a websocket, we need the whole header.
curhttp->pathbuffer[curhttp->state_deets-1] = 0;
curhttp->state_deets = 0;
if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws", 5 ) == 0 )
{
curhttp->state = HTTP_STATE_DATA_WEBSOCKET;
curhttp->state_deets = 0;
}
else
{
curhttp->state = HTTP_STATE_WAIT_PROTO;
}
}
break;
case HTTP_STATE_WAIT_PROTO:
if( c == '\n' )
{
curhttp->state = HTTP_STATE_WAIT_FLAG;
}
break;
case HTTP_STATE_WAIT_FLAG:
if( c == '\n' )
{
curhttp->state = HTTP_STATE_DATA_XFER;
InternalStartHTTP( );
}
else if( c != '\r' )
{
curhttp->state = HTTP_STATE_WAIT_INFLAG;
}
break;
case HTTP_STATE_WAIT_INFLAG:
if( c == '\n' )
{
curhttp->state = HTTP_STATE_WAIT_FLAG;
curhttp->state_deets = 0;
}
break;
case HTTP_STATE_DATA_XFER:
//Ignore any further data?
curlen = 0;
break;
case HTTP_STATE_DATA_WEBSOCKET:
WebSocketGotData( c );
break;
case HTTP_WAIT_CLOSE:
if( curhttp->keep_alive )
{
curhttp->state = HTTP_STATE_WAIT_METHOD;
}
else
{
HTTPClose( );
}
break;
default:
break;
};
}
}
static void DoHTTP( uint8_t timed )
{
switch( curhttp->state )
{
case HTTP_STATE_NONE: //do nothing if no state.
curhttp->send_pending = 0;
break;
case HTTP_STATE_DATA_XFER:
curhttp->send_pending = 1;
if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend
{
if( curhttp->is_dynamic )
{
HTTPCustomCallback( );
}
else
{
HTTPHandleInternalCallback( );
}
}
break;
case HTTP_WAIT_CLOSE:
curhttp->send_pending = 0;
if( TCPDoneSend( curhttp->socket ) )
{
if( curhttp->keep_alive )
{
curhttp->state = HTTP_STATE_WAIT_METHOD;
}
else
{
HTTPClose( );
}
}
break;
case HTTP_STATE_DATA_WEBSOCKET:
curhttp->send_pending = 0;
if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend
{
WebSocketTickInternal();
}
break;
default:
if( timed )
{
if( curhttp->timeout++ > HTTP_SERVER_TIMEOUT )
{
HTTPClose( );
}
}
}
}
void HTTPTick( uint8_t timed )
{
uint8_t i;
for( i = 0; i < HTTP_CONNECTIONS; i++ )
{
if( curhttp ) { HTDEBUG( "HTTPRXQ\n" ); break; }
curhttp = &HTTPConnections[i];
DoHTTP( timed );
curhttp = 0;
}
}
void HTTPHandleInternalCallback( )
{
uint16_t i, bytestoread;
if( curhttp->isdone )
{
HTTPClose( );
return;
}
if( curhttp->is404 )
{
DataStartPacket();
PushString("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\nFile not found.");
EndTCPWrite( curhttp->socket );
curhttp->isdone = 1;
return;
}
if( curhttp->isfirst )
{
char stto[10];
uint8_t slen = strlen( curhttp->pathbuffer );
const char * k;
DataStartPacket();;
//TODO: Content Length? MIME-Type?
PushString("HTTP/1.1 200 Ok\r\n");
#ifdef CUSTOM_HTTPHEADER_CODE
CUSTOM_HTTPHEADER_CODE
#endif
if( curhttp->bytesleft < 0xfffffffe )
{
PushString("Connection: "ISKEEPALIVE"\r\nContent-Length: ");
Uint32To10Str( stto, curhttp->bytesleft );
PushBlob( stto, strlen( stto ) );
curhttp->keep_alive = 1;
}
else
{
PushString("Connection: close");
curhttp->keep_alive = 0;
}
PushString( "\r\nContent-Type: " );
//Content-Type?
while( slen && ( curhttp->pathbuffer[--slen] != '.' ) );
k = &curhttp->pathbuffer[slen+1];
if( strcmp( k, "mp3" ) == 0 ) PushString( "audio/mpeg3" );
else if( strcmp( k, "jpg" ) == 0 ) PushString( "image/jpeg" );
else if( strcmp( k, "png" ) == 0 ) PushString( "image/png" );
else if( strcmp( k, "css" ) == 0 ) PushString( "text/css" );
else if( strcmp( k, "js" ) == 0 ) PushString( "text/javascript" );
else if( strcmp( k, "gz" ) == 0 ) PushString( "text/plain\r\nContent-Encoding: gzip\r\nCache-Control: public, max-age=3600" );
else if( curhttp->bytesleft == 0xfffffffe ) PushString( "text/plain" );
else PushString( "text/html" );
if( curhttp->is_gzip ) PushString( "\r\nContent-Encoding: gzip" );
PushString( "\r\n\r\n" );
EndTCPWrite( curhttp->socket );
curhttp->isfirst = 0;
return;
}
DataStartPacket();
for( i = 0; i < 2 && curhttp->bytesleft; i++ )
{
int bpt = curhttp->bytesleft;
if( bpt > MFS_SECTOR ) bpt = MFS_SECTOR;
curhttp->bytesleft = MFSReadSector( databuff_ptr, &curhttp->data.filedescriptor );
databuff_ptr += bpt;
}
EndTCPWrite( curhttp->socket );
if( !curhttp->bytesleft )
curhttp->isdone = 1;
}
void InternalStartHTTP( )
{
int32_t clusterno;
int8_t i;
char * path = &curhttp->pathbuffer[0];
curhttp->is_gzip = 0;
if( curhttp->pathbuffer[0] == '/' )
path++;
if( path[0] == 'd' && path[1] == '/' )
{
curhttp->is_dynamic = 1;
curhttp->isfirst = 1;
curhttp->isdone = 0;
curhttp->is404 = 0;
HTTPCustomStart();
return;
}
if( !path[0] )
{
path = "index.html";
}
for( i = 0; path[i]; i++ )
if( path[i] == '?' ) path[i] = 0;
i = MFSOpenFile( path, &curhttp->data.filedescriptor );
curhttp->bytessofar = 0;
curhttp->is_gzip = 0;
if( i == MFS_FILE_COMPRESSED_MEMORY )
{
curhttp->is_gzip = 1;
}
else if( i < 0 )
{
HTDEBUG( "404(%s)\n", path );
curhttp->is404 = 1;
curhttp->isfirst = 1;
curhttp->isdone = 0;
curhttp->is_dynamic = 0;
curhttp->bytesleft = 0;
return;
}
curhttp->isfirst = 1;
curhttp->isdone = 0;
curhttp->is404 = 0;
curhttp->is_dynamic = 0;
curhttp->bytesleft = curhttp->data.filedescriptor.filelen;
}
void http_disconnetcb(int conn ) {
int r = conn;
if( r>=0 )
{
if( !HTTPConnections[r].is_dynamic ) MFSClose( &HTTPConnections[r].data.filedescriptor );
HTTPConnections[r].state = 0;
}
}
void http_recvcb(int conn, char *pusrdata, unsigned short length)
{
int whichhttp = conn;
//Though it might be possible for this to interrupt the other
//tick task, I don't know if this is actually a probelem.
//I'm adding this back-up-the-register just in case.
if( curhttp ) { HTDEBUG( "Unexpected Race Condition\n" ); return; }
curhttp = &HTTPConnections[whichhttp];
curdata = (uint8_t*)pusrdata;
curlen = length;
HTTPGotData();
curhttp = 0 ;
}
int httpserver_connectcb( int socket )
{
int i;
for( i = 0; i < HTTP_CONNECTIONS; i++ )
{
if( HTTPConnections[i].state == 0 )
{
HTTPConnections[i].socket = socket;
HTTPConnections[i].state = HTTP_STATE_WAIT_METHOD;
break;
}
}
if( i == HTTP_CONNECTIONS )
{
return -1;
}
return i;
}
int URLDecode( char * decodeinto, int maxlen, const char * buf )
{
int i = 0;
for( ; buf && *buf; buf++ )
{
char c = *buf;
if( c == '+' )
{
decodeinto[i++] = ' ';
}
else if( c == '?' || c == '&' )
{
break;
}
else if( c == '%' )
{
if( *(buf+1) && *(buf+2) )
{
decodeinto[i++] = hex2byte( buf+1 );
buf += 2;
}
}
else
{
decodeinto[i++] = c;
}
if( i >= maxlen -1 ) break;
}
decodeinto[i] = 0;
return i;
}
#ifndef RD_SHA1_HASH_LEN
#define RD_SHA1_HASH_LEN RD_SHA1_DIGEST_SIZE
#endif
void WebSocketGotData( uint8_t c )
{
switch( curhttp->state_deets )
{
case 0:
{
int i = 0;
char inkey[120];
unsigned char hash[RD_SHA1_HASH_LEN];
RD_SHA1_CTX c;
int inkeylen = 0;
curhttp->is_dynamic = 1;
while( curlen > 20 )
{
curdata++; curlen--;
if( strncmp( curdata, "Sec-WebSocket-Key: ", 19 ) == 0 )
{
break;
}
}
if( curlen <= 21 )
{
HTDEBUG( "No websocket key found.\n" );
curhttp->state = HTTP_WAIT_CLOSE;
return;
}
curdata+= 19;
curlen -= 19;
#define WS_KEY_LEN 36
#define WS_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_RETKEY_SIZEM1 32
while( curlen > 1 )
{
uint8_t lc = *(curdata++);
inkey[i] = lc;
curlen--;
if( lc == '\r' )
{
inkey[i] = 0;
break;
}
i++;
if( i >= sizeof( inkey ) - WS_KEY_LEN - 5 )
{
HTDEBUG( "Websocket key too big.\n" );
curhttp->state = HTTP_WAIT_CLOSE;
return;
}
}
if( curlen <= 1 )
{
HTDEBUG( "Invalid websocket key found.\n" );
curhttp->state = HTTP_WAIT_CLOSE;
return;
}
if( i + WS_KEY_LEN + 1 >= sizeof( inkey ) )
{
HTDEBUG( "WSKEY Too Big.\n" );
curhttp->state = HTTP_WAIT_CLOSE;
return;
}
memcpy( &inkey[i], WS_KEY, WS_KEY_LEN + 1 );
i += WS_KEY_LEN;
RD_SHA1_Init( &c );
RD_SHA1_Update( &c, inkey, i );
RD_SHA1_Final( hash, &c );
#if (WS_RETKEY_SIZE > MAX_HTTP_PATHLEN - 10 )
#error MAX_HTTP_PATHLEN too short.
#endif
my_base64_encode( hash, RD_SHA1_HASH_LEN, curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) );
curhttp->bytessofar = 0;
curhttp->bytesleft = 0;
NewWebSocket();
//Respond...
curhttp->state_deets = 1;
break;
}
case 1:
if( c == '\n' ) curhttp->state_deets = 2;
break;
case 2:
if( c == '\r' ) curhttp->state_deets = 3;
else curhttp->state_deets = 1;
break;
case 3:
if( c == '\n' ) curhttp->state_deets = 4;
else curhttp->state_deets = 1;
break;
case 5: //Established connection.
{
//XXX TODO: Seems to malfunction on large-ish packets. I know it has problems with 140-byte payloads.
do
{
if( curlen < 5 ) //Can't interpret packet.
break;
uint8_t fin = c & 1;
uint8_t opcode = c << 4;
uint16_t payloadlen = *(curdata++);
curlen--;
if( !(payloadlen & 0x80) )
{
HTDEBUG( "Unmasked packet (%d)\n", payloadlen );
curhttp->state = HTTP_WAIT_CLOSE;
break;
}
if( opcode == 128 )
{
//Close connection.
//HTDEBUG( "CLOSE\n" );
//curhttp->state = HTTP_WAIT_CLOSE;
//break;
}
payloadlen &= 0x7f;
if( payloadlen == 127 )
{
//Very long payload.
//Not supported.
HTDEBUG( "Unsupported payload packet.\n" );
curhttp->state = HTTP_WAIT_CLOSE;
break;
}
else if( payloadlen == 126 )
{
payloadlen = (curdata[0] << 8) | curdata[1];
curdata += 2;
curlen -= 2;
}
wsmask[0] = curdata[0];
wsmask[1] = curdata[1];
wsmask[2] = curdata[2];
wsmask[3] = curdata[3];
curdata += 4;
curlen -= 4;
wsmaskplace = 0;
//XXX Warning: When packets get larger, they may split the
//websockets packets into multiple parts. We could handle this
//but at the cost of prescious RAM. I am chosing to just drop those
//packets on the floor, and restarting the connection.
if( curlen < payloadlen )
{
extern int cork_binary_rx;
cork_binary_rx = 1;
//HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen );
//curhttp->state = HTTP_WAIT_CLOSE;
HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen );
curhttp->state = HTTP_WAIT_CLOSE;
return;
}
char * newcurdata = curdata + payloadlen + 1;
WebSocketData( payloadlen );
curlen -= payloadlen;
curdata = newcurdata;
}
while( curlen > 5 );
break;
}
default:
break;
}
}
void WebSocketTickInternal()
{
switch( curhttp->state_deets )
{
case 4: //Has key full HTTP header, etc. wants response.
DataStartPacket();;
PushString( "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " );
PushString( curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) );
PushString( "\r\n\r\n" );
EndTCPWrite( curhttp->socket );
curhttp->state_deets = 5;
curhttp->keep_alive = 0;
break;
case 5:
WebSocketTick();
break;
}
}
void WebSocketSend( uint8_t * data, int size )
{
DataStartPacket();;
PushByte( 0x82 ); //0x81 is text.
if( size >= 126 )
{
PushByte( 0x00 | 126 );
PushByte( size>>8 );
PushByte( size&0xff );
}
else
{
PushByte( 0x00 | size );
}
PushBlob( data, size );
EndTCPWrite( curhttp->socket );
curhttp->send_pending = 1;
}
uint8_t WSPOPMASK()
{
uint8_t mask = wsmask[wsmaskplace];
wsmaskplace = (wsmaskplace+1)&3;
return (*curdata++)^(mask);
}
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#define socklen_t uint32_t
#define SHUT_RDWR SD_BOTH
#define MSG_NOSIGNAL 0
#else
#define closesocket close
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <unistd.h>
#include <sys/time.h>
uint16_t htons(uint16_t hostshort);
#endif
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
static int serverSocket;
uint8_t * databuff_ptr;
uint8_t databuff[1536];
int cork_binary_rx;
#ifndef HTTP_POLL_TIMEOUT
#define HTTP_POLL_TIMEOUT 0 //This is a little weird, it's an adaptation from cnhttp.
#endif
#define DESTROY_SOCKETS_LIST 200
int destroy_sockets[DESTROY_SOCKETS_LIST];
int destroy_socket_head = 0;
int sockets[HTTP_CONNECTIONS];
void et_espconn_disconnect( int socket )
{
shutdown( socket, SHUT_RDWR );
int i;
//printf( "Shut: %d\n", socket );
for( i = 0; i < HTTP_CONNECTIONS; i++ )
{
if( sockets[i] == socket )
{
http_disconnetcb( i );
sockets[i] = 0;
}
}
if( destroy_sockets[destroy_socket_head] ) closesocket( destroy_sockets[destroy_socket_head] );
destroy_sockets[destroy_socket_head] = socket;
destroy_socket_head = (destroy_socket_head+1)%DESTROY_SOCKETS_LIST;
}
void DataStartPacket()
{
databuff_ptr = databuff;
}
void PushByte( uint8_t c )
{
if( databuff_ptr - databuff + 1 >= sizeof( databuff ) ) return;
*(databuff_ptr++) = c;
}
void PushBlob( const uint8_t * data, int len )
{
if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return;
memcpy( databuff_ptr, data, len );
databuff_ptr += len;
}
void PushString( const char * data )
{
int len = strlen( data );
if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return;
memcpy( databuff_ptr, data, len );
databuff_ptr += len;
}
int TCPCanSend( int socket, int size )
{
fd_set write_fd_set;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&write_fd_set);
FD_SET (socket, &write_fd_set);
int r = select (FD_SETSIZE, NULL, &write_fd_set, NULL, &tv);
if (r < 0)
{
perror ("select");
return -1;
}
return r;
}
int TCPCanRead( int sock )
{
fd_set read_fd_set;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&read_fd_set);
FD_SET (sock, &read_fd_set);
int r;
r = select (FD_SETSIZE, &read_fd_set, NULL, NULL, &tv);
if (r < 0)
{
perror ("select");
return -1;
}
return r;
}
int TCPException( int sock )
{
int error_code;
int error_code_size = sizeof(error_code);
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error_code, &error_code_size);
if( error_code >= 0 ) return 0;
else return 1;
}
int TCPDoneSend( int socket )
{
return TCPCanSend( socket, 1 );
}
int EndTCPWrite( int socket )
{
int r = send( socket, databuff, databuff_ptr-databuff, MSG_NOSIGNAL );
databuff_ptr = databuff;
return r;
}
void TermHTTPServer()
{
shutdown( serverSocket, SHUT_RDWR );
}
int TickHTTP()
{
int i;
//struct pollfd allpolls[HTTP_CONNECTIONS+1];
short mappedhttp[HTTP_CONNECTIONS+1];
if( serverSocket == 0 ) return -1;
do
{
static double last;
double now;
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
static LARGE_INTEGER lpf;
LARGE_INTEGER li;
if( !lpf.QuadPart )
{
QueryPerformanceFrequency( &lpf );
}
QueryPerformanceCounter( &li );
now = (double)li.QuadPart / (double)lpf.QuadPart;
#else
struct timeval tv;
gettimeofday( &tv, 0 );
now = ((double)tv.tv_usec)/1000000. + (tv.tv_sec);
#endif
double dl = now - last;
if( dl > .1 )
{
int i;
HTTPTick( 1 );
last = now;
}
else
{
HTTPTick( 0 );
}
/* int pollct = 1;
allpolls[0].fd = serverSocket;
allpolls[0].events = LISTENPOLL;
for( i = 0; i < HTTP_CONNECTIONS;i ++)
{
if( !sockets[i] || HTTPConnections[i].state == 0 ) continue;
allpolls[pollct].fd = sockets[i];
allpolls[pollct].events = POLLIN | (HTTPConnections[i].send_pending?POLLOUT:0);
mappedhttp[pollct] = i;
pollct++;
}
//Do something to watch all currently-waiting sockets.
poll( allpolls, pollct, HTTP_POLL_TIMEOUT );
*/
//If there's faults, bail.
if( TCPException( serverSocket ) )
{
closesocket( serverSocket );
for( i = 0; i < HTTP_CONNECTIONS;i ++)
{
if( sockets[i] ) closesocket( sockets[i] );
}
break;
}
if( TCPCanRead( serverSocket ) )
{
struct sockaddr_in tin;
socklen_t addrlen = sizeof(tin);
memset( &tin, 0, addrlen );
int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen );
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
struct linger lx;
lx.l_onoff = 1;
lx.l_linger = 0;
//Disable the linger here, too.
setsockopt( tsocket, SOL_SOCKET, SO_LINGER, (const char*)&lx, sizeof( lx ) );
#else
struct linger lx;
lx.l_onoff = 1;
lx.l_linger = 0;
//Disable the linger here, too.
setsockopt( tsocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) );
#endif
int r = httpserver_connectcb( tsocket );
if( r == -1 )
{
closesocket( tsocket );
}
else
{
sockets[r] = tsocket;
}
}
for( i = 0; i < HTTP_CONNECTIONS; i++)
{
int wc = i;
if( !sockets[i] || HTTPConnections[i].state == 0 ) continue;
if( TCPException(sockets[i]) )
{
http_disconnetcb( wc );
closesocket( sockets[wc] );
sockets[wc] = 0;
}
else if( TCPCanRead( sockets[i] ) )
{
int dco = HTTPConnections[i].corked_data_place;
uint8_t data[8192];
memcpy( data, HTTPConnections[i].corked_data, dco );
int len = recv( sockets[wc], data+dco, 8192-dco, 0 );
if( len )
{
cork_binary_rx = 0;
http_recvcb( wc, data, len+dco );
if( cork_binary_rx )
{
int to_cork = len;
if( to_cork > sizeof( HTTPConnections[i].corked_data ) + HTTPConnections[i].corked_data_place )
{
http_disconnetcb( wc );
closesocket ( sockets[wc] );
sockets[wc] = 0;
fprintf( stderr, "Error: too much data to buffer on websocket\n" );
}
else
{
memcpy( HTTPConnections[i].corked_data + dco, data + dco, to_cork );
HTTPConnections[i].corked_data_place += to_cork;
}
}
else
{
HTTPConnections[i].corked_data_place = 0;
}
}
else
{
http_disconnetcb( wc );
closesocket( sockets[wc] );
sockets[wc] = 0;
}
}
}
}
#if HTTP_POLL_TIMEOUT != 0
while(1);
#else
while(0);
#endif
return 0;
}
int RunHTTP( int port )
{
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
fprintf( stderr, "WSAStartup failed with error: %d\n", err);
return 1;
}
}
#endif
int acceptfaults = 0;
struct sockaddr_in sin;
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
//Make sure the socket worked.
if( serverSocket == -1 )
{
fprintf( stderr, "Error: Cannot create socket.\n" );
return -1;
}
//Disable SO_LINGER (Well, enable it but turn it way down)
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
struct linger lx;
lx.l_onoff = 1;
lx.l_linger = 0;
setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) );
//Enable SO_REUSEADDR
int reusevar = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar));
#else
struct linger lx;
lx.l_onoff = 1;
lx.l_linger = 0;
setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) );
//Enable SO_REUSEADDR
int reusevar = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar));
#endif
//Setup socket for listening address.
memset( &sin, 0, sizeof( sin ) );
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons( port );
//Actually bind to the socket
if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 )
{
fprintf( stderr, "Could not bind to socket: %d\n", port );
closesocket( serverSocket );
serverSocket = 0;
return -1;
}
//Finally listen.
if( listen( serverSocket, 5 ) == -1 )
{
fprintf(stderr, "Could not lieten to socket.");
closesocket( serverSocket );
serverSocket = 0;
return -1;
}
return 0;
}
void Uint32To10Str( char * out, uint32_t dat )
{
int tens = 1000000000;
int val;
int place = 0;
while( tens > 1 )
{
if( dat/tens ) break;
tens/=10;
}
while( tens )
{
val = dat/tens;
dat -= val*tens;
tens /= 10;
out[place++] = val + '0';
}
out[place] = 0;
}
//from http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
static const char encoding_table[] = {'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', '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', '+', '/'};
static const int mod_table[] = {0, 2, 1};
void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data )
{
int i, j;
int output_length = 4 * ((input_length + 2) / 3);
if( !encoded_data ) return;
if( !data ) { encoded_data[0] = '='; encoded_data[1] = 0; return; }
for (i = 0, j = 0; i < input_length; ) {
uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
}
for (i = 0; i < mod_table[input_length % 3]; i++)
encoded_data[output_length - 1 - i] = '=';
encoded_data[j] = 0;
}
uint8_t hex1byte( char c )
{
if( c >= '0' && c <= '9' ) return c - '0';
if( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
if( c >= 'A' && c <= 'F' ) return c - 'A' + 10;
return 0;
}
uint8_t hex2byte( const char * c )
{
return (hex1byte(c[0])<<4) | (hex1byte(c[1]));
}
#include <string.h>
#ifdef USE_RAM_MFS
unsigned char webpage_buffer[] = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xbd, 0x3b, 0x6b, 0x73, 0xda, 0xc8, 0xb2, 0x9f, 0xe1, 0x57, 0x4c, 0xb8, 0x75, 0x37, 0x60, 0x83, 0xc4, 0xc3, 0x76, 0xbc, 0xc6, 0x76,
0x15, 0xb6, 0x71, 0xe2, 0x73, 0x6c, 0xc7, 0xd7, 0xe0, 0x4d, 0x52, 0xa9, 0x14, 0x25, 0xa4, 0x01, 0x69, 0x2d, 0x24, 0x56, 0x0f, 0x03, 0xd9, 0xf8, 0xbf, 0x9f, 0xee, 0x9e, 0xd1, 0x13, 0x61, 0x9c,
0x3d, 0xbb, 0x97, 0xc4, 0x20, 0xcd, 0xf4, 0xf4, 0xf4, 0x6b, 0xfa, 0x31, 0x1a, 0xb1, 0xe3, 0x37, 0x17, 0x1f, 0xcf, 0x87, 0x5f, 0xee, 0xfa, 0xcc, 0x0c, 0x66, 0xf6, 0x69, 0xf9, 0x58, 0xfc, 0x94,
0x8e, 0x4d, 0xae, 0x19, 0xf0, 0x5b, 0x3a, 0x9e, 0xf1, 0x40, 0x63, 0xba, 0xa9, 0x79, 0x3e, 0x0f, 0x4e, 0x2a, 0x0f, 0xc3, 0xcb, 0xc6, 0x61, 0x85, 0x3a, 0x02, 0x2b, 0xb0, 0xf9, 0xa9, 0xee, 0x98,
0x41, 0x30, 0x3f, 0x56, 0xc5, 0x1d, 0xb6, 0xfb, 0xc1, 0x4a, 0x5c, 0x95, 0xc6, 0xae, 0xb1, 0x62, 0x7f, 0xb2, 0xb1, 0xa6, 0x3f, 0x4e, 0x3d, 0x37, 0x74, 0x8c, 0x86, 0xee, 0xda, 0xae, 0x77, 0xc4,
0xfe, 0xa7, 0x09, 0x9f, 0xc3, 0x66, 0x97, 0x3d, 0x23, 0x98, 0xe1, 0xea, 0xe1, 0x8c, 0x3b, 0x01, 0x3b, 0x62, 0x7f, 0xba, 0x4f, 0xdc, 0x9b, 0xd8, 0xee, 0xe2, 0x88, 0x99, 0x96, 0x61, 0x70, 0xa7,
0xcb, 0x16, 0x96, 0x11, 0x98, 0x27, 0xad, 0x66, 0xf3, 0x7f, 0xbb, 0xcc, 0xe4, 0xd6, 0xd4, 0x0c, 0xc4, 0x0d, 0x0e, 0x3d, 0x56, 0xa3, 0xc9, 0x8e, 0x55, 0x49, 0xf0, 0x31, 0x4d, 0x4a, 0xcd, 0x27,
0x15, 0x01, 0x7f, 0x44, 0xf0, 0x33, 0xcd, 0x9b, 0x5a, 0xce, 0x11, 0x4c, 0x3a, 0xd7, 0x0c, 0xc3, 0x72, 0xa6, 0x47, 0x4d, 0xc1, 0x87, 0x61, 0x3d, 0xb1, 0xc1, 0xf0, 0xcb, 0x75, 0xff, 0xa4, 0x32,
0x77, 0x7d, 0x2b, 0x70, 0x9d, 0x23, 0x6d, 0xec, 0xbb, 0x76, 0x18, 0xf0, 0xee, 0xf7, 0x86, 0xe5, 0x18, 0x7c, 0x79, 0xd4, 0x21, 0xd0, 0xd2, 0xb1, 0xae, 0x39, 0x4f, 0x9a, 0x2f, 0x69, 0xda, 0x6f,
0x36, 0x23, 0x8a, 0xf0, 0xd2, 0x32, 0x4e, 0x44, 0xf7, 0xe9, 0xb1, 0x2a, 0x2f, 0x88, 0x44, 0xc0, 0x5f, 0x38, 0x8f, 0x95, 0x9e, 0x68, 0xec, 0x06, 0x81, 0x3b, 0x03, 0xea, 0x08, 0xf5, 0xd1, 0xc1,
0x5e, 0x33, 0x9e, 0xbb, 0xd5, 0xec, 0x0a, 0xb1, 0x2d, 0x4c, 0x0b, 0x20, 0xb5, 0x23, 0x8f, 0x1b, 0x5d, 0xc9, 0xd9, 0x7e, 0x73, 0xbe, 0xac, 0x9c, 0x1e, 0x5b, 0xce, 0x3c, 0x0c, 0x58, 0xb0, 0x9a,
0xf3, 0x13, 0x3f, 0x1c, 0xcf, 0xac, 0x80, 0x3d, 0x69, 0x76, 0x08, 0x02, 0x98, 0xf8, 0x15, 0xe6, 0x3a, 0xba, 0x6d, 0xe9, 0x8f, 0x27, 0x95, 0xa1, 0x3b, 0x9d, 0xda, 0xfc, 0x72, 0x50, 0xad, 0x75,
0x61, 0x0c, 0x92, 0x03, 0x24, 0xfb, 0x81, 0x16, 0x84, 0x48, 0x32, 0x92, 0x99, 0x10, 0xeb, 0xeb, 0x9e, 0x35, 0x0f, 0x4e, 0xcb, 0x36, 0x0f, 0x98, 0xc7, 0x81, 0x0e, 0x0f, 0x24, 0xc6, 0x4e, 0xd8,
0x44, 0xb3, 0x7d, 0xde, 0xa5, 0xe6, 0x49, 0x68, 0xdb, 0x00, 0xc6, 0xb9, 0xb3, 0xa9, 0x1d, 0x95, 0xe9, 0x59, 0x06, 0x87, 0x7e, 0x07, 0x1a, 0xbb, 0xe5, 0x49, 0xe8, 0xe8, 0xc8, 0x37, 0x4b, 0x48,
0x01, 0xfb, 0x28, 0x1c, 0xf0, 0x66, 0xbd, 0x15, 0xcd, 0xa5, 0xac, 0xaa, 0xe7, 0xae, 0x33, 0xb1, 0xa6, 0xa1, 0xc7, 0xd9, 0x27, 0x3e, 0x7e, 0x7f, 0xcd, 0x06, 0x41, 0x38, 0x99, 0xb0, 0xaa, 0x66,
0x83, 0xd9, 0xb0, 0xc0, 0x65, 0x63, 0x0e, 0x1a, 0xf6, 0x02, 0xe6, 0x4e, 0xd8, 0xd4, 0x76, 0xc7, 0x9a, 0xcd, 0x74, 0xd7, 0x09, 0xf8, 0x32, 0xa8, 0x11, 0x79, 0x52, 0x85, 0x27, 0x2c, 0x32, 0x3b,
0x65, 0xca, 0x83, 0xbe, 0xcd, 0xf1, 0xf2, 0x6c, 0x75, 0x65, 0x54, 0xdf, 0x0a, 0x88, 0xb7, 0x35, 0xc1, 0xce, 0x62, 0x6a, 0x03, 0xb0, 0x68, 0x43, 0xd0, 0x73, 0x81, 0xac, 0xfa, 0x76, 0xc1, 0xc7,
0x53, 0x1b, 0xa1, 0xac, 0x49, 0x95, 0xbd, 0x41, 0xb0, 0x5a, 0xf9, 0xcf, 0x72, 0x49, 0x55, 0xff, 0xa5, 0x39, 0x8f, 0x2b, 0xd6, 0x00, 0xc1, 0xb3, 0x4b, 0xcb, 0xe3, 0x13, 0x77, 0xc9, 0x0e, 0x3b,
0x75, 0x30, 0x9a, 0xc0, 0x64, 0xb7, 0xbf, 0x5d, 0x5d, 0x5c, 0xf5, 0xd8, 0xfb, 0xbb, 0x87, 0x3a, 0x5b, 0xb9, 0x21, 0x73, 0x38, 0x37, 0x90, 0x68, 0xcd, 0x7f, 0x64, 0xc1, 0xc2, 0xd2, 0xb9, 0x52,
0x2e, 0x6d, 0x9f, 0xf1, 0x39, 0xa2, 0x6c, 0x60, 0x6a, 0xa0, 0x9a, 0x48, 0xbe, 0x4c, 0x55, 0x07, 0x81, 0xe6, 0x18, 0x9a, 0x67, 0xb0, 0x89, 0xad, 0x01, 0xaf, 0x68, 0x37, 0xcc, 0x27, 0xa0, 0x68,
0x48, 0xef, 0xec, 0xb7, 0x18, 0x1e, 0x3e, 0xaa, 0xda, 0xf3, 0x3c, 0x6d, 0xc5, 0xc6, 0x20, 0x44, 0xc0, 0x34, 0x01, 0x78, 0x10, 0x77, 0x00, 0x94, 0xf8, 0xc9, 0x88, 0xf3, 0x2d, 0x23, 0x68, 0x1e,
0x5f, 0x89, 0x06, 0x3c, 0x7c, 0xbe, 0xbc, 0xbf, 0x49, 0x86, 0xa8, 0xea, 0x83, 0x63, 0x01, 0xd8, 0x8c, 0xd9, 0xae, 0xae, 0x91, 0xfe, 0x71, 0x50, 0xe0, 0x69, 0x8e, 0x4f, 0xcd, 0xd0, 0x00, 0xcb,
0xc0, 0x32, 0x24, 0x1e, 0x54, 0xf2, 0x43, 0x60, 0xd9, 0x56, 0x80, 0x0b, 0x19, 0x95, 0x8b, 0xe0, 0x42, 0xd9, 0xbe, 0x66, 0x22, 0xc3, 0x60, 0x17, 0x84, 0x48, 0x49, 0x4c, 0x0a, 0xe6, 0x1d, 0xcd,
0xb4, 0x47, 0x2e, 0x44, 0x52, 0x25, 0x2e, 0x86, 0x20, 0xb6, 0x3a, 0x9b, 0x78, 0xda, 0x14, 0xaf, 0x84, 0x7e, 0x90, 0x46, 0xec, 0x03, 0xfa, 0x60, 0x88, 0x42, 0x98, 0xa2, 0x41, 0xd8, 0xf0, 0x5b,
0xff, 0x7e, 0xd8, 0xff, 0x3c, 0x1a, 0x7c, 0xe8, 0x5d, 0xf4, 0xef, 0x41, 0xd6, 0xa8, 0x0e, 0x45, 0x88, 0x70, 0xe0, 0x86, 0x9e, 0xce, 0xab, 0x38, 0xba, 0x1e, 0xe3, 0x67, 0x11, 0x8c, 0xee, 0xce,
0xe6, 0x96, 0x1d, 0xa1, 0xc2, 0x6e, 0xec, 0xb1, 0xc0, 0x34, 0xd1, 0x38, 0x50, 0x91, 0xa2, 0xeb, 0x4e, 0xf3, 0x34, 0x70, 0xaa, 0x12, 0xa6, 0x4e, 0x54, 0x9c, 0x7f, 0xbc, 0xb9, 0xbb, 0xba, 0xee,
0x8f, 0x06, 0xc3, 0xde, 0xf0, 0x61, 0x50, 0x83, 0x55, 0x81, 0xee, 0x46, 0xb3, 0x01, 0xa0, 0x9a, 0x19, 0x7c, 0xe5, 0x4c, 0xdc, 0x6b, 0x77, 0x2a, 0xd0, 0x23, 0x7e, 0x58, 0x12, 0xc4, 0x12, 0x32,
0xb9, 0x89, 0xa5, 0xcb, 0xfb, 0xde, 0xfb, 0x9b, 0xfe, 0xed, 0xf0, 0x25, 0xa6, 0x70, 0x7c, 0x5a, 0x54, 0x85, 0x4c, 0x61, 0xf7, 0x36, 0xa6, 0x04, 0xa2, 0xbf, 0xc4, 0x14, 0xa1, 0x17, 0x4c, 0x95,
0x84, 0xe3, 0xc9, 0x6a, 0xe9, 0xce, 0x73, 0xa7, 0x30, 0x4d, 0x35, 0x22, 0x4e, 0x0b, 0x02, 0x4d, 0x37, 0x25, 0x6d, 0x00, 0x2c, 0xe8, 0xdf, 0xdc, 0x1b, 0xe9, 0x04, 0x7b, 0x6d, 0xcb, 0x79, 0x8c,
0xf0, 0x41, 0x67, 0xd4, 0x3c, 0x06, 0x9f, 0xdb, 0x0b, 0x02, 0xcf, 0x1a, 0x5f, 0x4b, 0x5b, 0xad, 0x32, 0x1a, 0xdb, 0xac, 0xb3, 0x8a, 0xd6, 0xac, 0xb0, 0xad, 0x80, 0x2d, 0x04, 0x6c, 0x09, 0x40,
0x68, 0x08, 0x3d, 0x07, 0xdb, 0x71, 0xcd, 0x96, 0x85, 0x7b, 0xf8, 0xc4, 0x61, 0x1d, 0x68, 0xb0, 0xea, 0x17, 0xae, 0x5c, 0x9a, 0x7e, 0x1d, 0x96, 0x00, 0x67, 0x96, 0xcf, 0x34, 0xb1, 0x10, 0x1a,
0xe9, 0x85, 0x5b, 0x27, 0xf3, 0x9f, 0xb9, 0x3e, 0x08, 0x44, 0x5b, 0x18, 0xf0, 0xc7, 0xdc, 0xf1, 0xef, 0x5c, 0x0f, 0x7c, 0xe1, 0x2b, 0x62, 0x1f, 0x90, 0x5f, 0x01, 0x20, 0xed, 0x4a, 0x28, 0x57,
0xde, 0x13, 0xd7, 0xf7, 0xd8, 0x72, 0xe2, 0xcd, 0xba, 0x4c, 0x23, 0xaa, 0x21, 0xe8, 0x60, 0x63, 0x87, 0x69, 0xcd, 0x5c, 0xd3, 0x1e, 0xd3, 0x5a, 0x5d, 0x08, 0x20, 0xde, 0x0a, 0x5d, 0x3e, 0x35,
0x3c, 0xe9, 0xd0, 0xe0, 0xc2, 0x02, 0x9d, 0x69, 0x96, 0x43, 0x3e, 0x1b, 0xa6, 0xba, 0x93, 0x41, 0x0c, 0x66, 0x46, 0xa0, 0x2a, 0x60, 0x52, 0x96, 0xab, 0x1d, 0x9c, 0x04, 0x7e, 0x77, 0xe9, 0xf7,
0xfb, 0xa2, 0x8e, 0xcd, 0xdf, 0x41, 0x7e, 0xca, 0x3e, 0x88, 0x04, 0x30, 0x01, 0x38, 0xe2, 0x7f, 0xae, 0xd4, 0x91, 0xbe, 0xb9, 0xc7, 0x75, 0xcb, 0x47, 0x2c, 0x33, 0x6e, 0x58, 0xe1, 0x6c, 0x0e,
0x8e, 0xcb, 0xd5, 0x82, 0xd7, 0xcc, 0x7f, 0x09, 0xba, 0x3e, 0x27, 0x39, 0x49, 0x02, 0x9e, 0x74, 0x98, 0xf7, 0xfb, 0xa2, 0x86, 0xc8, 0x51, 0xfc, 0x24, 0x9d, 0xc8, 0x17, 0x49, 0x83, 0x93, 0x9e,
0x28, 0xd6, 0x59, 0x2c, 0x3f, 0xd0, 0x1a, 0x52, 0x2c, 0x07, 0x62, 0x94, 0x21, 0xc3, 0x67, 0x81, 0xc9, 0x23, 0x2d, 0x09, 0x71, 0x2b, 0xa1, 0x1f, 0x1b, 0x62, 0x3c, 0x5a, 0x0e, 0x7a, 0x0f, 0x06,
0xeb, 0xbb, 0x33, 0x4e, 0x96, 0xc6, 0x97, 0xaa, 0x50, 0xa3, 0x70, 0x94, 0xa0, 0x64, 0xf0, 0xf3, 0x18, 0xa7, 0xa7, 0x1c, 0x40, 0x02, 0x6f, 0xc5, 0x2c, 0x47, 0xa0, 0x14, 0x2e, 0x39, 0xb1, 0xf3,
0x33, 0x1a, 0x10, 0x99, 0xb9, 0x70, 0xbf, 0x45, 0xbd, 0xc2, 0x9a, 0xde, 0x42, 0x24, 0x0c, 0x7d, 0x94, 0x15, 0x5a, 0x54, 0x3c, 0x99, 0xef, 0xb2, 0xdf, 0x43, 0xb0, 0x19, 0xee, 0x68, 0x63, 0xc1,
0xc7, 0xac, 0xce, 0x1c, 0x77, 0x21, 0xb9, 0x10, 0xcd, 0xbf, 0x11, 0x9d, 0xc2, 0x8e, 0xc9, 0xab, 0x57, 0x9b, 0x91, 0x81, 0x6f, 0x02, 0x68, 0xc9, 0x89, 0xfb, 0x02, 0xaf, 0x66, 0xcf, 0x4d, 0x8d,
0xc1, 0x95, 0x83, 0x39, 0x55, 0x7a, 0x28, 0xad, 0xf1, 0xb3, 0xeb, 0xfe, 0xed, 0x45, 0xbc, 0x66, 0x10, 0xea, 0x12, 0x1c, 0x36, 0x75, 0x0d, 0xee, 0xcf, 0x47, 0xbd, 0xeb, 0xbb, 0x0f, 0x3d, 0xe1,
0x2d, 0x3e, 0xde, 0xf6, 0x47, 0x37, 0x57, 0xb7, 0x0f, 0x83, 0x51, 0xdc, 0x81, 0xe3, 0x20, 0x76, 0xc3, 0x92, 0xe0, 0x8a, 0x0d, 0xfe, 0x81, 0x62, 0x31, 0x86, 0x3f, 0x55, 0xbd, 0x70, 0x19, 0x45,
0x43, 0xb6, 0x70, 0x3d, 0x88, 0x9c, 0x26, 0x04, 0xbb, 0x19, 0x72, 0x6b, 0x6a, 0xf3, 0x39, 0x24, 0x24, 0x1c, 0x14, 0xb0, 0x42, 0x6f, 0x30, 0x83, 0x80, 0x8a, 0xf1, 0x44, 0x08, 0x0d, 0x62, 0x6d,
0x2c, 0x7c, 0x7f, 0xe5, 0x07, 0x7c, 0xa6, 0x40, 0xdf, 0xd0, 0x84, 0x95, 0x67, 0x42, 0xdc, 0xb4, 0xb9, 0x2f, 0xe5, 0x87, 0xd2, 0x04, 0xae, 0x80, 0x54, 0x13, 0xf3, 0x08, 0x70, 0x16, 0xdc, 0x57,
0x7d, 0x3e, 0xc5, 0x24, 0x01, 0x64, 0x0b, 0xb0, 0x91, 0xc8, 0x41, 0xac, 0x20, 0x03, 0x1b, 0x96, 0x10, 0x26, 0x5a, 0x30, 0x9b, 0xc8, 0x39, 0x9e, 0xe2, 0x9c, 0x27, 0x89, 0x50, 0xe7, 0xb7, 0x97,
0xef, 0xfb, 0x90, 0x9c, 0x9d, 0x41, 0x3e, 0x0c, 0x82, 0x18, 0x7a, 0x96, 0xe6, 0x40, 0x16, 0xe4, 0xff, 0x6b, 0x20, 0xa2, 0x95, 0x7f, 0x59, 0x97, 0x11, 0xf0, 0x4a, 0xf8, 0x2a, 0x1d, 0x72, 0x66,
0x19, 0xb5, 0x30, 0xc7, 0x48, 0x9a, 0x8e, 0x4f, 0x58, 0x93, 0xd5, 0x98, 0x70, 0x2f, 0x5d, 0x26, 0xa4, 0x14, 0x30, 0x6d, 0x2c, 0x0d, 0xa5, 0x77, 0x7f, 0xdf, 0xfb, 0x32, 0x3a, 0x7b, 0xb8, 0xbc,
0xec, 0xdf, 0xa7, 0xfc, 0x95, 0xb4, 0x1c, 0x6d, 0x5c, 0x97, 0x89, 0x40, 0xac, 0x18, 0xea, 0xb8, 0xd0, 0x02, 0x8d, 0x3a, 0x23, 0x62, 0xb0, 0xeb, 0xe2, 0xcb, 0x6d, 0xef, 0xe6, 0xea, 0x7c, 0x74,
0x71, 0xdf, 0xfb, 0x14, 0x81, 0x3f, 0xa5, 0x8c, 0xe2, 0xce, 0xb5, 0x1c, 0x74, 0xfb, 0xe0, 0x1f, 0x3b, 0x62, 0xc4, 0xe5, 0xf5, 0xc7, 0xde, 0xb0, 0x2e, 0x32, 0x42, 0xf2, 0x9b, 0xcd, 0xda, 0x0b,
0x34, 0x9c, 0x6f, 0xa0, 0xc1, 0xe1, 0x0b, 0xf6, 0x00, 0xa8, 0x0f, 0x85, 0xd5, 0x45, 0x82, 0x91, 0x60, 0xac, 0xf6, 0x73, 0xd4, 0x81, 0x53, 0xde, 0x13, 0x23, 0x1e, 0x6e, 0x07, 0x57, 0xef, 0x6f,
0xfb, 0x17, 0xa3, 0xb3, 0x2f, 0xc3, 0x3e, 0x2c, 0x49, 0x2f, 0xcc, 0x11, 0x89, 0x0e, 0x96, 0xe6, 0xf4, 0xc9, 0x4a, 0x87, 0xf7, 0x57, 0xbd, 0xdb, 0xf7, 0xd7, 0xfd, 0x01, 0x41, 0xa5, 0xb4, 0x02,
0xe0, 0x89, 0xaa, 0x85, 0xc4, 0xc8, 0xc9, 0x83, 0xad, 0x41, 0xe6, 0x12, 0xa0, 0x71, 0x44, 0x9a, 0xd7, 0x30, 0x95, 0x01, 0x3b, 0x94, 0xe9, 0x11, 0xf6, 0xa7, 0x12, 0xaa, 0xd4, 0x00, 0xe1, 0x68,
0x44, 0x76, 0x04, 0x51, 0x18, 0x72, 0xdc, 0x68, 0x0c, 0x04, 0xe5, 0x6c, 0x0e, 0x36, 0x14, 0xfd, 0xd2, 0x6e, 0x69, 0xc8, 0x58, 0xa2, 0xc9, 0xe6, 0x61, 0x99, 0xd9, 0x5e, 0x93, 0x8a, 0x11, 0x1a,
0xc8, 0x17, 0xcb, 0x14, 0xaa, 0x34, 0x98, 0xc3, 0x71, 0x21, 0x95, 0x0c, 0x3d, 0xa8, 0x03, 0x02, 0x1b, 0x16, 0x4e, 0x38, 0x9f, 0xbb, 0x5e, 0x20, 0xad, 0x1f, 0xfc, 0xcc, 0x24, 0x58, 0x20, 0x94,
0x28, 0x13, 0xb8, 0xa7, 0x94, 0x37, 0xc4, 0x38, 0x84, 0x76, 0xe1, 0xcb, 0x13, 0x91, 0x4e, 0x72, 0x98, 0x89, 0x72, 0x11, 0x0b, 0x00, 0x0b, 0xdf, 0xe0, 0x71, 0x13, 0x71, 0xad, 0x47, 0xb7, 0xbf,
0x25, 0xb8, 0xb5, 0x59, 0xf0, 0xb7, 0x05, 0xb7, 0x40, 0x04, 0x37, 0x00, 0x90, 0xf1, 0xed, 0x55, 0xe1, 0x4d, 0x50, 0x10, 0xf1, 0xe2, 0x6b, 0xb3, 0x39, 0x64, 0x48, 0xed, 0x0b, 0x94, 0xcf, 0x96,
0xa8, 0x27, 0x25, 0xd8, 0xbe, 0xa8, 0xc2, 0x55, 0x3d, 0xd0, 0x6b, 0xca, 0xe2, 0xfb, 0x6a, 0xd9, 0x95, 0xd1, 0x2f, 0x67, 0x00, 0x9b, 0x03, 0x20, 0x02, 0x64, 0xc2, 0x5f, 0xc6, 0x63, 0x61, 0xef,
0xd5, 0x4c, 0x9b, 0xf2, 0x2b, 0x5c, 0x49, 0x60, 0xcb, 0xd5, 0x10, 0x97, 0xe5, 0x8c, 0xcf, 0x0c, 0x58, 0xab, 0x75, 0xb6, 0x84, 0x42, 0x06, 0x96, 0x56, 0x9d, 0x99, 0x89, 0xa7, 0x5a, 0x08, 0x0f,
0xf5, 0xe3, 0x07, 0x34, 0x66, 0x7d, 0x55, 0xb9, 0x28, 0x86, 0xe2, 0x14, 0x32, 0xaa, 0xdc, 0x60, 0x9a, 0x03, 0x1e, 0x17, 0x6d, 0x25, 0xb0, 0x20, 0x92, 0x2e, 0x38, 0x14, 0x6a, 0xce, 0xdb, 0x00,
0xfc, 0x2d, 0x8f, 0x18, 0x16, 0xd1, 0x4d, 0x34, 0x5b, 0x0e, 0xe8, 0x09, 0xa2, 0x22, 0x3a, 0x60, 0xa8, 0x65, 0xbd, 0x40, 0x91, 0x24, 0xc8, 0x35, 0x23, 0x2c, 0x9f, 0xd5, 0x4a, 0xf1, 0x22, 0x4a,
0x22, 0xa9, 0x5c, 0x43, 0x10, 0x4a, 0x99, 0xa4, 0x4b, 0x03, 0xbe, 0x9f, 0xe2, 0x76, 0x5a, 0xff, 0xfd, 0xcf, 0xc3, 0x87, 0xfb, 0x7e, 0x33, 0x8a, 0x45, 0x50, 0x66, 0xb7, 0x0d, 0x89, 0x45, 0xf6,
0x8d, 0xda, 0x17, 0x29, 0x07, 0x17, 0x0d, 0x06, 0xb0, 0xba, 0xa4, 0x42, 0xf2, 0x76, 0xeb, 0x06, 0x5c, 0x84, 0xaa, 0xd0, 0xb1, 0xad, 0x47, 0x91, 0x5b, 0x38, 0xa0, 0x0c, 0x2a, 0x51, 0x51, 0xa5,
0xee, 0x9c, 0x7b, 0xa4, 0x96, 0x7a, 0xc2, 0xb7, 0xa9, 0x3d, 0x01, 0x73, 0x10, 0xd2, 0x96, 0xb0, 0x4a, 0x41, 0x34, 0x13, 0x9f, 0x07, 0x51, 0x26, 0x22, 0x74, 0xb9, 0x27, 0xd8, 0x8d, 0x95, 0x8d,
0x89, 0x55, 0x4b, 0x51, 0xc9, 0x17, 0x5a, 0x7c, 0x81, 0x8b, 0xf5, 0x13, 0xee, 0x2a, 0xd4, 0x59, 0x23, 0xd7, 0xfc, 0x81, 0x76, 0x11, 0x10, 0xbe, 0xa1, 0xec, 0xef, 0x2e, 0x8b, 0x86, 0x28, 0xfb,
0x8d, 0x55, 0xc1, 0x98, 0x28, 0x4d, 0x1a, 0x9a, 0xdc, 0xa7, 0x7a, 0x5b, 0x64, 0xff, 0x3e, 0x13, 0x9e, 0xe0, 0x8f, 0x10, 0x0a, 0x5f, 0x43, 0x61, 0x0c, 0x78, 0x06, 0x87, 0x01, 0x6d, 0x0b, 0x73,
0x45, 0xfc, 0x1a, 0x7c, 0xa2, 0x85, 0x76, 0xe0, 0x4b, 0xee, 0x30, 0x7c, 0x4b, 0x6e, 0x40, 0xb9, 0x71, 0x15, 0x61, 0xc5, 0xe2, 0x8b, 0x85, 0xfc, 0xe9, 0xbe, 0x77, 0x37, 0x1a, 0xca, 0xca, 0xe2,
0xba, 0x77, 0x03, 0x37, 0x1f, 0x47, 0xfd, 0x8b, 0xf7, 0xfd, 0xc8, 0x71, 0xbf, 0x66, 0xfc, 0xe0, 0xaf, 0x8f, 0x87, 0xdc, 0x64, 0x74, 0x79, 0x75, 0x3d, 0xec, 0xdf, 0x8b, 0xf6, 0xdb, 0x7e, 0xef,
0xbe, 0x3f, 0x18, 0x46, 0x79, 0x26, 0x0e, 0xa7, 0x65, 0x82, 0xab, 0x11, 0xc7, 0x36, 0x05, 0xd8, 0xfd, 0xfb, 0xb3, 0x9e, 0x58, 0x1e, 0xd9, 0x26, 0x90, 0x79, 0x41, 0x38, 0x4a, 0x2f, 0x2d, 0x21,
0xe2, 0x97, 0x92, 0x06, 0xc0, 0x81, 0x61, 0xf2, 0x12, 0x5d, 0x4a, 0xa7, 0x2d, 0x03, 0xe5, 0xd7, 0x66, 0x1d, 0xff, 0xc1, 0x94, 0xf4, 0x83, 0x9f, 0x45, 0xdd, 0x8c, 0x2e, 0x19, 0x6b, 0x46, 0xed,
0xb9, 0x56, 0xb8, 0x61, 0xdf, 0x20, 0xae, 0x4a, 0xa4, 0xe9, 0xd8, 0x2b, 0x51, 0x02, 0x70, 0x7b, 0x7f, 0x3f, 0x7d, 0x19, 0xdd, 0xb2, 0xe6, 0x86, 0x56, 0x79, 0x29, 0xf1, 0x1e, 0xc4, 0x59, 0xf9,
0xa6, 0x24, 0xfa, 0xb9, 0x8b, 0x61, 0x67, 0x80, 0xe1, 0xcb, 0x84, 0x34, 0xce, 0x5f, 0x68, 0xf3, 0x28, 0xa3, 0xc5, 0xa0, 0xa1, 0x43, 0xa6, 0x05, 0x39, 0xdc, 0x78, 0xc5, 0x4c, 0xf4, 0x13, 0x90,
0xeb, 0x89, 0x58, 0x47, 0x89, 0xde, 0x48, 0x44, 0x31, 0xee, 0x8d, 0x0c, 0xac, 0xa6, 0xe2, 0x3d, 0x29, 0x40, 0x08, 0x16, 0x29, 0xdc, 0x4a, 0x26, 0x22, 0xe7, 0x1c, 0xdd, 0xb9, 0xcd, 0x35, 0xef,
0x12, 0x31, 0xc5, 0x9e, 0x4e, 0x2e, 0x4b, 0xf2, 0x6b, 0xe2, 0xf2, 0x07, 0x78, 0xb3, 0xa8, 0x34, 0x46, 0x78, 0x72, 0xc5, 0x55, 0x56, 0xad, 0x52, 0xf7, 0xe9, 0x69, 0x7b, 0xaf, 0xf6, 0x4b, 0x73,
0x39, 0x99, 0xd4, 0x54, 0x60, 0x5d, 0xa9, 0x27, 0x1d, 0xad, 0x83, 0x0d, 0x1d, 0x87, 0x1b, 0xda, 0x9b, 0xe9, 0x76, 0x8c, 0x2e, 0xa9, 0x59, 0xab, 0xb2, 0xbe, 0xbe, 0xfe, 0x78, 0x2f, 0xb3, 0xbc,
0xd1, 0xd9, 0xd5, 0x90, 0xfd, 0x48, 0x35, 0x5f, 0xf4, 0xef, 0x86, 0x1f, 0xa8, 0x95, 0xf6, 0x8b, 0xb2, 0xac, 0x42, 0x9d, 0x72, 0x01, 0xce, 0xd5, 0xc1, 0xc8, 0xe4, 0x57, 0xe7, 0x60, 0x9d, 0x73,
0x93, 0x98, 0xfc, 0xd0, 0xef, 0xdd, 0xb5, 0x0e, 0xbe, 0xce, 0x17, 0x40, 0xf0, 0xb7, 0x64, 0x13, 0x8a, 0x36, 0x25, 0xbb, 0x49, 0xb7, 0x99, 0xed, 0x16, 0x9b, 0x91, 0xd9, 0x69, 0x3e, 0xe2, 0x24,
0x3d, 0xb9, 0xbd, 0x39, 0x84, 0xc9, 0x28, 0x80, 0xc9, 0x82, 0x18, 0x0d, 0x0c, 0x32, 0x3d, 0x68, 0xc3, 0x90, 0x24, 0x7a, 0xd5, 0x56, 0xb3, 0xd9, 0x54, 0x68, 0x8f, 0x4f, 0xe4, 0x55, 0x90, 0xdf,
0xfb, 0x2e, 0x18, 0x7d, 0x9c, 0xbd, 0xe4, 0xdb, 0x47, 0x14, 0x20, 0x4e, 0xc0, 0xab, 0xe1, 0x76, 0xf2, 0x7a, 0x37, 0xb8, 0x6b, 0x07, 0xca, 0x64, 0x6e, 0x64, 0x6c, 0xe1, 0x0e, 0x5c, 0xbf, 0x4d,
0xd9, 0x0f, 0xb8, 0x55, 0x66, 0x6b, 0xde, 0x14, 0x7e, 0x75, 0x3d, 0x9c, 0x85, 0xb6, 0x86, 0xb0, 0xc2, 0xdc, 0x94, 0x72, 0xaa, 0x6d, 0x24, 0x93, 0xce, 0x93, 0x38, 0x2b, 0x8d, 0x57, 0xdb, 0x5e,
0x93, 0x3e, 0x28, 0xe4, 0xf5, 0x01, 0xa3, 0xb9, 0xad, 0xe9, 0x48, 0x21, 0x10, 0x57, 0x46, 0xa7, 0x08, 0x56, 0x38, 0x32, 0xe1, 0xbe, 0x73, 0xb8, 0xd7, 0x8d, 0x1b, 0x16, 0xd0, 0xb0, 0xdf, 0x6a,
0x13, 0x71, 0x1f, 0x29, 0x3d, 0x92, 0xdb, 0x96, 0xb8, 0x35, 0xcb, 0xcb, 0xba, 0x33, 0x99, 0x8e, 0x74, 0x19, 0xf1, 0xd1, 0x22, 0xc4, 0x27, 0x1d, 0xa6, 0x07, 0xe0, 0x18, 0x06, 0xb0, 0x54, 0xaa,
0x71, 0x00, 0x7e, 0xb3, 0x59, 0x16, 0xe9, 0x50, 0x4c, 0x79, 0xa2, 0x0c, 0x66, 0x19, 0xbf, 0xdf, 0x15, 0x7d, 0x66, 0xae, 0xef, 0x83, 0x54, 0xb4, 0x0c, 0xf2, 0x27, 0x10, 0x5d, 0x43, 0x48, 0xfa,
0x26, 0x50, 0x1e, 0x01, 0xe2, 0x72, 0x09, 0x37, 0x78, 0x22, 0x7c, 0x98, 0x47, 0x43, 0x86, 0x23, 0xf2, 0xfd, 0x08, 0x69, 0xc9, 0x2c, 0xea, 0x8d, 0xd1, 0x3e, 0x97, 0x4b, 0x2f, 0x2e, 0x65, 0xf4,
0x54, 0x39, 0xce, 0x14, 0x1f, 0x98, 0xaf, 0xae, 0x95, 0x0b, 0x5f, 0x69, 0xcb, 0xf5, 0xf0, 0x5d, 0x9d, 0x1d, 0xec, 0xc3, 0x2f, 0xf9, 0x43, 0x06, 0x56, 0x7d, 0x48, 0x8e, 0x18, 0xec, 0xf7, 0x10,
0x9d, 0x1d, 0x2e, 0x2b, 0x55, 0xad, 0x0c, 0x3e, 0xf5, 0xee, 0x2a, 0x19, 0xeb, 0xfd, 0x40, 0x59, 0xf5, 0x8d, 0x2b, 0xf6, 0x75, 0x44, 0x8a, 0x03, 0x29, 0x64, 0x92, 0xe1, 0xbc, 0x20, 0xdf, 0x72,
0xe9, 0x67, 0xc8, 0x7c, 0x07, 0x24, 0xbe, 0xfb, 0x15, 0x48, 0x84, 0xfa, 0xe4, 0xdd, 0x21, 0xa5, 0x53, 0x4b, 0x22, 0x13, 0x66, 0x5c, 0xd1, 0x45, 0x33, 0xfa, 0x8f, 0x04, 0x08, 0xa2, 0x21, 0xf0,
0x56, 0x6e, 0x3e, 0x0e, 0x6f, 0x8b, 0x88, 0x3e, 0x0b, 0x83, 0x20, 0x45, 0x74, 0x00, 0xb9, 0x84, 0xe1, 0x2e, 0x9c, 0x7f, 0x80, 0xf4, 0x83, 0x03, 0x20, 0x7b, 0x7f, 0x3b, 0xe9, 0x38, 0x3d, 0x51,
0x92, 0xa2, 0xfe, 0xec, 0xa1, 0x98, 0xfa, 0x7f, 0x73, 0x40, 0xfd, 0xc8, 0x57, 0xff, 0x18, 0xd1, 0xef, 0x80, 0xe0, 0x03, 0x94, 0x37, 0xfc, 0x21, 0x03, 0x82, 0xc4, 0x64, 0xc6, 0x84, 0xc4, 0x7f,
0xf7, 0xbf, 0x9c, 0x11, 0x89, 0xb8, 0x30, 0xff, 0xb1, 0x0f, 0x46, 0xa7, 0x7b, 0xb9, 0xad, 0x17, 0xc9, 0xc2, 0xc7, 0xdc, 0x7d, 0x64, 0x6b, 0xe8, 0x1c, 0x70, 0xe1, 0x8c, 0xf0, 0x09, 0xc8, 0xa8,
0x0d, 0xeb, 0x40, 0xd9, 0xef, 0x96, 0xd1, 0x81, 0x8f, 0xce, 0x7a, 0xc3, 0xf3, 0x0f, 0xd0, 0xd0, 0x3e, 0x68, 0xb7, 0xf6, 0xc0, 0x99, 0x94, 0xa9, 0x19, 0x77, 0x6b, 0xb0, 0x82, 0xfe, 0x4d, 0xba,
0xaa, 0x4c, 0x66, 0x90, 0x8c, 0xdb, 0xe9, 0xd4, 0xba, 0x19, 0xf8, 0xf3, 0x02, 0xd7, 0x96, 0x80, 0xa7, 0x80, 0xef, 0x52, 0x0e, 0x2d, 0x13, 0x51, 0x2e, 0xed, 0xd0, 0x37, 0xef, 0xa9, 0x02, 0x14,
0x5e, 0xe8, 0xa5, 0xbc, 0x25, 0x43, 0x6a, 0x3d, 0x73, 0x7b, 0x9e, 0xdc, 0x8a, 0xb9, 0x30, 0x3b, 0x2b, 0x98, 0x3d, 0x6d, 0x39, 0x38, 0xcd, 0xff, 0x85, 0x1a, 0xa8, 0x5b, 0x5f, 0x82, 0x2e, 0xf5,
0x15, 0x7e, 0x2d, 0x5b, 0x78, 0x85, 0x5f, 0xcb, 0x36, 0x5e, 0xe1, 0xd7, 0xb2, 0x83, 0x57, 0x1d, 0xd0, 0xb0, 0xd8, 0x89, 0x3d, 0xe3, 0xba, 0x86, 0x65, 0xc5, 0x1f, 0x30, 0x38, 0xca, 0x5c, 0x21,
0x36, 0xac, 0xb0, 0xd6, 0x98, 0x84, 0xb6, 0x58, 0x71, 0x56, 0xf0, 0xd6, 0x67, 0x63, 0xcc, 0x1f, 0x02, 0x17, 0xec, 0x84, 0xcf, 0x93, 0x3d, 0x9f, 0x20, 0x62, 0x8a, 0x59, 0x13, 0x36, 0x77, 0x7d,
0xdf, 0x1a, 0xdb, 0xf8, 0x00, 0x47, 0x6e, 0x29, 0x81, 0x07, 0xf5, 0x01, 0x15, 0x23, 0xdd, 0x6a, 0xde, 0xd8, 0x02, 0xaf, 0xe6, 0xad, 0xc4, 0x6c, 0xb2, 0x4a, 0xc9, 0x72, 0x76, 0x7a, 0xc2, 0x12,
0xa9, 0x37, 0x20, 0x5b, 0x5a, 0x17, 0x6d, 0x94, 0x12, 0xc6, 0xe2, 0xfb, 0x9a, 0x41, 0xb1, 0xd3, 0xd9, 0x6d, 0x52, 0x60, 0x5e, 0x42, 0xe1, 0xcb, 0x5e, 0x06, 0x14, 0x11, 0x7c, 0xd5, 0xec, 0x6e,
0xc3, 0xd8, 0x11, 0x18, 0x5b, 0x5b, 0x31, 0xee, 0x09, 0x8c, 0xad, 0xad, 0x18, 0x0f, 0x04, 0xc6, 0xf6, 0x56, 0x8c, 0xef, 0x04, 0xc6, 0xf6, 0x56, 0x8c, 0xbf, 0xbe, 0x16, 0x63, 0xab, 0xf9, 0x5a,
0x94, 0xad, 0x76, 0xcc, 0xf7, 0x16, 0xc0, 0xce, 0x6b, 0xf9, 0x6e, 0xed, 0x0b, 0x94, 0x9d, 0xad, 0x28, 0x85, 0x84, 0x56, 0x9d, 0x1c, 0xca, 0xf3, 0x2c, 0xa4, 0x54, 0x76, 0x9c, 0x21, 0x6c, 0x81,
0x6e, 0xfd, 0x14, 0x74, 0xfb, 0xa7, 0xa0, 0x3b, 0x3f, 0x05, 0xbd, 0xf7, 0x53, 0xd0, 0xfb, 0x1b, 0xa1, 0xc5, 0xba, 0xd9, 0x3d, 0x61, 0x07, 0xeb, 0x49, 0xee, 0x10, 0x3c, 0xcf, 0x9d, 0xb5, 0xe4,
0x90, 0xc6, 0xa3, 0x33, 0x58, 0xb5, 0x44, 0x34, 0x59, 0xb6, 0x76, 0x77, 0xbb, 0x70, 0x07, 0xdf, 0x22, 0xc9, 0xb1, 0xdb, 0x72, 0x77, 0x69, 0xcd, 0xe1, 0xc6, 0x00, 0xe1, 0x26, 0x88, 0xdd, 0x26,
0x7a, 0xe4, 0x52, 0xe2, 0x81, 0x96, 0xad, 0x06, 0x80, 0xe3, 0x6c, 0xe2, 0x17, 0x66, 0xb3, 0xdb, 0xe9, 0xdb, 0xa8, 0x9b, 0x9a, 0xe3, 0x5e, 0xf8, 0x91, 0x79, 0xfa, 0x3a, 0x0f, 0x03, 0xb1, 0xed,
0x1c, 0x71, 0x01, 0xa3, 0x70, 0x48, 0x9b, 0xb8, 0x51, 0xd5, 0xec, 0x06, 0xf9, 0xb2, 0xc5, 0x76, 0x59, 0x05, 0xfe, 0xed, 0x22, 0xbf, 0xd1, 0xe5, 0xb2, 0x9d, 0xb4, 0xb6, 0x45, 0x29, 0x81, 0x7c,
0x59, 0x00, 0x7d, 0x02, 0x43, 0x24, 0x9b, 0xd6, 0x0a, 0x6f, 0x57, 0xf1, 0xed, 0x12, 0xe5, 0xb2, 0x6c, 0xc7, 0xbd, 0x78, 0x8b, 0xcb, 0x46, 0xdc, 0x1b, 0xb8, 0x95, 0x02, 0x30, 0x0d, 0x2b, 0x46,
0x60, 0xac, 0xb0, 0x69, 0x05, 0x4d, 0x09, 0x92, 0x19, 0x3d, 0x95, 0x6c, 0x29, 0xea, 0x8d, 0x16, 0x98, 0x8a, 0xff, 0x87, 0x17, 0x54, 0x8d, 0xe5, 0x8e, 0xb1, 0xdc, 0x35, 0x56, 0x3b, 0xc6, 0x0a,
0x9d, 0x3b, 0x20, 0xda, 0x39, 0x21, 0x40, 0xbc, 0x59, 0xa5, 0x6e, 0x10, 0x01, 0x24, 0x9d, 0xa6, 0x8b, 0x53, 0x01, 0xfc, 0x4b, 0x3a, 0x22, 0x38, 0x98, 0xbf, 0x01, 0xd8, 0x37, 0xc0, 0x81, 0xc7,
0x45, 0x41, 0x00, 0xaa, 0xa5, 0x8a, 0x02, 0x11, 0xaa, 0x43, 0xbe, 0xb0, 0x6d, 0x95, 0x6e, 0x03, 0xc1, 0x34, 0x24, 0x5c, 0x23, 0x86, 0x13, 0x6d, 0xab, 0xa4, 0x2d, 0x71, 0xf4, 0xee, 0xd4, 0xd2,
0xb1, 0x3e, 0xb5, 0x1c, 0xdd, 0xf5, 0x3c, 0xc8, 0x4b, 0x14, 0xf6, 0xf9, 0xf3, 0x67, 0x76, 0x79, 0xf5, 0xf9, 0xa6, 0xaf, 0xa4, 0xcd, 0xa3, 0x4a, 0xa8, 0x25, 0x57, 0x35, 0xa8, 0xf6, 0x08, 0xad,
0xa4, 0x9e, 0xee, 0x49, 0x83, 0xd9, 0xfe, 0xdd, 0x4c, 0x7f, 0x3b, 0x37, 0xbe, 0x9d, 0x19, 0x4f, 0x6a, 0x4b, 0x23, 0x20, 0xc5, 0xc5, 0x18, 0x8a, 0x8a, 0x41, 0xb4, 0xb1, 0x7b, 0xa0, 0x99, 0x42,
0x59, 0xa1, 0x95, 0xfd, 0x37, 0xd6, 0x92, 0x62, 0x1e, 0x30, 0xd4, 0x61, 0x58, 0x1d, 0x60, 0xe5, 0x2f, 0xde, 0xb7, 0xe5, 0x7d, 0x7b, 0x13, 0x6d, 0x77, 0xae, 0x0d, 0x99, 0xdb, 0x1c, 0x77, 0xf2,
0x7d, 0xf9, 0x6c, 0x42, 0x50, 0x25, 0x2e, 0x65, 0x45, 0x4e, 0xb3, 0x62, 0x6e, 0xf0, 0x23, 0xba, 0x83, 0xe8, 0xec, 0x47, 0x5b, 0xf3, 0x8d, 0x76, 0x77, 0x7b, 0xc0, 0xc5, 0x01, 0x3b, 0x9d, 0x4d,
0x51, 0x77, 0x82, 0x75, 0x3e, 0xcd, 0xd0, 0x85, 0x9f, 0x63, 0x42, 0x0f, 0x57, 0xbb, 0xbb, 0x51, 0xe1, 0xf3, 0x8a, 0xb0, 0x2c, 0x98, 0xf8, 0xda, 0xfc, 0xd6, 0xdd, 0x0a, 0xdf, 0x4a, 0xc1, 0xb7,
0x5e, 0x01, 0xdf, 0x49, 0xc1, 0x5b, 0x3b, 0x6d, 0xf0, 0xde, 0xdb, 0xc7, 0xec, 0xe5, 0xc6, 0x74, 0x5e, 0x31, 0xe6, 0x20, 0x37, 0x66, 0xef, 0x15, 0x63, 0xde, 0xe5, 0xc6, 0xec, 0x7f, 0x43, 0x81,
0xfe, 0x64, 0x60, 0xfb, 0xc9, 0xc8, 0xf6, 0x73, 0xa1, 0x2d, 0x05, 0x1f, 0x87, 0x94, 0x0e, 0x15, 0xa4, 0x79, 0x8b, 0x1c, 0xf0, 0xe0, 0x1a, 0xca, 0x5d, 0x2a, 0x68, 0xab, 0xe2, 0x20, 0x98, 0xb0,
0xc6, 0x4d, 0x29, 0x3b, 0xdd, 0xaa, 0x6d, 0xa5, 0xd9, 0x85, 0x4c, 0x5f, 0x7a, 0x18, 0x2a, 0x2a, 0x52, 0xa7, 0xa1, 0x2c, 0xfd, 0xf1, 0x53, 0x54, 0xb9, 0x24, 0x45, 0x7c, 0xd1, 0x16, 0x4a, 0x83,
0x15, 0x6e, 0x82, 0x9c, 0xb2, 0x4e, 0x64, 0x86, 0x9b, 0x8e, 0x35, 0xb1, 0x8a, 0x38, 0xea, 0x55, 0x61, 0x35, 0xc5, 0x82, 0xe2, 0xc9, 0xfb, 0x30, 0xbc, 0xb9, 0x06, 0xfa, 0x2a, 0xe7, 0xa2, 0x94,
0x82, 0x6a, 0xbc, 0x82, 0x72, 0x03, 0x2a, 0x6f, 0xe5, 0xa1, 0x24, 0x59, 0x64, 0x81, 0x07, 0x2b, 0x6d, 0xd8, 0x7a, 0x29, 0x20, 0xb1, 0x5b, 0x00, 0x5d, 0xb0, 0x13, 0xb3, 0x06, 0x24, 0x4b, 0x10,
0x90, 0xc3, 0x40, 0xc8, 0x81, 0xb1, 0xca, 0xc2, 0x3f, 0x82, 0x42, 0x0c, 0xa4, 0x16, 0x3d, 0xad, 0x52, 0x68, 0xdf, 0x0f, 0x82, 0x99, 0x6a, 0xa8, 0x0b, 0x5f, 0xd5, 0x67, 0xc6, 0x38, 0x9c, 0x88,
0xb3, 0x16, 0x79, 0x7c, 0xb8, 0x19, 0x0f, 0xe9, 0xf6, 0x70, 0x35, 0x47, 0x42, 0xdf, 0x6a, 0x58, 0xd4, 0x88, 0x6d, 0x99, 0xb7, 0x45, 0xd0, 0xae, 0xe3, 0xce, 0xc5, 0x49, 0x36, 0xa9, 0x15, 0xd0,
0x43, 0x89, 0x04, 0xfa, 0x97, 0x25, 0xca, 0x0d, 0x12, 0xe8, 0x8b, 0xd2, 0xc0, 0xe7, 0x8f, 0x04, 0x94, 0xec, 0xe1, 0xe0, 0xed, 0x73, 0x21, 0x85, 0x33, 0xee, 0xfb, 0xda, 0x94, 0xa7, 0x88, 0x64,
0x55, 0xfe, 0x04, 0xe4, 0xc8, 0xa3, 0x35, 0xe8, 0xf5, 0xe8, 0x19, 0x30, 0x35, 0x2a, 0xb8, 0x89, 0x5c, 0x48, 0xc0, 0xcb, 0xca, 0x23, 0xdb, 0xd3, 0xc6, 0xca, 0x78, 0x15, 0xf0, 0x6b, 0xee, 0x4c,
0xc1, 0xc0, 0x4f, 0xe8, 0x19, 0x0e, 0xf6, 0xd1, 0x3c, 0x25, 0xdc, 0x45, 0x73, 0x75, 0xa0, 0x26, 0xde, 0x2d, 0x2b, 0xc9, 0xe9, 0x13, 0x17, 0x2c, 0x1b, 0x70, 0x49, 0xd0, 0x6e, 0xb6, 0x70, 0xd3,
0xf0, 0x59, 0x98, 0x16, 0xc6, 0x19, 0xf4, 0xa1, 0x1b, 0xb7, 0xcc, 0x68, 0x2e, 0x39, 0x19, 0xa5, 0x16, 0x02, 0xc3, 0x3a, 0xfc, 0x57, 0x70, 0xc0, 0xdf, 0x04, 0x5e, 0x24, 0xbb, 0x2a, 0x20, 0x4f,
0x4f, 0x4f, 0x59, 0x7b, 0x0f, 0x3c, 0xf9, 0x2f, 0xb8, 0x63, 0xc6, 0x7e, 0xf9, 0x85, 0x65, 0xbb, 0x0e, 0x6b, 0xec, 0x0d, 0xd4, 0xce, 0x58, 0xea, 0xa7, 0x26, 0xca, 0xa6, 0x52, 0x95, 0x4b, 0x7c,
0x4e, 0x81, 0x8f, 0x95, 0xd0, 0x00, 0x2d, 0xca, 0xa0, 0xee, 0xb9, 0x86, 0xa7, 0x1c, 0xa8, 0x85, 0xb0, 0x61, 0x23, 0x72, 0x48, 0x2d, 0x31, 0xab, 0xd8, 0x0a, 0xcb, 0x08, 0xdb, 0x8a, 0x28, 0x6e,
0x80, 0xb7, 0x8a, 0xf2, 0xb1, 0xc2, 0xfe, 0xf6, 0xcb, 0xfd, 0xe0, 0x04, 0x3a, 0x5b, 0x30, 0x80, 0x67, 0x17, 0xba, 0x2c, 0x95, 0xc6, 0x50, 0xd3, 0x3e, 0x8a, 0xeb, 0x67, 0x21, 0xcf, 0x44, 0x23,
0xb1, 0x85, 0x94, 0xfc, 0x85, 0x15, 0xe8, 0x66, 0x46, 0x46, 0x39, 0xd9, 0x68, 0x50, 0x28, 0x1f, 0x1c, 0xa5, 0x6e, 0x9a, 0x47, 0x0c, 0x9d, 0x05, 0x9e, 0x4e, 0xb4, 0x9c, 0x50, 0x3c, 0x4c, 0x6e,
0xe0, 0x73, 0x2b, 0xdf, 0x74, 0x43, 0xdb, 0x80, 0xd5, 0xfc, 0x84, 0x7b, 0x99, 0xb8, 0x8d, 0xc8, 0x3d, 0x2e, 0x4c, 0x24, 0x43, 0x8e, 0x40, 0xd3, 0x3a, 0x62, 0xe2, 0x89, 0x36, 0xa3, 0xbd, 0x74,
0xd1, 0x23, 0x27, 0x25, 0xdd, 0x83, 0x8b, 0xde, 0xa6, 0x7a, 0x40, 0x95, 0xde, 0x25, 0x45, 0x65, 0xe3, 0xa8, 0xac, 0x19, 0x4c, 0xe0, 0xe2, 0x07, 0xf5, 0x25, 0xbd, 0xad, 0x83, 0x74, 0xef, 0xf1,
0x31, 0x3b, 0x2c, 0x00, 0x3a, 0xcc, 0xc1, 0xd0, 0xa0, 0x3c, 0x50, 0x33, 0x07, 0x84, 0xf3, 0x76, 0x0b, 0x78, 0x7e, 0x4e, 0xb1, 0xde, 0x96, 0xe2, 0x2c, 0xe5, 0xca, 0x15, 0x52, 0x02, 0x22, 0xeb,
0x4c, 0x26, 0x93, 0x7a, 0xda, 0x72, 0x5b, 0x11, 0x37, 0xd8, 0x53, 0x3c, 0x01, 0x61, 0xee, 0x1c, 0xad, 0xcb, 0xd1, 0xe1, 0xcb, 0x60, 0xc2, 0x17, 0xd2, 0xcf, 0x5e, 0x39, 0x41, 0xeb, 0x40, 0xee,
0x8a, 0xad, 0xcb, 0x56, 0x1e, 0xb3, 0x50, 0x7c, 0xdb, 0xd2, 0x71, 0xad, 0xee, 0xec, 0xd5, 0xf1, 0x6b, 0xf7, 0x50, 0xec, 0x99, 0x5a, 0xbb, 0x27, 0xed, 0x6e, 0x8e, 0xf6, 0xb8, 0x4c, 0x91, 0xf3,
0x40, 0x96, 0x53, 0x8f, 0xaf, 0x5b, 0xa9, 0xeb, 0x76, 0xea, 0xba, 0xf3, 0x6d, 0xbb, 0x94, 0xf6, 0xfe, 0xff, 0x79, 0x49, 0xa5, 0xc3, 0x7f, 0x37, 0x37, 0xfb, 0x45, 0xdc, 0x84, 0xb3, 0x79, 0xe0,
0xcb, 0xf5, 0x28, 0xad, 0x88, 0xb6, 0xf8, 0x13, 0x10, 0x72, 0xeb, 0x7f, 0x9d, 0x57, 0x31, 0xc3, 0xce, 0x1e, 0xb1, 0x2c, 0xd1, 0x5a, 0x98, 0xe4, 0x88, 0x8e, 0x3c, 0xff, 0x22, 0xe5, 0xa6, 0x63,
0x38, 0x82, 0xb4, 0xad, 0x6c, 0xbd, 0xcb, 0xb1, 0xa5, 0xaa, 0x69, 0x48, 0xbc, 0xb5, 0xad, 0xe0, 0x4d, 0x6a, 0x9a, 0x5c, 0x7e, 0x9d, 0x70, 0xba, 0x5c, 0x6d, 0x5f, 0xed, 0x04, 0x87, 0xf5, 0xc6,
0x2a, 0x96, 0x56, 0xba, 0x0b, 0x31, 0x54, 0xa1, 0x2f, 0xb7, 0xc2, 0xb3, 0x40, 0x8b, 0xb4, 0xbc, 0x3b, 0xb9, 0x4e, 0x7c, 0x4c, 0x51, 0xc5, 0x5e, 0x40, 0xd1, 0xda, 0xab, 0x15, 0xc2, 0xc4, 0x2a,
0x49, 0x6f, 0x30, 0xbf, 0x5e, 0x25, 0x6c, 0xb1, 0x63, 0x16, 0x28, 0x04, 0x5a, 0xd3, 0xda, 0x58, 0x3f, 0x7d, 0x41, 0x6a, 0xc9, 0x1c, 0xba, 0x78, 0x59, 0x33, 0x87, 0x39, 0xcd, 0xbc, 0x28, 0x7c,
0x50, 0xf7, 0x18, 0x72, 0xad, 0xc7, 0x9c, 0x31, 0xa6, 0x41, 0x26, 0xbe, 0xc7, 0xff, 0x20, 0x27, 0x1b, 0x7b, 0xa5, 0xc8, 0x41, 0xb6, 0xd2, 0x70, 0xf4, 0x42, 0x88, 0x4d, 0x29, 0x55, 0x35, 0xf1,
0x5f, 0x89, 0x36, 0x32, 0x9e, 0x7b, 0x3c, 0x8d, 0x7c, 0xf7, 0x2b, 0x34, 0x0f, 0xf3, 0xfb, 0xdb, 0x21, 0xd3, 0x8f, 0xd7, 0x68, 0x44, 0xde, 0x08, 0xd2, 0x0f, 0xe4, 0xaa, 0x02, 0x64, 0x93, 0xbd,
0x6c, 0x7c, 0x3c, 0x9c, 0xa6, 0x3d, 0x23, 0xc8, 0xdf, 0xd3, 0x79, 0x50, 0x4a, 0x20, 0xff, 0x95, 0xc9, 0xc4, 0x22, 0x2d, 0x58, 0xc5, 0x52, 0x6d, 0x0d, 0x2a, 0x5b, 0xe8, 0x13, 0xa7, 0xad, 0xe9,
0x89, 0xf1, 0x74, 0xca, 0x05, 0xd7, 0x5d, 0xd2, 0xbc, 0x62, 0xd0, 0x55, 0x55, 0x92, 0x16, 0xe3, 0xcc, 0xbe, 0xd9, 0x81, 0xfa, 0xde, 0xb6, 0xfc, 0x7f, 0x3d, 0x8a, 0x17, 0xfa, 0x20, 0xfd, 0xcc,
0xbe, 0x8a, 0x47, 0xc7, 0x70, 0x53, 0x9d, 0xf6, 0xd4, 0x0d, 0x91, 0x1f, 0x18, 0x3c, 0x00, 0xff, 0xca, 0xc0, 0xfc, 0xe8, 0x91, 0x3c, 0x8a, 0x54, 0xbc, 0x1c, 0xa1, 0xb1, 0x66, 0xc3, 0x16, 0x19,
0xa7, 0x4c, 0x75, 0x95, 0xda, 0x36, 0xab, 0x5d, 0xcf, 0x27, 0x20, 0x2f, 0x51, 0x55, 0x1f, 0x53, 0x4f, 0xa8, 0xd3, 0x44, 0xf9, 0x95, 0x42, 0x92, 0xad, 0xdf, 0xd2, 0x0e, 0xb7, 0xd0, 0xc9, 0x3d,
0xcb, 0x6f, 0xfa, 0x29, 0x7e, 0x36, 0x19, 0xa7, 0xf0, 0xa5, 0x2d, 0x4f, 0x80, 0x23, 0x74, 0x1c, 0x0a, 0xa0, 0x24, 0x97, 0x2e, 0x50, 0xbf, 0xcf, 0x83, 0xea, 0xfa, 0xa3, 0x66, 0xf0, 0x37, 0xb5,
0xfa, 0x0b, 0x29, 0xf3, 0x16, 0x1a, 0xc0, 0x4c, 0x32, 0x49, 0xbd, 0xba, 0xd7, 0x8d, 0x28, 0x2a, 0xae, 0x36, 0x74, 0xdb, 0xf5, 0xd3, 0xb5, 0x86, 0x2c, 0x35, 0xa2, 0xaa, 0x28, 0x97, 0x2c, 0xbb,
0x9e, 0x0e, 0xfa, 0xa5, 0x31, 0x8a, 0x2c, 0xc8, 0x5e, 0x57, 0x06, 0x3e, 0xcb, 0x2a, 0x3b, 0xf3, 0x89, 0xeb, 0x1b, 0x5a, 0x70, 0x03, 0x3c, 0xd7, 0x55, 0x95, 0x6f, 0xd7, 0x64, 0xe6, 0xdd, 0x3c,
0x43, 0x94, 0x1e, 0x17, 0xe9, 0xac, 0xd6, 0xdd, 0xfe, 0x94, 0xbc, 0x5c, 0x2a, 0x25, 0xcf, 0xc9, 0x11, 0xb8, 0xe8, 0x2d, 0xa5, 0xf8, 0x74, 0x99, 0x28, 0xbd, 0x21, 0x87, 0xbd, 0xa0, 0x53, 0x4d,
0x1a, 0xc8, 0x5d, 0x4f, 0xbf, 0x09, 0x45, 0x8f, 0xb6, 0x85, 0xde, 0x11, 0x30, 0xbb, 0xc0, 0xd6, 0x5f, 0x74, 0x12, 0xaf, 0x75, 0xa8, 0xea, 0xa5, 0xb5, 0x0c, 0xe7, 0xd1, 0x1b, 0x4b, 0xbe, 0xf5,
0x9d, 0xfb, 0x79, 0x5a, 0xe2, 0xa7, 0xee, 0xf9, 0x27, 0xf8, 0xb9, 0xc3, 0x1b, 0xb4, 0x11, 0xe1, 0x18, 0xee, 0x42, 0x54, 0xaa, 0xf1, 0xf3, 0xf8, 0x82, 0x93, 0x5d, 0xb9, 0x63, 0x1d, 0xb9, 0x91,
0xf1, 0xb3, 0xfa, 0x92, 0x84, 0xa2, 0x57, 0xed, 0xb0, 0xea, 0x5d, 0x7f, 0xa9, 0x8d, 0x05, 0xee, 0x1c, 0x5f, 0xb8, 0xb3, 0xf9, 0x24, 0x80, 0xdf, 0x4a, 0x4e, 0x0a, 0xaf, 0xa0, 0x39, 0x76, 0xe2,
0xaf, 0xa6, 0x35, 0xf6, 0xea, 0xdd, 0x52, 0x89, 0xe6, 0x93, 0x12, 0x2b, 0x32, 0x83, 0x48, 0x6b, 0x37, 0xda, 0x23, 0x17, 0xa7, 0xd4, 0x22, 0xf4, 0x74, 0xc2, 0x5b, 0xbe, 0x55, 0x17, 0x9d, 0x60,
0xa5, 0x03, 0x48, 0x72, 0x7b, 0x97, 0x86, 0x7d, 0xc2, 0x23, 0x7a, 0x78, 0x52, 0xd5, 0xc7, 0xe3, 0x88, 0xe0, 0xd8, 0x80, 0x16, 0x36, 0xd7, 0xa0, 0x2a, 0x86, 0xa0, 0xcd, 0x3d, 0x3a, 0xd5, 0xe7,
0x7a, 0x50, 0x30, 0xc2, 0x62, 0xc4, 0xbd, 0xe1, 0xc0, 0x4d, 0xbd, 0x4d, 0xa0, 0xe4, 0x18, 0xaa, 0xca, 0x27, 0xd1, 0x05, 0x47, 0xf0, 0x36, 0x9d, 0xbe, 0x03, 0x12, 0x1e, 0xe6, 0x06, 0x9e, 0x63,
0x89, 0x0f, 0xb5, 0x27, 0x07, 0x79, 0xab, 0x03, 0x5d, 0xb3, 0xb9, 0xea, 0x9b, 0xd6, 0x84, 0xd6, 0xec, 0x86, 0xf3, 0x82, 0x75, 0x3c, 0x44, 0xf6, 0xd3, 0xa7, 0x05, 0x4b, 0x8d, 0xa6, 0xb2, 0x4f,
0xc7, 0x5e, 0x05, 0x21, 0xe9, 0xcd, 0x8b, 0xfc, 0xa6, 0x59, 0xcf, 0xb1, 0x66, 0x54, 0x1c, 0xd2, 0x52, 0x16, 0xab, 0x38, 0xb7, 0xe1, 0x05, 0xca, 0x4d, 0xaf, 0x73, 0x7a, 0x71, 0x86, 0xb4, 0x98,
0x1b, 0x9b, 0xc3, 0x25, 0x37, 0x8c, 0x5f, 0x0d, 0x2b, 0x4d, 0x45, 0x33, 0x8c, 0x3e, 0x7a, 0xb2, 0x6b, 0xcb, 0x0f, 0x38, 0x58, 0x74, 0xf5, 0xed, 0xcc, 0x85, 0xe0, 0x34, 0x83, 0xd5, 0xf7, 0xb6,
0xce, 0xc0, 0x94, 0x4f, 0xd9, 0x9f, 0xb9, 0x03, 0x20, 0x5c, 0x11, 0xe7, 0x2e, 0x3f, 0xd7, 0xe3, 0xcb, 0x2f, 0x78, 0x39, 0xa6, 0xb3, 0x16, 0x98, 0x1e, 0xb3, 0xe7, 0x17, 0x67, 0x08, 0xdc, 0x50,
0x37, 0x5f, 0x9e, 0x81, 0x40, 0x38, 0x6e, 0x0e, 0x2b, 0xba, 0x6d, 0xc1, 0x68, 0x9a, 0x6c, 0xad, 0x15, 0xe6, 0x6d, 0x6d, 0x9f, 0x8f, 0x38, 0x0a, 0xe7, 0xc9, 0x6c, 0x2c, 0x77, 0x3a, 0xe4, 0x65,
0x8e, 0xea, 0x58, 0xce, 0x76, 0xa3, 0xd3, 0x5a, 0xc2, 0x55, 0xbf, 0x66, 0x46, 0x3c, 0x41, 0x91, 0xe7, 0xf0, 0xb5, 0x33, 0xb6, 0x36, 0xcc, 0x18, 0xa7, 0x31, 0xeb, 0x73, 0x3e, 0xf2, 0x55, 0xd1,
0x8c, 0x74, 0x86, 0x84, 0x2b, 0xd0, 0x7b, 0x0e, 0xd9, 0x4d, 0x5a, 0x5e, 0x2f, 0xe3, 0x2a, 0x92, 0x57, 0x1e, 0x57, 0x33, 0xc6, 0x85, 0xc7, 0x42, 0x76, 0x52, 0x67, 0xb0, 0xe8, 0x75, 0x12, 0x61,
0xc1, 0x22, 0x9f, 0x4a, 0x1f, 0x8a, 0x5e, 0xcb, 0xc5, 0x02, 0xf7, 0x61, 0x78, 0x79, 0x98, 0xce, 0xbc, 0x5e, 0xe3, 0xc1, 0x73, 0x60, 0x1b, 0x9d, 0xa0, 0x89, 0x0b, 0x63, 0x47, 0x15, 0x6f, 0x48,
0xcb, 0x37, 0x79, 0xe1, 0x12, 0x5f, 0x8d, 0x3e, 0x2d, 0x1f, 0xab, 0xe2, 0x1d, 0xef, 0xff, 0x00, 0x62, 0xbb, 0xd2, 0xb4, 0xfc, 0x3d, 0x00, 0x00, };
//Returns 0 on succses.
//Returns size of file if non-empty
//If positive, populates mfi.
//Returns -1 if can't find file or reached end of file list.
int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi )
{
#ifdef CNFG_DISABLE_HTTP_FILES
mfi->filelen = 0;
return -1;
#else
if( strcmp( fname, "/" ) == 0 || strcmp( fname, "index.html" ) == 0 )
{
mfi->offset = 0;
mfi->filelen = sizeof(webpage_buffer);
return MFS_FILE_COMPRESSED_MEMORY;
}
else
return -1;
#endif
}
int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi )
{
#ifdef CNFG_DISABLE_HTTP_FILES
return 0;
#else
//returns # of bytes left tin file.
if( !mfi->filelen )
{
return 0;
}
int toread = mfi->filelen;
if( toread > MFS_SECTOR ) toread = MFS_SECTOR;
memcpy( data, &webpage_buffer[mfi->offset], toread );
mfi->offset += toread;
mfi->filelen -= toread;
return mfi->filelen;
#endif
}
void MFSClose( struct MFSFileInfo * mfi )
{
}
#else
//Returns 0 on succses.
//Returns size of file if non-empty
//If positive, populates mfi.
//Returns -1 if can't find file or reached end of file list.
int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi )
{
char targfile[1024];
if( strlen( fname ) == 0 || fname[strlen(fname)-1] == '/' )
{
snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s/index.html", fname );
}
else
{
snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s", fname );
}
//printf( ":%s:\n", targfile );
FILE * f = mfi->file = fopen( targfile, "rb" );
if( f <= 0 ) return -1;
//printf( "F: %p\n", f );
fseek( f, 0, SEEK_END );
mfi->filelen = ftell( f );
fseek( f, 0, SEEK_SET );
return 0;
}
int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi )
{
if( !mfi->filelen )
{
return 0;
}
int toread = fread( data, 1, MFS_SECTOR, mfi->file );
mfi->filelen -= toread;
return mfi->filelen;
}
void MFSClose( struct MFSFileInfo * mfi )
{
if( mfi->file ) fclose( mfi->file );
}
#endif
/*
SHA-1 in C
By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
-----------------
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
Corrected a problem which generated improper hash values on 16 bit machines
Routine SHA1Update changed from
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
len)
to
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
long len)
The 'len' parameter was declared an int which works fine on 32 bit machines.
However, on 16 bit machines an int is too small for the shifts being done
against
it. This caused the hash function to generate incorrect values if len was
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
an
int and a long are the same size in those environments.
--
I also corrected a few compiler warnings generated by Borland C.
1. Added #include <process.h> for exit() prototype
2. Removed unused variable 'j' in SHA1Final
3. Changed exit(0) to return(0) at end of main.
ALL changes I made can be located by searching for comments containing 'JHB'
-----------------
Modified 8/98
By Steve Reid <sreid@sea-to-sky.net>
Still 100% public domain
1- Removed #include <process.h> and used return() instead of exit()
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
-----------------
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
-----------------
Modified 07/2002
By Ralph Giles <giles@ghostscript.com>
Still 100% public domain
modified for use with stdint types, autoconf
code cleanup, removed attribution comments
switched SHA1Final() argument order for consistency
use SHA1_ prefix for public api
move public api to sha1.h
*/
/*
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
/* #define SHA1HANDSOFF */
#include <stdio.h>
#include <string.h>
static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
#define RDrol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
/* FIXME: can we do this in an endian-proof way? */
#ifdef WORDS_BIGENDIAN
#define RDblk0(i) block->l[i]
#else
#define RDblk0(i) (block->l[i] = (RDrol(block->l[i],24)&0xFF00FF00) \
|(RDrol(block->l[i],8)&0x00FF00FF))
#endif
#define RDblk(i) (block->l[i&15] = RDrol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk0(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0x6ED9EBA1+RDrol(v,5);w=RDrol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+RDblk(i)+0x8F1BBCDC+RDrol(v,5);w=RDrol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0xCA62C1D6+RDrol(v,5);w=RDrol(w,30);
/* Hash a single 512-bit block. This is the core of the algorithm. */
static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
{
uint32_t a, b, c, d, e;
typedef union {
uint8_t c[64];
uint32_t l[16];
} CHAR64LONG16;
CHAR64LONG16* block;
#ifdef SHA1HANDSOFF
static uint8_t workspace[64];
block = (CHAR64LONG16*)workspace;
memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
}
/* SHA1Init - Initialize new context */
static void RD_SHA1_Init(RD_SHA1_CTX* context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
static void RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len)
{
size_t i, j;
#ifdef VERBOSE
SHAPrintContext(context, "before");
#endif
j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
context->count[1] += (len >> 29);
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
RD_SHA1_Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
RD_SHA1_Transform(context->state, data + i);
}
j = 0;
}
else i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
#ifdef VERBOSE
SHAPrintContext(context, "after ");
#endif
}
/* Add padding and return the message digest. */
static void RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context)
{
uint32_t i;
uint8_t finalcount[8];
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
RD_SHA1_Update(context, (uint8_t *)"\200", 1);
while ((context->count[0] & 504) != 448) {
RD_SHA1_Update(context, (uint8_t *)"\0", 1);
}
RD_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
for (i = 0; i < RD_SHA1_DIGEST_SIZE; i++) {
digest[i] = (uint8_t)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
/* Wipe variables */
i = 0;
memset(context->buffer, 0, 64);
memset(context->state, 0, 20);
memset(context->count, 0, 8);
memset(finalcount, 0, 8); /* SWR */
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
SHA1_Transform(context->state, context->buffer);
#endif
}
#ifndef CNFGHTTPSERVERONLY
/*************************************************************/
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
char * cnfg_http_window_name;
int rd_request_fullscreen;
uint32_t * transitbuffer;
int transitlen;
int transitmax;
uint32_t * backbuffer;
int backbufferlen;
int backbuffermax;
uint32_t * datacmds;
int curdatacmds;
int maxdatacmds;
short last_dimensions_w;
short last_dimensions_h;
short cnfg_req_w;
short cnfg_req_h;
void HTTPCustomCallback( )
{
if( curhttp->rcb )
((void(*)())curhttp->rcb)();
else
curhttp->isdone = 1;
}
//Close of curhttp happened.
void CloseEvent()
{
}
static void readrdbuffer_websocket_dat( int len )
{
do
{
int bufferok = TCPCanSend( curhttp->socket, 1340 );
if( transitlen <= 0 || !bufferok || curhttp->bytesleft == -1 ) return;
int offset = transitlen * 4 - curhttp->bytesleft;
uint8_t * offdata = ((uint8_t*)transitbuffer) + offset;
int tosend = curhttp->bytesleft;
if( tosend > 1340 ) tosend = 1340;
WebSocketSend( offdata, tosend );
//Special - we send an empty frame to indicate competion.
if( tosend == 0 )
curhttp->bytesleft = -1;
else
curhttp->bytesleft -= tosend;
} while(1);
}
void ConsumeBackBufferForTransit()
{
int len = transitlen;
int maxv = transitmax;
uint32_t * dcmd = transitbuffer;
transitbuffer = backbuffer;
transitlen = backbufferlen;
transitmax = backbuffermax;
backbuffer = dcmd;
backbufferlen = 0;
backbuffermax = maxv;
}
static void readrdbuffer_websocket_cmd( int len )
{
uint8_t buf[1300];
int i;
if( len > 1300 ) len = 1300;
for( i = 0; i < len; i++ )
{
buf[i] = WSPOPMASK();
}
if( strncmp( buf, "SWAP", 4 ) == 0 )
{
last_dimensions_w = buf[4] | ( buf[5]<<8 );
last_dimensions_h = buf[6] | ( buf[7]<<8 );
ConsumeBackBufferForTransit();
curhttp->bytesleft = transitlen * 4;
}
if( strncmp( buf, "MOTN", 4 ) == 0 )
{
int x = buf[4] | ( buf[5]<<8 );
int y = buf[6] | ( buf[7]<<8 );
int but = buf[11];
HandleMotion( x, y, but );
}
if( strncmp( buf, "BUTN", 4 ) == 0 )
{
int x = buf[4] | ( buf[5]<<8 );
int y = buf[6] | ( buf[7]<<8 );
int down = buf[10];
int but = buf[11];
HandleButton( x, y, but, down );
}
if( strncmp( buf, "KEYB", 4 ) == 0 )
{
int key = buf[6];
int down = buf[7];
HandleKey( key, down );
}
}
void NewWebSocket()
{
if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws/cmdbuf", 9 ) == 0 )
{
printf( "Got connection.\n" );
curhttp->rcb = (void*)&readrdbuffer_websocket_dat;
curhttp->rcbDat = (void*)&readrdbuffer_websocket_cmd;
}
else
{
curhttp->is404 = 1;
}
}
void WebSocketTick()
{
if( curhttp->rcb )
{
((void(*)())curhttp->rcb)();
}
}
void WebSocketData( int len )
{
if( curhttp->rcbDat )
{
((void(*)( int ))curhttp->rcbDat)( len );
}
}
void HTTPCustomStart( )
{
curhttp->rcb = 0;
curhttp->bytesleft = 0;
curhttp->isfirst = 1;
HTTPHandleInternalCallback();
}
void QueueCmds( uint32_t * toqueue, int numwords )
{
int origcmds = curdatacmds;
curdatacmds += numwords;
if( curdatacmds > maxdatacmds )
{
datacmds = realloc( datacmds, curdatacmds*4 );
maxdatacmds = curdatacmds;
}
memcpy( datacmds + origcmds, toqueue, numwords*4 );
}
int CNFGSetup( const char * WindowName, int w, int h )
{
RunHTTP( 8888 );
if( cnfg_http_window_name ) free( cnfg_http_window_name );
cnfg_http_window_name = strdup( WindowName );
datacmds = 0;
backbuffer = 0;
backbufferlen = 0;
curdatacmds = 0;
maxdatacmds = 0;
rd_request_fullscreen = 0;
cnfg_req_w = w;
cnfg_req_h = h;
return 0;
}
void CNFGSetupFullscreen( const char * WindowName, int screen_number )
{
CNFGSetup( WindowName, -1, -1 );
rd_request_fullscreen = 1;
}
int CNFGHandleInput()
{
TickHTTP();
return 1;
}
// command structure:
// 0: Continuation
// 1: Color
// 2: Pixel
// 3: SegmentStart
// 4: RectangleStart
// 5: PolyStart
// 6: PolyContinue
// 7: Blit Pixels
// 8: Clear frame
// 9: Swap Buffers (Used as "end of frame")
// a: CNFGSetLineWidth
uint32_t CNFGColor( uint32_t RGBA )
{
uint32_t cmds[2] = { 0x10000000, RGBA };
QueueCmds( cmds, 2 );
return RGBA;
}
void CNFGTackPixel( short x1, short y1 )
{
if( x1 < 0 || x1 > 0x3fff ) return;
if( y1 < 0 || y1 > 0x3fff ) return;
uint32_t cmds[1] = { 0x20000000 | (x1 & 0x3fff ) | ( ( y1 & 0x3fff ) << 14 ) };
QueueCmds( cmds, 1 );
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
uint32_t cmds[3] = {
0x30000000,
( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ),
( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) };
QueueCmds( cmds, 3 );
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
uint32_t cmds[3] = {
0x40000000,
( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ),
( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) };
QueueCmds( cmds, 3 );
}
void CNFGSetLineWidth( short width )
{
uint32_t cmds[1] = { 0xa0000000 | width };
QueueCmds( cmds, 1 );
}
void CNFGTackPoly( RDPoint * points, int verts )
{
uint32_t * cmds = alloca( 4 * verts + 4 );
int i;
cmds[0] = 0x50000000 | verts;
for( i = 0; i < verts; i++ )
{
uint16_t lx = points[i].x;
uint16_t ly = points[i].y;
cmds[i+1] = lx | ( ly << 16 );
}
QueueCmds( cmds, verts + 1 );
}
void CNFGClearFrame()
{
int sl = strlen( cnfg_http_window_name );
int blocks = ( sl + 8 + 8 ) / 4;
uint32_t * cmds = alloca( blocks * 4 );
cmds[0] = 0x80000000 | blocks | (rd_request_fullscreen<<8) | (sl<<16);
cmds[1] = CNFGBGColor;
cmds[2] = cnfg_req_w | ( cnfg_req_h << 16);
memcpy( cmds+3, cnfg_http_window_name, sl + 1 );
QueueCmds( cmds, blocks );
}
void CNFGSwapBuffers()
{
uint32_t cmds[1] = { 0x90000000 };
QueueCmds( cmds, 1 );
// And swap.
int len = curdatacmds;
int maxv = maxdatacmds;
uint32_t * dcmd = datacmds;
datacmds = backbuffer;
curdatacmds = 0;
maxdatacmds = backbuffermax;
backbuffer = dcmd;
backbufferlen = len;
backbuffermax = maxv;
}
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
if( w < 0 || h < 0 ) return;
uint32_t * cmds = alloca( 4 * w * h + 8 );
cmds[0] = 0x70000000 | ( w & 0x3fff ) | ( ( h & 0x3fff ) << 14 );
cmds[1] = ( x | ( y<<16) );
int i;
memcpy( cmds + 2, data, w * h * 4 );
QueueCmds( cmds, w * h + 2 );
}
void CNFGGetDimensions( short * x, short * y )
{
*x = last_dimensions_w;
*y = last_dimensions_h;
}
#endif
#endif
#elif defined( __wasm__ )
//Right now, designed for use with https://github.com/cnlohr/rawdrawwasm/
#include <CNFG.h>
#include <stdint.h>
extern void __attribute__((import_module("bynsyncify"))) CNFGSwapBuffersInternal();
void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h );
void print( double idebug );
void prints( const char* sdebug );
//Forward declarations that we get from either WASM or our javascript code.
void CNFGClearFrameInternal( uint32_t bgcolor );
//The WASM driver handles internal resizing automatically.
#ifndef CNFGRASTERIZER
void CNFGInternalResize( short x, short y )
{
}
void CNFGFlushRender()
{
if( !CNFGVertPlace ) return;
CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace );
CNFGVertPlace = 0;
}
void CNFGClearFrame()
{
CNFGFlushRender();
CNFGClearFrameInternal( CNFGBGColor );
}
void CNFGSwapBuffers()
{
CNFGFlushRender();
CNFGSwapBuffersInternal( );
}
int CNFGHandleInput()
{
//Do nothing.
//Input is handled on swap frame.
return 1;
}
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
CNFGBlitImageInternal( data, x, y, w, h );
}
#else
//Rasterizer - if you want to do this, you will need to enable blitting in the javascript.
//XXX TODO: NEED MEMORY ALLOCATOR
extern unsigned char __heap_base;
unsigned int bump_pointer = (unsigned int)&__heap_base;
void* malloc(unsigned long size) {
unsigned int ptr = bump_pointer;
bump_pointer += size;
return (void *)ptr;
}
void free(void* ptr) { }
//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin.
#ifdef CNFGRASTERIZER
//#include <stdlib.h>
#include <stdint.h>
uint32_t * CNFGBuffer = 0;
short CNFGBufferx;
short CNFGBuffery;
#ifdef CNFGOGL
void CNFGFlushRender()
{
}
#endif
void CNFGInternalResize( short x, short y )
{
CNFGBufferx = x;
CNFGBuffery = y;
if( CNFGBuffer ) free( CNFGBuffer );
CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 );
#ifdef CNFGOGL
void CNFGInternalResizeOGLBACKEND( short w, short h );
CNFGInternalResizeOGLBACKEND( x, y );
#endif
}
#ifdef __wasm__
static uint32_t SWAPS( uint32_t r )
{
uint32_t ret = (r&0xFF)<<24;
r>>=8;
ret |= (r&0xff)<<16;
r>>=8;
ret |= (r&0xff)<<8;
r>>=8;
ret |= (r&0xff)<<0;
return ret;
}
#elif !defined(CNFGOGL)
#define SWAPS(x) (x>>8)
#else
static uint32_t SWAPS( uint32_t r )
{
uint32_t ret = (r&0xFF)<<16;
r>>=8;
ret |= (r&0xff)<<8;
r>>=8;
ret |= (r&0xff);
r>>=8;
ret |= (r&0xff)<<24;
return ret;
}
#endif
uint32_t CNFGColor( uint32_t RGB )
{
CNFGLastColor = SWAPS(RGB);
return CNFGLastColor;
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
short tx, ty;
//float slope, lp;
float slope;
short dx = x2 - x1;
short dy = y2 - y1;
if( !CNFGBuffer ) return;
if( dx < 0 ) dx = -dx;
if( dy < 0 ) dy = -dy;
if( dx > dy )
{
short minx = (x1 < x2)?x1:x2;
short maxx = (x1 < x2)?x2:x1;
short miny = (x1 < x2)?y1:y2;
short maxy = (x1 < x2)?y2:y1;
float thisy = miny;
slope = (float)(maxy-miny) / (float)(maxx-minx);
for( tx = minx; tx <= maxx; tx++ )
{
ty = thisy;
if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue;
if( tx >= CNFGBufferx ) break;
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
thisy += slope;
}
}
else
{
short minx = (y1 < y2)?x1:x2;
short maxx = (y1 < y2)?x2:x1;
short miny = (y1 < y2)?y1:y2;
short maxy = (y1 < y2)?y2:y1;
float thisx = minx;
slope = (float)(maxx-minx) / (float)(maxy-miny);
for( ty = miny; ty <= maxy; ty++ )
{
tx = thisx;
if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue;
if( ty >= CNFGBuffery ) break;
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
thisx += slope;
}
}
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
short minx = (x1<x2)?x1:x2;
short miny = (y1<y2)?y1:y2;
short maxx = (x1>=x2)?x1:x2;
short maxy = (y1>=y2)?y1:y2;
short x, y;
if( minx < 0 ) minx = 0;
if( miny < 0 ) miny = 0;
if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1;
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
for( y = miny; y <= maxy; y++ )
{
uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx];
for( x = minx; x <= maxx; x++ )
{
(*CNFGBufferstart++) = CNFGLastColor;
}
}
}
void CNFGTackPoly( RDPoint * points, int verts )
{
short minx = 10000, miny = 10000;
short maxx =-10000, maxy =-10000;
short i, x, y;
//Just in case...
if( verts > 32767 ) return;
for( i = 0; i < verts; i++ )
{
RDPoint * p = &points[i];
if( p->x < minx ) minx = p->x;
if( p->y < miny ) miny = p->y;
if( p->x > maxx ) maxx = p->x;
if( p->y > maxy ) maxy = p->y;
}
if( miny < 0 ) miny = 0;
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
for( y = miny; y <= maxy; y++ )
{
short startfillx = maxx;
short endfillx = minx;
//Figure out what line segments intersect this line.
for( i = 0; i < verts; i++ )
{
short pl = i + 1;
if( pl == verts ) pl = 0;
RDPoint ptop;
RDPoint pbot;
ptop.x = points[i].x;
ptop.y = points[i].y;
pbot.x = points[pl].x;
pbot.y = points[pl].y;
//printf( "Poly: %d %d\n", pbot.y, ptop.y );
if( pbot.y < ptop.y )
{
RDPoint ptmp;
ptmp.x = pbot.x;
ptmp.y = pbot.y;
pbot.x = ptop.x;
pbot.y = ptop.y;
ptop.x = ptmp.x;
ptop.y = ptmp.y;
}
//Make sure this line segment is within our range.
//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y );
if( ptop.y <= y && pbot.y >= y )
{
short diffy = pbot.y - ptop.y;
uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math.
short diffx = pbot.x - ptop.x;
short isectx;
if( diffy == 0 )
{
if( pbot.x < ptop.x )
{
if( startfillx > pbot.x ) startfillx = pbot.x;
if( endfillx < ptop.x ) endfillx = ptop.x;
}
else
{
if( startfillx > ptop.x ) startfillx = ptop.x;
if( endfillx < pbot.x ) endfillx = pbot.x;
}
}
else
{
//Inner part is scaled by 65536, outer part must be scaled back.
isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x;
if( isectx < startfillx ) startfillx = isectx;
if( isectx > endfillx ) endfillx = isectx;
}
//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx );
}
}
//printf( "%d %d %d\n", y, startfillx, endfillx );
if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1;
if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1;
if( startfillx < 0 ) startfillx = 0;
if( startfillx < 0 ) startfillx = 0;
unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx];
for( x = startfillx; x <= endfillx; x++ )
{
(*bufferstart++) = CNFGLastColor;
}
}
//exit(1);
}
void CNFGClearFrame()
{
int i, m;
uint32_t col = 0;
short x, y;
CNFGGetDimensions( &x, &y );
if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer )
{
CNFGBufferx = x;
CNFGBuffery = y;
CNFGBuffer = malloc( x * y * 8 );
}
m = x * y;
col = CNFGColor( CNFGBGColor );
for( i = 0; i < m; i++ )
{
CNFGBuffer[i] = col;
}
}
void CNFGTackPixel( short x, short y )
{
if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor;
}
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
int ox = x;
int stride = w;
if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
if( x < 0 ) { w += x; x = 0; }
if( y < 0 ) { h += y; y = 0; }
//Switch w,h to x2, y2
h += y;
w += x;
if( w >= CNFGBufferx ) { w = CNFGBufferx; }
if( h >= CNFGBuffery ) { h = CNFGBuffery; }
for( ; y < h-1; y++ )
{
x = ox;
uint32_t * indat = data;
uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x;
for( ; x < w-1; x++ )
{
uint32_t newm = *(indat++);
uint32_t oldm = *(outdat);
if( (newm & 0xff) == 0xff )
{
*(outdat++) = newm;
}
else
{
//Alpha blend.
int alfa = newm&0xff;
int onemalfa = 255-alfa;
#ifdef __wasm__
uint32_t newv = 255<<0; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
uint32_t newv = 255UL<<24; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
#elif defined( ANDROID ) || defined( __android__ )
uint32_t newv = 255<<16; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#elif defined( CNFGOGL ) //OGL, on X11
uint32_t newv = 255<<16; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#else //X11
uint32_t newv = 255UL<<24; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
#endif
*(outdat++) = newv;
}
}
data += stride;
}
}
void CNFGSwapBuffers()
{
CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery );
}
#endif
extern void CNFGUpdateScreenWithBitmapInternal( uint32_t * data, int w, int h );
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
{
CNFGBlitImageInternal( data, 0, 0, w, h );
CNFGSwapBuffersInternal();
}
void CNFGSetLineWidth( short width )
{
//Rasterizer does not support line width.
}
void CNFGHandleInput()
{
//Do nothing.
//Input is handled on swap frame.
}
#endif
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
//Portion from: http://en.wikibooks.org/wiki/Windows_Programming/Window_Creation
#ifndef _CNFGWINDRIVER_C
#define _CNFGWINDRIVER_C
#include <windows.h>
#include <stdlib.h>
#include <malloc.h> //for alloca
#include <ctype.h>
HBITMAP CNFGlsBitmap;
HWND CNFGlsHWND;
HDC CNFGlsWindowHDC;
HDC CNFGlsHDC;
HDC CNFGlsHDCBlit;
int ShouldClose = 0;
//Queue up lines and points for a faster render.
#ifndef CNFG_WINDOWS_DISABLE_BATCH
#define BATCH_ELEMENTS
#endif
#define COLORSWAPS( RGB ) \
((((RGB )& 0xFF000000)>>24) | ( ((RGB )& 0xFF0000 ) >> 8 ) | ( ((RGB )& 0xFF00 )<<8 ))
void CNFGChangeWindowTitle( const char * windowtitle )
{
SetWindowTextA( CNFGlsHWND, windowtitle );
}
#ifdef CNFGRASTERIZER
//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin.
#ifdef CNFGRASTERIZER
//#include <stdlib.h>
#include <stdint.h>
uint32_t * CNFGBuffer = 0;
short CNFGBufferx;
short CNFGBuffery;
#ifdef CNFGOGL
void CNFGFlushRender()
{
}
#endif
void CNFGInternalResize( short x, short y )
{
CNFGBufferx = x;
CNFGBuffery = y;
if( CNFGBuffer ) free( CNFGBuffer );
CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 );
#ifdef CNFGOGL
void CNFGInternalResizeOGLBACKEND( short w, short h );
CNFGInternalResizeOGLBACKEND( x, y );
#endif
}
#ifdef __wasm__
static uint32_t SWAPS( uint32_t r )
{
uint32_t ret = (r&0xFF)<<24;
r>>=8;
ret |= (r&0xff)<<16;
r>>=8;
ret |= (r&0xff)<<8;
r>>=8;
ret |= (r&0xff)<<0;
return ret;
}
#elif !defined(CNFGOGL)
#define SWAPS(x) (x>>8)
#else
static uint32_t SWAPS( uint32_t r )
{
uint32_t ret = (r&0xFF)<<16;
r>>=8;
ret |= (r&0xff)<<8;
r>>=8;
ret |= (r&0xff);
r>>=8;
ret |= (r&0xff)<<24;
return ret;
}
#endif
uint32_t CNFGColor( uint32_t RGB )
{
CNFGLastColor = SWAPS(RGB);
return CNFGLastColor;
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
short tx, ty;
//float slope, lp;
float slope;
short dx = x2 - x1;
short dy = y2 - y1;
if( !CNFGBuffer ) return;
if( dx < 0 ) dx = -dx;
if( dy < 0 ) dy = -dy;
if( dx > dy )
{
short minx = (x1 < x2)?x1:x2;
short maxx = (x1 < x2)?x2:x1;
short miny = (x1 < x2)?y1:y2;
short maxy = (x1 < x2)?y2:y1;
float thisy = miny;
slope = (float)(maxy-miny) / (float)(maxx-minx);
for( tx = minx; tx <= maxx; tx++ )
{
ty = thisy;
if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue;
if( tx >= CNFGBufferx ) break;
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
thisy += slope;
}
}
else
{
short minx = (y1 < y2)?x1:x2;
short maxx = (y1 < y2)?x2:x1;
short miny = (y1 < y2)?y1:y2;
short maxy = (y1 < y2)?y2:y1;
float thisx = minx;
slope = (float)(maxx-minx) / (float)(maxy-miny);
for( ty = miny; ty <= maxy; ty++ )
{
tx = thisx;
if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue;
if( ty >= CNFGBuffery ) break;
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
thisx += slope;
}
}
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
short minx = (x1<x2)?x1:x2;
short miny = (y1<y2)?y1:y2;
short maxx = (x1>=x2)?x1:x2;
short maxy = (y1>=y2)?y1:y2;
short x, y;
if( minx < 0 ) minx = 0;
if( miny < 0 ) miny = 0;
if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1;
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
for( y = miny; y <= maxy; y++ )
{
uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx];
for( x = minx; x <= maxx; x++ )
{
(*CNFGBufferstart++) = CNFGLastColor;
}
}
}
void CNFGTackPoly( RDPoint * points, int verts )
{
short minx = 10000, miny = 10000;
short maxx =-10000, maxy =-10000;
short i, x, y;
//Just in case...
if( verts > 32767 ) return;
for( i = 0; i < verts; i++ )
{
RDPoint * p = &points[i];
if( p->x < minx ) minx = p->x;
if( p->y < miny ) miny = p->y;
if( p->x > maxx ) maxx = p->x;
if( p->y > maxy ) maxy = p->y;
}
if( miny < 0 ) miny = 0;
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
for( y = miny; y <= maxy; y++ )
{
short startfillx = maxx;
short endfillx = minx;
//Figure out what line segments intersect this line.
for( i = 0; i < verts; i++ )
{
short pl = i + 1;
if( pl == verts ) pl = 0;
RDPoint ptop;
RDPoint pbot;
ptop.x = points[i].x;
ptop.y = points[i].y;
pbot.x = points[pl].x;
pbot.y = points[pl].y;
//printf( "Poly: %d %d\n", pbot.y, ptop.y );
if( pbot.y < ptop.y )
{
RDPoint ptmp;
ptmp.x = pbot.x;
ptmp.y = pbot.y;
pbot.x = ptop.x;
pbot.y = ptop.y;
ptop.x = ptmp.x;
ptop.y = ptmp.y;
}
//Make sure this line segment is within our range.
//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y );
if( ptop.y <= y && pbot.y >= y )
{
short diffy = pbot.y - ptop.y;
uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math.
short diffx = pbot.x - ptop.x;
short isectx;
if( diffy == 0 )
{
if( pbot.x < ptop.x )
{
if( startfillx > pbot.x ) startfillx = pbot.x;
if( endfillx < ptop.x ) endfillx = ptop.x;
}
else
{
if( startfillx > ptop.x ) startfillx = ptop.x;
if( endfillx < pbot.x ) endfillx = pbot.x;
}
}
else
{
//Inner part is scaled by 65536, outer part must be scaled back.
isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x;
if( isectx < startfillx ) startfillx = isectx;
if( isectx > endfillx ) endfillx = isectx;
}
//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx );
}
}
//printf( "%d %d %d\n", y, startfillx, endfillx );
if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1;
if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1;
if( startfillx < 0 ) startfillx = 0;
if( startfillx < 0 ) startfillx = 0;
unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx];
for( x = startfillx; x <= endfillx; x++ )
{
(*bufferstart++) = CNFGLastColor;
}
}
//exit(1);
}
void CNFGClearFrame()
{
int i, m;
uint32_t col = 0;
short x, y;
CNFGGetDimensions( &x, &y );
if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer )
{
CNFGBufferx = x;
CNFGBuffery = y;
CNFGBuffer = malloc( x * y * 8 );
}
m = x * y;
col = CNFGColor( CNFGBGColor );
for( i = 0; i < m; i++ )
{
CNFGBuffer[i] = col;
}
}
void CNFGTackPixel( short x, short y )
{
if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor;
}
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
int ox = x;
int stride = w;
if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
if( x < 0 ) { w += x; x = 0; }
if( y < 0 ) { h += y; y = 0; }
//Switch w,h to x2, y2
h += y;
w += x;
if( w >= CNFGBufferx ) { w = CNFGBufferx; }
if( h >= CNFGBuffery ) { h = CNFGBuffery; }
for( ; y < h-1; y++ )
{
x = ox;
uint32_t * indat = data;
uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x;
for( ; x < w-1; x++ )
{
uint32_t newm = *(indat++);
uint32_t oldm = *(outdat);
if( (newm & 0xff) == 0xff )
{
*(outdat++) = newm;
}
else
{
//Alpha blend.
int alfa = newm&0xff;
int onemalfa = 255-alfa;
#ifdef __wasm__
uint32_t newv = 255<<0; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
uint32_t newv = 255UL<<24; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
#elif defined( ANDROID ) || defined( __android__ )
uint32_t newv = 255<<16; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#elif defined( CNFGOGL ) //OGL, on X11
uint32_t newv = 255<<16; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#else //X11
uint32_t newv = 255UL<<24; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
#endif
*(outdat++) = newv;
}
}
data += stride;
}
}
void CNFGSwapBuffers()
{
CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery );
}
#endif
void InternalHandleResize()
{
if( CNFGlsBitmap ) DeleteObject( CNFGlsBitmap );
CNFGInternalResize( CNFGBufferx, CNFGBuffery );
CNFGlsBitmap = CreateBitmap( CNFGBufferx, CNFGBuffery, 1, 32, CNFGBuffer );
SelectObject( CNFGlsHDC, CNFGlsBitmap );
CNFGInternalResize( CNFGBufferx, CNFGBuffery);
}
#else
static short CNFGBufferx, CNFGBuffery;
static void InternalHandleResize();
#endif
#ifdef CNFGOGL
#include <GL/gl.h>
static HGLRC hRC=NULL;
static void InternalHandleResize() { }
void CNFGSwapBuffers()
{
#ifdef CNFG_BATCH
#ifndef CNFGCONTEXTONLY
CNFGFlushRender();
#endif
#endif
SwapBuffers(CNFGlsWindowHDC);
}
#endif
void CNFGGetDimensions( short * x, short * y )
{
static short lastx, lasty;
RECT window;
GetClientRect( CNFGlsHWND, &window );
CNFGBufferx = (short)( window.right - window.left);
CNFGBuffery = (short)( window.bottom - window.top);
if( CNFGBufferx != lastx || CNFGBuffery != lasty )
{
lastx = CNFGBufferx;
lasty = CNFGBuffery;
#ifndef CNFGCONTEXTONLY
CNFGInternalResize( lastx, lasty );
#endif
InternalHandleResize();
}
*x = CNFGBufferx;
*y = CNFGBuffery;
}
#ifndef CNFGOGL
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
{
RECT r;
SelectObject( CNFGlsHDC, CNFGlsBitmap );
SetBitmapBits(CNFGlsBitmap,w*h*4,data);
BitBlt(CNFGlsWindowHDC, 0, 0, w, h, CNFGlsHDC, 0, 0, SRCCOPY);
UpdateWindow( CNFGlsHWND );
short thisw, thish;
//Check to see if the window is closed.
if( !IsWindow( CNFGlsHWND ) )
{
exit( 0 );
}
GetClientRect( CNFGlsHWND, &r );
thisw = (short)(r.right - r.left);
thish = (short)(r.bottom - r.top);
if( thisw != CNFGBufferx || thish != CNFGBuffery )
{
CNFGBufferx = thisw;
CNFGBuffery = thish;
InternalHandleResize();
}
}
#endif
void CNFGTearDown()
{
PostQuitMessage(0);
ShouldClose = 1;
}
//This was from the article
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
#ifndef CNFGOGL
case WM_SYSCOMMAND: //Not sure why, if deactivated, the dc gets unassociated?
if( wParam == SC_RESTORE || wParam == SC_MAXIMIZE || wParam == SC_SCREENSAVE )
{
SelectObject( CNFGlsHDC, CNFGlsBitmap );
SelectObject( CNFGlsWindowHDC, CNFGlsBitmap );
}
break;
#endif
case WM_DESTROY:
HandleDestroy();
CNFGTearDown();
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen );
void CNFGSetupFullscreen( const char * WindowName, int screen_number )
{
// Get primary monitor dimensions, but default to 1920x1080
int monitorW = GetSystemMetrics(SM_CXSCREEN);
if(0 == monitorW)
{
monitorW = 1920;
}
int monitorH = GetSystemMetrics(SM_CYSCREEN);
if(0 == monitorH)
{
monitorH = 1080;
}
CNFGSetupWinInternal(WindowName, monitorW, monitorH, 1);
}
int CNFGSetup( const char * name_of_window, int width, int height )
{
return CNFGSetupWinInternal(name_of_window, width, height, 0);
}
//This was from the article, too... well, mostly.
int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen )
{
static LPCSTR szClassName = "MyClass";
RECT client, window;
WNDCLASSA wnd;
int w, h, wd, hd;
int show_window = 1;
HINSTANCE hInstance = GetModuleHandle(NULL);
if( width < 0 )
{
show_window = 0;
width = -width;
}
if( height < 0 )
{
show_window = 0;
height = -height;
}
CNFGBufferx = (short)width;
CNFGBuffery = (short)height;
wnd.style = CS_HREDRAW | CS_VREDRAW; //we will explain this later
wnd.lpfnWndProc = MyWndProc;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hInstance = hInstance;
wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); //default icon
wnd.hCursor = LoadCursor(NULL, IDC_ARROW); //default arrow mouse cursor
wnd.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wnd.lpszMenuName = NULL; //no menu
wnd.lpszClassName = szClassName;
if(!RegisterClassA(&wnd)) //register the WNDCLASS
{
MessageBoxA(NULL, "This Program Requires Windows NT", "Error", MB_OK);
}
#ifdef UNICODE
// CreateWindowA **requires** unicode window name even in non-unicode mode.
int wlen = strlen( name_of_window );
char * unicodeao = (char*)alloca( wlen * 2 + 2 );
int i;
for( i = 0; i <= wlen; i++ )
{
unicodeao[i * 2 + 1] = 0;
unicodeao[i * 2 + 0] = name_of_window[i];
}
name_of_window = unicodeao;
#endif
CNFGlsHWND = CreateWindowA(szClassName,
name_of_window, //name_of_window, but must always be
isFullscreen ? (WS_MAXIMIZE | WS_POPUP) : (WS_OVERLAPPEDWINDOW), //basic window style
CW_USEDEFAULT,
CW_USEDEFAULT, //set starting point to default value
CNFGBufferx,
CNFGBuffery, //set all the dimensions to default value
NULL, //no parent window
NULL, //no menu
hInstance,
NULL); //no parameters to pass
CNFGlsWindowHDC = GetDC( CNFGlsHWND );
#ifdef CNFGOGL
//From NeHe
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
8, 0, 8, 8, 8, 16,
8,
24,
32,
8, 8, 8, 8,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
GLuint PixelFormat = ChoosePixelFormat( CNFGlsWindowHDC, &pfd );
if( !SetPixelFormat( CNFGlsWindowHDC, PixelFormat, &pfd ) )
{
MessageBoxA( 0, "Could not create PFD for OpenGL Context\n", 0, 0 );
exit( -1 );
}
if (!(hRC=wglCreateContext(CNFGlsWindowHDC))) // Are We Able To Get A Rendering Context?
{
MessageBoxA( 0, "Could not create OpenGL Context\n", 0, 0 );
exit( -1 );
}
if(!wglMakeCurrent(CNFGlsWindowHDC,hRC)) // Try To Activate The Rendering Context
{
MessageBoxA( 0, "Could not current OpenGL Context\n", 0, 0 );
exit( -1 );
}
#endif
CNFGlsHDC = CreateCompatibleDC( CNFGlsWindowHDC );
CNFGlsHDCBlit = CreateCompatibleDC( CNFGlsWindowHDC );
CNFGlsBitmap = CreateCompatibleBitmap( CNFGlsWindowHDC, CNFGBufferx, CNFGBuffery );
SelectObject( CNFGlsHDC, CNFGlsBitmap );
//lsClearBrush = CreateSolidBrush( CNFGBGColor );
//lsHBR = CreateSolidBrush( 0xFFFFFF );
//lsHPEN = CreatePen( PS_SOLID, 0, 0xFFFFFF );
if( show_window )
ShowWindow(CNFGlsHWND, isFullscreen ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); //display the window on the screen
//Once set up... we have to change the window's borders so we get the client size right.
GetClientRect( CNFGlsHWND, &client );
GetWindowRect( CNFGlsHWND, &window );
w = ( window.right - window.left);
h = ( window.bottom - window.top);
wd = w - client.right;
hd = h - client.bottom;
MoveWindow( CNFGlsHWND, window.left, window.top, CNFGBufferx + wd, CNFGBuffery + hd, 1 );
InternalHandleResize();
#ifdef CNFG_BATCH
#ifndef CNFGCONTEXTONLY
CNFGSetupBatchInternal();
#endif
#endif
return 0;
}
int CNFGHandleInput()
{
#ifdef CNFGOGL
if (ShouldClose)
exit(0);
#endif
MSG msg;
while( PeekMessage( &msg, NULL, 0, 0xFFFF, 1 ) )
{
TranslateMessage(&msg);
switch( msg.message )
{
case WM_MOUSEMOVE:
HandleMotion( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, ( (msg.wParam & 0x01)?1:0) | ((msg.wParam & 0x02)?2:0) | ((msg.wParam & 0x10)?4:0) );
break;
case WM_LBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 1 ); break;
case WM_RBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 1 ); break;
case WM_MBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 1 ); break;
case WM_LBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 0 ); break;
case WM_RBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 0 ); break;
case WM_MBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 0 ); break;
case WM_KEYDOWN:
case WM_KEYUP:
if (msg.lParam & 0x01000000) HandleKey( (int) msg.wParam + 0x7C , (msg.message==WM_KEYDOWN) );
else HandleKey( (int) msg.wParam, (msg.message==WM_KEYDOWN) );
break;
case WM_MOUSEWHEEL:
{
POINT p = { 0 };
p.x = LOWORD( msg.lParam );
p.y = HIWORD( msg.lParam );
ScreenToClient(CNFGlsHWND, &p);
HandleButton(p.x, p.y, GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0 ? 0x0E : 0x0F, 1);
} break;
default:
DispatchMessage(&msg);
break;
}
}
return !ShouldClose;
}
#ifndef CNFGOGL
#ifndef CNFGRASTERIZER
static HBITMAP lsBackBitmap;
static HBRUSH lsHBR;
static HPEN lsHPEN;
static HBRUSH lsClearBrush;
static void InternalHandleResize()
{
DeleteObject( lsBackBitmap );
lsBackBitmap = CreateCompatibleBitmap( CNFGlsHDC, CNFGBufferx, CNFGBuffery );
SelectObject( CNFGlsHDC, lsBackBitmap );
}
#ifdef BATCH_ELEMENTS
static int linelisthead;
static int pointlisthead;
static int polylisthead;
static int polylistindex;
static POINT linelist[4096*3];
static DWORD twoarray[4096];
static POINT pointlist[4096];
static POINT polylist[8192];
static INT polylistcutoffs[8192];
static int last_linex;
static int last_liney;
static int possible_lastline;
void FlushTacking()
{
int i;
if( twoarray[0] != 2 )
for( i = 0; i < 4096; i++ ) twoarray[i] = 2;
if( linelisthead )
{
PolyPolyline( CNFGlsHDC, linelist, twoarray, linelisthead );
linelisthead = 0;
}
if( polylistindex )
{
PolyPolygon( CNFGlsHDC, polylist, polylistcutoffs, polylistindex );
polylistindex = 0;
polylisthead = 0;
}
if( possible_lastline )
CNFGTackPixel( last_linex, last_liney );
possible_lastline = 0;
//XXX TODO: Consider locking the bitmap, and manually drawing the pixels.
if( pointlisthead )
{
for( i = 0; i < pointlisthead; i++ )
{
SetPixel( CNFGlsHDC, pointlist[i].x, pointlist[i].y, CNFGLastColor );
}
pointlisthead = 0;
}
}
#endif
uint32_t CNFGColor( uint32_t RGB )
{
RGB = COLORSWAPS( RGB );
if( CNFGLastColor == RGB ) return RGB;
#ifdef BATCH_ELEMENTS
FlushTacking();
#endif
CNFGLastColor = RGB;
DeleteObject( lsHBR );
lsHBR = CreateSolidBrush( RGB );
SelectObject( CNFGlsHDC, lsHBR );
DeleteObject( lsHPEN );
lsHPEN = CreatePen( PS_SOLID, 0, RGB );
SelectObject( CNFGlsHDC, lsHPEN );
return RGB;
}
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
static int pbw, pbh;
static HBITMAP pbb;
if( !pbb || pbw != w || pbh !=h )
{
if( pbb ) DeleteObject( pbb );
pbb = CreateBitmap( w, h, 1, 32, 0 );
pbh = h;
pbw = w;
}
SetBitmapBits(pbb,w*h*4,data);
SelectObject( CNFGlsHDCBlit, pbb );
BitBlt(CNFGlsHDC, x, y, w, h, CNFGlsHDCBlit, 0, 0, SRCCOPY);
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
#ifdef BATCH_ELEMENTS
if( ( x1 != last_linex || y1 != last_liney ) && possible_lastline )
{
CNFGTackPixel( last_linex, last_liney );
}
if( x1 == x2 && y1 == y2 )
{
CNFGTackPixel( x1, y1 );
possible_lastline = 0;
return;
}
last_linex = x2;
last_liney = y2;
possible_lastline = 1;
if( x1 != x2 || y1 != y2 )
{
linelist[linelisthead*2+0].x = x1;
linelist[linelisthead*2+0].y = y1;
linelist[linelisthead*2+1].x = x2;
linelist[linelisthead*2+1].y = y2;
linelisthead++;
if( linelisthead >= 2048 ) FlushTacking();
}
#else
POINT pt[2] = { {x1, y1}, {x2, y2} };
Polyline( CNFGlsHDC, pt, 2 );
SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor );
SetPixel( CNFGlsHDC, x2, y2, CNFGLastColor );
#endif
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
#ifdef BATCH_ELEMENTS
FlushTacking();
#endif
RECT r;
if( x1 < x2 ) { r.left = x1; r.right = x2; }
else { r.left = x2; r.right = x1; }
if( y1 < y2 ) { r.top = y1; r.bottom = y2; }
else { r.top = y2; r.bottom = y1; }
FillRect( CNFGlsHDC, &r, lsHBR );
}
void CNFGClearFrame()
{
#ifdef BATCH_ELEMENTS
FlushTacking();
#endif
RECT r = { 0, 0, CNFGBufferx, CNFGBuffery };
DeleteObject( lsClearBrush );
lsClearBrush = CreateSolidBrush( COLORSWAPS(CNFGBGColor) );
HBRUSH prevBrush = SelectObject( CNFGlsHDC, lsClearBrush );
FillRect( CNFGlsHDC, &r, lsClearBrush);
SelectObject( CNFGlsHDC, prevBrush );
}
void CNFGTackPoly( RDPoint * points, int verts )
{
#ifdef BATCH_ELEMENTS
if( verts > 8192 )
{
FlushTacking();
//Fall-through
}
else
{
if( polylistindex >= 8191 || polylisthead + verts >= 8191 )
{
FlushTacking();
}
int i;
for( i = 0; i < verts; i++ )
{
polylist[polylisthead].x = points[i].x;
polylist[polylisthead].y = points[i].y;
polylisthead++;
}
polylistcutoffs[polylistindex++] = verts;
return;
}
#endif
{
int i;
POINT * t = (POINT*)alloca( sizeof( POINT ) * verts );
for( i = 0; i < verts; i++ )
{
t[i].x = points[i].x;
t[i].y = points[i].y;
}
Polygon( CNFGlsHDC, t, verts );
}
}
void CNFGTackPixel( short x1, short y1 )
{
#ifdef BATCH_ELEMENTS
pointlist[pointlisthead+0].x = x1;
pointlist[pointlisthead+0].y = y1;
pointlisthead++;
if( pointlisthead >=4096 ) FlushTacking();
#else
SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor );
#endif
}
void CNFGSwapBuffers()
{
#ifdef BATCH_ELEMENTS
FlushTacking();
#endif
int thisw, thish;
RECT r;
BitBlt( CNFGlsWindowHDC, 0, 0, CNFGBufferx, CNFGBuffery, CNFGlsHDC, 0, 0, SRCCOPY );
UpdateWindow( CNFGlsHWND );
//Check to see if the window is closed.
if( !IsWindow( CNFGlsHWND ) )
{
exit( 0 );
}
GetClientRect( CNFGlsHWND, &r );
thisw = r.right - r.left;
thish = r.bottom - r.top;
if( thisw != CNFGBufferx || thish != CNFGBuffery )
{
CNFGBufferx = (short)thisw;
CNFGBuffery = (short)thish;
InternalHandleResize();
}
}
void CNFGInternalResize( short bfx, short bfy ) { }
#endif
#endif
#endif // _CNFGWINDRIVER_C
#elif defined( EGL_LEAN_AND_MEAN )
//Copyright (c) 2011, 2017, 2018, 2020 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
//This driver cannot create an OpenGL Surface, but can be used for computing for background tasks.
//NOTE: This is a truly incomplete driver - if no EGL surface is available, it does not support direct buffer rendering.
//Additionally no input is connected.
#include <stdio.h>
#include <stdlib.h>
#include <GLES3/gl3.h>
#include <GLES3/gl32.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES2/gl2ext.h>
static const EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_NONE
};
EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
static int pbufferWidth = 0;
static int pbufferHeight = 0;
static EGLint pbufferAttribs[] = {
EGL_WIDTH, 0,
EGL_HEIGHT, 0,
EGL_NONE,
};
EGLDisplay eglDpy = 0;
EGLContext eglCtx = 0;
EGLSurface eglSurf = 0;
void CNFGGetDimensions( short * x, short * y )
{
*x = pbufferWidth;
*y = pbufferHeight;
}
void CNFGChangeWindowTitle( const char * WindowName )
{
}
void CNFGSetupFullscreen( const char * WindowName, int screen_no )
{
//Fullscreen is meaningless for this driver, since it doesn't really open a window.
CNFGSetup( WindowName, 1024, 1024 );
}
void CNFGTearDown()
{
if( eglDpy )
{
eglTerminate( eglDpy );
}
//Unimplemented.
}
int CNFGSetup( const char * WindowName, int w, int h )
{
eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
atexit( CNFGTearDown );
printf( "EGL Display: %p\n", eglDpy );
pbufferAttribs[1] = pbufferWidth = w;
pbufferAttribs[3] = pbufferHeight = h;
EGLint major, minor;
eglInitialize(eglDpy, &major, &minor);
EGLint numConfigs=0;
EGLConfig eglCfg=NULL;
eglChooseConfig(eglDpy, configAttribs, 0, 0, &numConfigs); //this gets number of configs
if (numConfigs) {
eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs);
printf( " EGL config found\n" );
} else {
printf( " Error could not find a valid config avail.. \n" );
}
printf( "EGL Major Minor: %d %d\n", major, minor );
eglBindAPI(EGL_OPENGL_API);
eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, context_attribs);
int err = eglGetError(); if(err != EGL_SUCCESS) { printf("1. Error %d\n", err); }
printf( "EGL Got context: %p\n", eglCtx );
if( w > 0 && h > 0 )
{
eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs);
eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx);
printf( "EGL Current, with surface %p\n", eglSurf );
//Actually have a surface. Need to allocate it.
EGLint surfwid;
EGLint surfht;
eglQuerySurface(eglDpy, eglSurf, EGL_WIDTH, &surfwid);
eglQuerySurface(eglDpy, eglSurf, EGL_HEIGHT, &surfht);
printf("Window dimensions: %d x %d\n", surfwid, surfht);
}
else
{
eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx);
printf( "EGL Current, no surface.\n" );
}
return 0;
}
int CNFGHandleInput()
{
//Stubbed (No input)
return 1;
}
void CNFGSetVSync( int vson )
{
//No-op
}
void CNFGSwapBuffers()
{
//No-op
}
#elif defined( __android__ ) || defined( ANDROID )
/*
* Copyright (c) 2011-2013 Luc Verhaegen <libv@skynet.be>
* Copyright (c) 2018-2020 <>< Charles Lohr
*
* 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, sub license,
* 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 (including the
* next paragraph) 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 NON-INFRINGEMENT. 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.
*/
#if defined( __android__ ) && !defined( ANDROID )
#define ANDROID
#endif
//Note: This interface provides the following two things privately.
//you may "extern" them in your code.
#ifdef ANDROID
#ifndef _CNFG_ANDROID_H
#define _CNFG_ANDROID_H
//This file contains the additional functions that are available on the Android platform.
//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID
// Tricky: Android headers are confused by c++ if linking statically.
#ifdef __cplusplus
extern "C" {
int __system_property_get(const char* __name, char* __value);
};
#endif
extern struct android_app * gapp;
void AndroidMakeFullscreen();
int AndroidHasPermissions(const char* perm_name);
void AndroidRequestAppPermissions(const char * perm);
void AndroidDisplayKeyboard(int pShow);
int AndroidGetUnicodeChar( int keyCode, int metaState );
void AndroidSendToBack( int param );
extern int android_sdk_version; //Derived at start from property ro.build.version.sdk
extern int android_width, android_height;
extern int UpdateScreenWithBitmapOffsetX;
extern int UpdateScreenWithBitmapOffsetY;
// If you need them, these are the names of raw EGL symbols.
//extern EGLDisplay egl_display;
//extern EGLSurface egl_surface;
//extern EGLContext egl_context;
//extern EGLConfig egl_config;
//You must implement these.
void HandleResume();
void HandleSuspend();
//Departures:
// HandleMotion's "mask" parameter is actually just an index, not a mask
// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation
// decoration is removed. Fullscreen means *full screen* To choose fullscreen
// or not fullscrene, modify, in your AndroidManifest.xml file, the application
// section to either contain or not contain:
// android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
#endif
struct android_app * gapp;
static int OGLESStarted;
int android_width, android_height;
int override_android_screen_dimensons = 0;
int android_sdk_version;
#include <android_native_app_glue.h>
#include <jni.h>
#include <android/native_activity.h>
#define ERRLOG(...) printf( __VA_ARGS__ );
#else
#define ERRLOG(...) fprintf( stderr, __VA_ARGS__ );
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <EGL/egl.h>
#ifdef ANDROID
#include <GLES3/gl3.h>
#else
#include <GLES2/gl2.h>
#endif
#define EGL_ZBITS 16
#define EGL_IMMEDIATE_SIZE 2048
#ifdef USE_EGL_X
#error This feature has never been completed or tested.
Display *XDisplay;
Window XWindow;
#else
typedef enum
{
FBDEV_PIXMAP_DEFAULT = 0,
FBDEV_PIXMAP_SUPPORTS_UMP = (1<<0),
FBDEV_PIXMAP_ALPHA_FORMAT_PRE = (1<<1),
FBDEV_PIXMAP_COLORSPACE_sRGB = (1<<2),
FBDEV_PIXMAP_EGL_MEMORY = (1<<3) /* EGL allocates/frees this memory */
} fbdev_pixmap_flags;
typedef struct fbdev_window
{
unsigned short width;
unsigned short height;
} fbdev_window;
typedef struct fbdev_pixmap
{
unsigned int height;
unsigned int width;
unsigned int bytes_per_pixel;
unsigned char buffer_size;
unsigned char red_size;
unsigned char green_size;
unsigned char blue_size;
unsigned char alpha_size;
unsigned char luminance_size;
fbdev_pixmap_flags flags;
unsigned short *data;
unsigned int format; /* extra format information in case rgbal is not enough, especially for YUV formats */
} fbdev_pixmap;
#if defined( ANDROID )
EGLNativeWindowType native_window;
#else
struct fbdev_window native_window;
#endif
#endif
static EGLint const config_attribute_list[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_BUFFER_SIZE, 32,
EGL_STENCIL_SIZE, 0,
EGL_DEPTH_SIZE, EGL_ZBITS,
//EGL_SAMPLES, 1,
#ifdef ANDROID
#if ANDROIDVERSION >= 28
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
#else
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#endif
#else
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PIXMAP_BIT,
#endif
EGL_NONE
};
static EGLint window_attribute_list[] = {
EGL_NONE
};
static const EGLint context_attribute_list[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLDisplay egl_display;
EGLSurface egl_surface;
EGLContext egl_context;
EGLConfig egl_config;
void CNFGSetVSync( int vson )
{
eglSwapInterval(egl_display, vson);
}
static short iLastInternalW, iLastInternalH;
void CNFGSwapBuffers()
{
CNFGFlushRender();
eglSwapBuffers(egl_display, egl_surface);
#ifdef ANDROID
if( !override_android_screen_dimensons )
{
android_width = ANativeWindow_getWidth( native_window );
android_height = ANativeWindow_getHeight( native_window );
}
glViewport( 0, 0, android_width, android_height );
if( iLastInternalW != android_width || iLastInternalH != android_height )
CNFGInternalResize( iLastInternalW=android_width, iLastInternalH=android_height );
#endif
}
void CNFGGetDimensions( short * x, short * y )
{
#ifdef ANDROID
*x = android_width;
*y = android_height;
#else
*x = native_window.width;
*y = native_window.height;
#endif
if( *x != iLastInternalW || *y != iLastInternalH )
CNFGInternalResize( iLastInternalW=*x, iLastInternalH=*y );
}
int CNFGSetup( const char * WindowName, int w, int h )
{
EGLint egl_major, egl_minor;
EGLint num_config;
//This MUST be called before doing any initialization.
int events;
while( !OGLESStarted )
{
struct android_poll_source* source;
if (ALooper_pollAll( 0, 0, &events, (void**)&source) >= 0)
{
if (source != NULL) source->process(gapp, source);
}
}
#ifdef USE_EGL_X
XDisplay = XOpenDisplay(NULL);
if (!XDisplay) {
ERRLOG( "Error: failed to open X display.\n");
return -1;
}
Window XRoot = DefaultRootWindow(XDisplay);
XSetWindowAttributes XWinAttr;
XWinAttr.event_mask = ExposureMask | PointerMotionMask;
XWindow = XCreateWindow(XDisplay, XRoot, 0, 0, WIDTH, HEIGHT, 0,
CopyFromParent, InputOutput,
CopyFromParent, CWEventMask, &XWinAttr);
Atom XWMDeleteMessage =
XInternAtom(XDisplay, "WM_DELETE_WINDOW", False);
XMapWindow(XDisplay, XWindow);
XStoreName(XDisplay, XWindow, "Mali libs test");
XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 1);
egl_display = eglGetDisplay((EGLNativeDisplayType) XDisplay);
#else
#ifndef ANDROID
if( w >= 1 && h >= 1 )
{
native_window.width = w;
native_window.height =h;
}
#endif
egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
#endif
if (egl_display == EGL_NO_DISPLAY) {
ERRLOG( "Error: No display found!\n");
return -1;
}
if (!eglInitialize(egl_display, &egl_major, &egl_minor)) {
ERRLOG( "Error: eglInitialise failed!\n");
return -1;
}
printf("EGL Version: \"%s\"\n",
eglQueryString(egl_display, EGL_VERSION));
printf("EGL Vendor: \"%s\"\n",
eglQueryString(egl_display, EGL_VENDOR));
printf("EGL Extensions: \"%s\"\n",
eglQueryString(egl_display, EGL_EXTENSIONS));
eglChooseConfig(egl_display, config_attribute_list, &egl_config, 1,
&num_config);
printf( "Config: %d\n", num_config );
printf( "Creating Context\n" );
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT,
// NULL );
context_attribute_list);
if (egl_context == EGL_NO_CONTEXT) {
ERRLOG( "Error: eglCreateContext failed: 0x%08X\n",
eglGetError());
return -1;
}
printf( "Context Created %p\n", egl_context );
#ifdef USE_EGL_X
egl_surface = eglCreateWindowSurface(egl_display, egl_config, XWindow,
window_attribute_list);
#else
if( native_window && !gapp->window )
{
printf( "WARNING: App restarted without a window. Cannot progress.\n" );
exit( 0 );
}
printf( "Getting Surface %p\n", native_window = gapp->window );
if( !native_window )
{
printf( "FAULT: Cannot get window\n" );
return -5;
}
if( w <= 0 || h <= 0 )
{
android_width = ANativeWindow_getWidth( native_window );
android_height = ANativeWindow_getHeight( native_window );
}
else
{
override_android_screen_dimensons = 1;
android_width = w;
android_height = h;
}
printf( "Width/Height: %dx%d\n", android_width, android_height );
egl_surface = eglCreateWindowSurface(egl_display, egl_config,
#ifdef ANDROID
gapp->window,
#else
(EGLNativeWindowType)&native_window,
#endif
window_attribute_list);
#endif
printf( "Got Surface: %p\n", egl_surface );
if (egl_surface == EGL_NO_SURFACE) {
ERRLOG( "Error: eglCreateWindowSurface failed: "
"0x%08X\n", eglGetError());
return -1;
}
#ifndef ANDROID
int width, height;
if (!eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &width) ||
!eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &height)) {
ERRLOG( "Error: eglQuerySurface failed: 0x%08X\n",
eglGetError());
return -1;
}
printf("Surface size: %dx%d\n", width, height);
native_window.width = width;
native_window.height = height;
#endif
if (!eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) {
ERRLOG( "Error: eglMakeCurrent() failed: 0x%08X\n",
eglGetError());
return -1;
}
printf("GL Vendor: \"%s\"\n", glGetString(GL_VENDOR));
printf("GL Renderer: \"%s\"\n", glGetString(GL_RENDERER));
printf("GL Version: \"%s\"\n", glGetString(GL_VERSION));
printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));
CNFGSetupBatchInternal();
{
short dummyx, dummyy;
CNFGGetDimensions( &dummyx, &dummyy );
}
return 0;
}
void CNFGSetupFullscreen( const char * WindowName, int screen_number )
{
//Removes decoration, must be called before setup.
AndroidMakeFullscreen();
CNFGSetup( WindowName, -1, -1 );
}
int debuga, debugb, debugc;
int32_t handle_input(struct android_app* app, AInputEvent* event)
{
#ifdef ANDROID
//Potentially do other things here.
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
{
int action = AMotionEvent_getAction( event );
int whichsource = action >> 8;
action &= AMOTION_EVENT_ACTION_MASK;
size_t pointerCount = AMotionEvent_getPointerCount(event);
for (size_t i = 0; i < pointerCount; ++i)
{
int x, y, index;
x = AMotionEvent_getX(event, i);
y = AMotionEvent_getY(event, i);
index = AMotionEvent_getPointerId( event, i );
if( action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_DOWN )
{
int id = index;
if( action == AMOTION_EVENT_ACTION_POINTER_DOWN && id != whichsource ) continue;
HandleButton( x, y, id, 1 );
ANativeActivity_showSoftInput( gapp->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED );
}
else if( action == AMOTION_EVENT_ACTION_POINTER_UP || action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL )
{
int id = index;
if( action == AMOTION_EVENT_ACTION_POINTER_UP && id != whichsource ) continue;
HandleButton( x, y, id, 0 );
}
else if( action == AMOTION_EVENT_ACTION_MOVE )
{
HandleMotion( x, y, index );
}
}
return 1;
}
else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
{
int code = AKeyEvent_getKeyCode(event);
#ifdef ANDROID_USE_SCANCODES
HandleKey( code, AKeyEvent_getAction(event) );
#else
int unicode = AndroidGetUnicodeChar( code, AMotionEvent_getMetaState( event ) );
if( unicode )
HandleKey( unicode, AKeyEvent_getAction(event) );
else
{
HandleKey( code, !AKeyEvent_getAction(event) );
return (code == 4)?1:0; //don't override functionality.
}
#endif
return 1;
}
#endif
return 0;
}
int CNFGHandleInput()
{
#ifdef ANDROID
int events;
struct android_poll_source* source;
while( ALooper_pollAll( 0, 0, &events, (void**)&source) >= 0 )
{
if (source != NULL)
{
source->process(gapp, source);
}
}
#endif
#ifdef USE_EGL_X
while (1) {
XEvent event;
XNextEvent(XDisplay, &event);
if ((event.type == MotionNotify) ||
(event.type == Expose))
Redraw(width, height);
else if (event.type == ClientMessage) {
if (event.xclient.data.l[0] == XWMDeleteMessage)
break;
}
}
XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 0);
#endif
return 1;
}
#ifdef ANDROID
void handle_cmd(struct android_app* app, int32_t cmd)
{
switch (cmd)
{
case APP_CMD_DESTROY:
//This gets called initially after back.
HandleDestroy();
ANativeActivity_finish( gapp->activity );
break;
case APP_CMD_INIT_WINDOW:
//When returning from a back button suspension, this isn't called.
if( !OGLESStarted )
{
OGLESStarted = 1;
printf( "Got start event\n" );
}
else
{
CNFGSetup( "", -1, -1 );
HandleResume();
}
break;
//case APP_CMD_TERM_WINDOW:
//This gets called initially when you click "back"
//This also gets called when you are brought into standby.
//Not sure why - callbacks here seem to break stuff.
// break;
default:
printf( "event not handled: %d", cmd);
}
}
int __system_property_get(const char* name, char* value);
void android_main(struct android_app* app)
{
int main( int argc, char ** argv );
char mainptr[5] = { 'm', 'a', 'i', 'n', 0 };
char * argv[] = { mainptr, 0 };
{
char sdk_ver_str[92];
int len = __system_property_get("ro.build.version.sdk", sdk_ver_str);
if( len <= 0 )
android_sdk_version = 0;
else
android_sdk_version = atoi(sdk_ver_str);
}
gapp = app;
app->onAppCmd = handle_cmd;
app->onInputEvent = handle_input;
printf( "Starting with Android SDK Version: %d\n", android_sdk_version );
main( 1, argv );
printf( "Main Complete\n" );
}
#ifdef __cplusplus
#define SETUP_FOR_JAVA_CALL \
JNIEnv * env = 0; \
JNIEnv ** envptr = &env; \
JavaVM * jniiptr = gapp->activity->vm; \
jniiptr->AttachCurrentThread( (JNIEnv**)&env, 0 ); \
env = (*envptr);
#define ENVCALL
#define JAVA_CALL_DETACH jniiptr->DetachCurrentThread();
#else
#define SETUP_FOR_JAVA_CALL \
const struct JNINativeInterface * env = (struct JNINativeInterface*)gapp->activity->env; \
const struct JNINativeInterface ** envptr = &env; \
const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; \
const struct JNIInvokeInterface * jnii = *jniiptr; \
jnii->AttachCurrentThread( jniiptr, &envptr, NULL); \
env = (*envptr);
#define ENVCALL envptr,
#define JAVA_CALL_DETACH jnii->DetachCurrentThread( jniiptr );
#endif
void AndroidMakeFullscreen()
{
//Partially based on https://stackoverflow.com/questions/47507714/how-do-i-enable-full-screen-immersive-mode-for-a-native-activity-ndk-app
SETUP_FOR_JAVA_CALL
//Get android.app.NativeActivity, then get getWindow method handle, returns view.Window type
jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity");
jmethodID getWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;");
jobject window = env->CallObjectMethod( ENVCALL gapp->activity->clazz, getWindow);
jclass windowClass = env->FindClass( ENVCALL "android/view/Window");
jmethodID getDecorView = env->GetMethodID( ENVCALL windowClass, "getDecorView", "()Landroid/view/View;");
jobject decorView = env->CallObjectMethod( ENVCALL window, getDecorView);
/*
jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" );
const int flag_WindowProp = env->GetStaticIntField( ENVCALL windowClass, env->GetStaticFieldID( ENVCALL windowClass, "FEATURE_NO_TITLE", "I") );
jmethodID requestWindowFeature = env->GetMethodID( ENVCALL ClassActivity, "requestWindowFeature", "(I)Z" );
jobject lNativeActivity = gapp->activity->clazz;
env->CallBooleanMethod( ENVCALL lNativeActivity, requestWindowFeature, flag_WindowProp );
*/
//Get the flag values associated with systemuivisibility
jclass viewClass = env->FindClass( ENVCALL "android/view/View");
const int flagLayoutHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I"));
const int flagLayoutFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I"));
const int flagLowProfile = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LOW_PROFILE", "I"));
const int flagHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I"));
const int flagFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I"));
const int flagImmersiveSticky = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I"));
const int flagLayoutStable = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE", "I"));
jmethodID setSystemUiVisibility = env->GetMethodID( ENVCALL viewClass, "setSystemUiVisibility", "(I)V");
//Call the decorView.setSystemUiVisibility(FLAGS)
env->CallVoidMethod( ENVCALL decorView, setSystemUiVisibility,
(flagLayoutHideNavigation | flagLayoutFullscreen | flagLowProfile | flagHideNavigation | flagFullscreen | flagImmersiveSticky | flagLayoutStable));
//now set some more flags associated with layoutmanager -- note the $ in the class path
//search for api-versions.xml
//https://android.googlesource.com/platform/development/+/refs/tags/android-9.0.0_r48/sdk/api-versions.xml
jclass layoutManagerClass = env->FindClass( ENVCALL "android/view/WindowManager$LayoutParams");
const int flag_WinMan_Fullscreen = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_FULLSCREEN", "I") ));
const int flag_WinMan_KeepScreenOn = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_KEEP_SCREEN_ON", "I") ));
const int flag_WinMan_hw_acc = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_HARDWARE_ACCELERATED", "I") ));
const int flag_WinMan_NoLimits = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_LAYOUT_NO_LIMITS", "I") ));
// const int flag_WinMan_flag_not_fullscreen = env->GetStaticIntField(layoutManagerClass, (env->GetStaticFieldID(layoutManagerClass, "FLAG_FORCE_NOT_FULLSCREEN", "I") ));
//call window.addFlags(FLAGS)
env->CallVoidMethod( ENVCALL window, (env->GetMethodID (ENVCALL windowClass, "addFlags" , "(I)V")), (flag_WinMan_Fullscreen | flag_WinMan_KeepScreenOn | flag_WinMan_hw_acc | flag_WinMan_NoLimits));
/*
// Seems to have no impact, and doesn't work with older Android versions.
jmethodID setDecorFitsSystemWindows = env->GetMethodID( ENVCALL windowClass, "setDecorFitsSystemWindows", "(Z)V");
env->CallVoidMethod( ENVCALL window, setDecorFitsSystemWindows, JNI_FALSE );
// "Immersive Mode" (Since Android 11+)
jmethodID getWindowInsetsController = env->GetMethodID( ENVCALL viewClass, "getWindowInsetsController", "()Landroid/view/WindowInsetsController;" );
if( getWindowInsetsController )
{
//windowInsetsController.hide(Type.systemBars())
jobject windowInsetsController = env->CallObjectMethod( ENVCALL decorView, getWindowInsetsController );
jclass windowInsetsControllerClass = env->FindClass( ENVCALL "android/view/WindowInsetsController" );
jclass windowInsetsTypeClass = env->FindClass( ENVCALL "android/view/WindowInsets/Type" );
jmethodID typeSystemBars = env->GetStaticMethodID( ENVCALL windowInsetsTypeClass, "systemBars", "()I" );
int systemBarsType = env->CallStaticIntMethod( ENVCALL windowInsetsTypeClass, typeSystemBars );
jmethodID hide = env->GetMethodID( ENVCALL windowInsetsControllerClass, "hide", "(I)V" );
env->CallVoidMethod( ENVCALL windowInsetsController, hide, systemBarsType );
}
*/
JAVA_CALL_DETACH
}
void AndroidDisplayKeyboard(int pShow)
{
//Based on https://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity
jint lFlags = 0;
SETUP_FOR_JAVA_CALL
jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity");
// Retrieves NativeActivity.
jobject lNativeActivity = gapp->activity->clazz;
// Retrieves Context.INPUT_METHOD_SERVICE.
jclass ClassContext = env->FindClass( ENVCALL "android/content/Context");
jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID( ENVCALL ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;" );
jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField( ENVCALL ClassContext, FieldINPUT_METHOD_SERVICE );
// Runs getSystemService(Context.INPUT_METHOD_SERVICE).
jclass ClassInputMethodManager = env->FindClass( ENVCALL "android/view/inputmethod/InputMethodManager" );
jmethodID MethodGetSystemService = env->GetMethodID( ENVCALL activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
jobject lInputMethodManager = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
// Runs getWindow().getDecorView().
jmethodID MethodGetWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;");
jobject lWindow = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetWindow);
jclass ClassWindow = env->FindClass( ENVCALL "android/view/Window");
jmethodID MethodGetDecorView = env->GetMethodID( ENVCALL ClassWindow, "getDecorView", "()Landroid/view/View;");
jobject lDecorView = env->CallObjectMethod( ENVCALL lWindow, MethodGetDecorView);
if (pShow) {
// Runs lInputMethodManager.showSoftInput(...).
jmethodID MethodShowSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
/*jboolean lResult = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags);
} else {
// Runs lWindow.getViewToken()
jclass ClassView = env->FindClass( ENVCALL "android/view/View");
jmethodID MethodGetWindowToken = env->GetMethodID( ENVCALL ClassView, "getWindowToken", "()Landroid/os/IBinder;");
jobject lBinder = env->CallObjectMethod( ENVCALL lDecorView, MethodGetWindowToken);
// lInputMethodManager.hideSoftInput(...).
jmethodID MethodHideSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z");
/*jboolean lRes = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodHideSoftInput, lBinder, lFlags);
}
JAVA_CALL_DETACH
}
int AndroidGetUnicodeChar( int keyCode, int metaState )
{
//https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c/43871301
int eventType = AKEY_EVENT_ACTION_DOWN;
SETUP_FOR_JAVA_CALL
//jclass activityClass = env->FindClass( envptr, "android/app/NativeActivity");
// Retrieves NativeActivity.
//jobject lNativeActivity = gapp->activity->clazz;
jclass class_key_event = env->FindClass( ENVCALL "android/view/KeyEvent");
int unicodeKey;
jmethodID method_get_unicode_char = env->GetMethodID( ENVCALL class_key_event, "getUnicodeChar", "(I)I");
jmethodID eventConstructor = env->GetMethodID( ENVCALL class_key_event, "<init>", "(II)V");
jobject eventObj = env->NewObject( ENVCALL class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = env->CallIntMethod( ENVCALL eventObj, method_get_unicode_char, metaState );
// Finished with the JVM.
JAVA_CALL_DETACH
//printf("Unicode key is: %d", unicodeKey);
return unicodeKey;
}
//Based on: https://stackoverflow.com/questions/41820039/jstringjni-to-stdstringc-with-utf8-characters
#ifdef __cplusplus
jstring android_permission_name(JNIEnv ** envptr, const char* perm_name)
#else
jstring android_permission_name(const struct JNINativeInterface ** envptr, const char* perm_name)
#endif
{
// nested class permission in class android.Manifest,
// hence android 'slash' Manifest 'dollar' permission
#ifdef __cplusplus
JNIEnv * env = *envptr;
#else
const struct JNINativeInterface * env = *envptr;
#endif
jclass ClassManifestpermission = env->FindClass( ENVCALL "android/Manifest$permission");
jfieldID lid_PERM = env->GetStaticFieldID( ENVCALL ClassManifestpermission, perm_name, "Ljava/lang/String;" );
jstring ls_PERM = (jstring)(env->GetStaticObjectField( ENVCALL ClassManifestpermission, lid_PERM ));
return ls_PERM;
}
/**
* \brief Tests whether a permission is granted.
* \param[in] app a pointer to the android app.
* \param[in] perm_name the name of the permission, e.g.,
* "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
* \retval true if the permission is granted.
* \retval false otherwise.
* \note Requires Android API level 23 (Marshmallow, May 2015)
*/
int AndroidHasPermissions( const char* perm_name)
{
struct android_app* app = gapp;
SETUP_FOR_JAVA_CALL
if( android_sdk_version < 23 )
{
printf( "Android SDK version %d does not support AndroidHasPermissions\n", android_sdk_version );
return 1;
}
int result = 0;
jstring ls_PERM = android_permission_name(envptr, perm_name);
jint PERMISSION_GRANTED = (-1);
{
jclass ClassPackageManager = env->FindClass( ENVCALL "android/content/pm/PackageManager" );
jfieldID lid_PERMISSION_GRANTED = env->GetStaticFieldID( ENVCALL ClassPackageManager, "PERMISSION_GRANTED", "I" );
PERMISSION_GRANTED = env->GetStaticIntField( ENVCALL ClassPackageManager, lid_PERMISSION_GRANTED );
}
{
jobject activity = app->activity->clazz;
jclass ClassContext = env->FindClass( ENVCALL "android/content/Context" );
jmethodID MethodcheckSelfPermission = env->GetMethodID( ENVCALL ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I" );
jint int_result = env->CallIntMethod( ENVCALL activity, MethodcheckSelfPermission, ls_PERM );
result = (int_result == PERMISSION_GRANTED);
}
JAVA_CALL_DETACH
return result;
}
/**
* \brief Query file permissions.
* \details This opens the system dialog that lets the user
* grant (or deny) the permission.
* \param[in] app a pointer to the android app.
* \note Requires Android API level 23 (Marshmallow, May 2015)
*/
void AndroidRequestAppPermissions(const char * perm)
{
if( android_sdk_version < 23 )
{
printf( "Android SDK version %d does not support AndroidRequestAppPermissions\n",android_sdk_version );
return;
}
struct android_app* app = gapp;
SETUP_FOR_JAVA_CALL
jobject activity = app->activity->clazz;
jobjectArray perm_array = env->NewObjectArray( ENVCALL 1, env->FindClass( ENVCALL "java/lang/String"), env->NewStringUTF( ENVCALL "" ) );
env->SetObjectArrayElement( ENVCALL perm_array, 0, android_permission_name(envptr, perm ) );
jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" );
jmethodID MethodrequestPermissions = env->GetMethodID( ENVCALL ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V" );
// Last arg (0) is just for the callback (that I do not use)
env->CallVoidMethod( ENVCALL activity, MethodrequestPermissions, perm_array, 0 );
JAVA_CALL_DETACH
}
/* Example:
int hasperm = android_has_permission( "RECORD_AUDIO" );
if( !hasperm )
{
android_request_app_permissions( "RECORD_AUDIO" );
}
*/
void AndroidSendToBack( int param )
{
struct android_app* app = gapp;
SETUP_FOR_JAVA_CALL
jobject activity = app->activity->clazz;
//_glfmCallJavaMethodWithArgs(jni, gapp->activity->clazz, "moveTaskToBack", "(Z)Z", Boolean, false);
jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" );
jmethodID MethodmoveTaskToBack = env->GetMethodID( ENVCALL ClassActivity, "moveTaskToBack", "(Z)Z" );
env->CallBooleanMethod( ENVCALL activity, MethodmoveTaskToBack, param );
JAVA_CALL_DETACH
}
#endif
#else
//Copyright (c) 2011, 2017, 2018 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
//portions from
//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html
//#define HAS_XINERAMA
//#define CNFG_HAS_XSHAPE
//#define FULL_SCREEN_STEAL_FOCUS
#ifndef _CNFGXDRIVER_C
#define _CNFGXDRIVER_C
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#ifdef HAS_XINERAMA
#include <X11/extensions/shape.h>
#include <X11/extensions/Xinerama.h>
#endif
#ifdef CNFG_HAS_XSHAPE
#include <X11/extensions/shape.h>
static XGCValues xsval;
static Pixmap xspixmap;
static GC xsgc;
static int taint_shape;
static int prepare_xshape;
static int was_transp;
#endif
#ifdef CNFG_BATCH
void CNFGSetupBatchInternal();
#endif
XWindowAttributes CNFGWinAtt;
XClassHint *CNFGClassHint;
char * wm_res_name = 0;
char * wm_res_class = 0;
Display *CNFGDisplay;
Window CNFGWindow;
int CNFGWindowInvisible;
Pixmap CNFGPixmap;
GC CNFGGC;
GC CNFGWindowGC;
int CNFGDepth;
int CNFGScreen;
Visual * CNFGVisual;
VisualID CNFGVisualID;
#ifdef CNFGOGL
#include <GL/glx.h>
#include <GL/glxext.h>
GLXContext CNFGCtx;
void * CNFGGetExtension( const char * extname ) { return (void*)glXGetProcAddressARB((const GLubyte *) extname); }
GLXFBConfig CNFGGLXFBConfig;
void CNFGGLXSetup( )
{
int attribs[] = {
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DEPTH_SIZE, 1,
None };
int elements = 0;
GLXFBConfig * cfgs = glXChooseFBConfig( CNFGDisplay, CNFGScreen, attribs, &elements );
if( elements == 0 )
{
fprintf( stderr, "Error: could not get valid GLXFBConfig visual.\n" );
exit( -1 );
}
CNFGGLXFBConfig = cfgs[0];
XVisualInfo * vis = glXGetVisualFromFBConfig( CNFGDisplay, CNFGGLXFBConfig );
CNFGVisual = vis->visual;
CNFGVisualID = vis->visualid;
CNFGDepth = vis->depth;
CNFGCtx = glXCreateContext( CNFGDisplay, vis, NULL, True );
}
#endif
int CNFGX11ForceNoDecoration;
XImage *xi;
int g_x_global_key_state;
int g_x_global_shift_key;
void CNFGSetWindowIconData( int w, int h, uint32_t * data )
{
static Atom net_wm_icon;
static Atom cardinal;
if( !net_wm_icon ) net_wm_icon = XInternAtom( CNFGDisplay, "_NET_WM_ICON", False );
if( !cardinal ) cardinal = XInternAtom( CNFGDisplay, "CARDINAL", False );
unsigned long outdata[w*h];
int i;
for( i = 0; i < w*h; i++ )
{
outdata[i+2] = data[i];
}
outdata[0] = w;
outdata[1] = h;
XChangeProperty(CNFGDisplay, CNFGWindow, net_wm_icon, cardinal,
32, PropModeReplace, (const unsigned char*)outdata, 2 + w*h);
}
#ifdef CNFG_HAS_XSHAPE
void CNFGPrepareForTransparency() { prepare_xshape = 1; }
void CNFGDrawToTransparencyMode( int transp )
{
static Pixmap BackupCNFGPixmap;
static GC BackupCNFGGC;
if( was_transp && ! transp )
{
CNFGGC = BackupCNFGGC;
CNFGPixmap = BackupCNFGPixmap;
}
if( !was_transp && transp )
{
BackupCNFGPixmap = CNFGPixmap;
BackupCNFGGC = CNFGGC;
taint_shape = 1;
CNFGGC = xsgc;
CNFGPixmap = xspixmap;
}
was_transp = transp;
}
void CNFGClearTransparencyLevel()
{
taint_shape = 1;
XSetForeground(CNFGDisplay, xsgc, 0);
XFillRectangle(CNFGDisplay, xspixmap, xsgc, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height);
XSetForeground(CNFGDisplay, xsgc, 1);
}
#endif
int FullScreen = 0;
void CNFGGetDimensions( short * x, short * y )
{
static int lastx;
static int lasty;
*x = CNFGWinAtt.width;
*y = CNFGWinAtt.height;
if( lastx != *x || lasty != *y )
{
lastx = *x;
lasty = *y;
#ifndef CNFGCONTEXTONLY
CNFGInternalResize( lastx, lasty );
#endif
}
}
void CNFGChangeWindowTitle( const char * WindowName )
{
XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, 0, 0, 0, 0, 0 );
}
static void InternalLinkScreenAndGo( const char * WindowName )
{
XFlush(CNFGDisplay);
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
if( !wm_res_name ) wm_res_name = strdup( "rawdraw" );
if( !wm_res_class ) wm_res_class = strdup( "rawdraw" );
XGetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint );
if (!CNFGClassHint) {
CNFGClassHint = XAllocClassHint();
if (CNFGClassHint) {
CNFGClassHint->res_name = wm_res_name;
CNFGClassHint->res_class = wm_res_class;
XSetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint );
} else {
fprintf( stderr, "Failed to allocate XClassHint!\n" );
}
} else {
fprintf( stderr, "Pre-existing XClassHint\n" );
}
XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask );
CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0);
if( CNFGX11ForceNoDecoration )
{
Atom window_type = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE_SPLASH", False);
XChangeProperty(CNFGDisplay, CNFGWindow, window_type,
XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 );
}
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
XSetLineAttributes(CNFGDisplay, CNFGGC, 1, LineSolid, CapRound, JoinRound);
CNFGChangeWindowTitle( WindowName );
if( !CNFGWindowInvisible )
XMapWindow(CNFGDisplay, CNFGWindow);
#ifdef CNFG_HAS_XSHAPE
if( prepare_xshape )
{
xsval.foreground = 1;
xsval.line_width = 1;
xsval.line_style = LineSolid;
xspixmap = XCreatePixmap(CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, 1);
xsgc = XCreateGC(CNFGDisplay, xspixmap, 0, &xsval);
XSetLineAttributes(CNFGDisplay, xsgc, 1, LineSolid, CapRound, JoinRound);
}
#endif
}
void CNFGSetupFullscreen( const char * WindowName, int screen_no )
{
#ifdef HAS_XINERAMA
XineramaScreenInfo *screeninfo = NULL;
int screens;
int event_basep, error_basep, a, b;
CNFGDisplay = XOpenDisplay(NULL);
CNFGScreen = XDefaultScreen(CNFGDisplay);
int xpos, ypos;
if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) {
fprintf( stderr, "X-Server does not support shape extension\n" );
exit( 1 );
}
CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen);
CNFGVisualID = 0;
CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, CNFGScreen);
#ifdef CNFGOGL
CNFGGLXSetup();
#endif
if (XineramaQueryExtension(CNFGDisplay, &a, &b ) &&
(screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) &&
XineramaIsActive(CNFGDisplay) && screen_no >= 0 &&
screen_no < screens ) {
CNFGWinAtt.width = screeninfo[screen_no].width;
CNFGWinAtt.height = screeninfo[screen_no].height;
xpos = screeninfo[screen_no].x_org;
ypos = screeninfo[screen_no].y_org;
} else
{
CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, CNFGScreen);
CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, CNFGScreen);
xpos = 0;
ypos = 0;
}
if (screeninfo)
XFree(screeninfo);
XSetWindowAttributes setwinattr;
setwinattr.override_redirect = 1;
setwinattr.save_under = 1;
#ifdef CNFG_HAS_XSHAPE
if (prepare_xshape && !XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep))
{
fprintf( stderr, "X-Server does not support shape extension" );
exit( 1 );
}
setwinattr.event_mask = 0;
#else
//This code is probably made irrelevant by the XSetEventMask in InternalLinkScreenAndGo, if this code is not found needed by 2019-12-31, please remove.
//setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask;
#endif
setwinattr.border_pixel = 0;
setwinattr.colormap = XCreateColormap( CNFGDisplay, RootWindow(CNFGDisplay, 0), CNFGVisual, AllocNone);
CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, CNFGScreen),
xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height,
0, CNFGWinAtt.depth, InputOutput, CNFGVisual,
CWBorderPixel/* | CWEventMask */ | CWOverrideRedirect | CWSaveUnder | CWColormap,
&setwinattr);
FullScreen = 1;
InternalLinkScreenAndGo( WindowName );
#ifdef CNFGOGL
glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx );
#endif
#ifdef CNFG_BATCH
#ifndef CNFGCONTEXTONLY
CNFGSetupBatchInternal();
#endif
#endif
#else
CNFGSetup( WindowName, 640, 480 );
#endif
}
void CNFGTearDown()
{
HandleDestroy();
if( xi ) free( xi );
if ( CNFGClassHint ) XFree( CNFGClassHint );
if ( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC );
if ( CNFGWindowGC ) XFreeGC( CNFGDisplay, CNFGWindowGC );
if ( CNFGDisplay ) XCloseDisplay( CNFGDisplay );
CNFGDisplay = NULL;
CNFGWindowGC = CNFGGC = NULL;
CNFGClassHint = NULL;
}
int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ )
{
wm_res_name = wm_res_name_;
wm_res_class = wm_res_class_;
return CNFGSetup( WindowName, w, h);
}
int CNFGSetup( const char * WindowName, int w, int h )
{
CNFGDisplay = XOpenDisplay(NULL);
if ( !CNFGDisplay ) {
fprintf( stderr, "Could not get an X Display.\n%s",
"Are you in text mode or using SSH without X11-Forwarding?\n" );
exit( 1 );
}
atexit( CNFGTearDown );
CNFGScreen = DefaultScreen(CNFGDisplay);
CNFGDepth = DefaultDepth(CNFGDisplay, CNFGScreen);
CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen);
CNFGVisualID = 0;
Window wnd = DefaultRootWindow( CNFGDisplay );
#ifdef CNFGOGL
CNFGGLXSetup( );
#endif
XSetWindowAttributes attr;
attr.background_pixel = 0;
attr.colormap = XCreateColormap( CNFGDisplay, wnd, CNFGVisual, AllocNone);
if( w > 0 && h > 0 )
CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr );
else
{
if( w < 0 ) w = -w;
if( h < 0 ) h = -h;
CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr );
CNFGWindowInvisible = 1;
}
InternalLinkScreenAndGo( WindowName );
//Not sure of the purpose of this code - if it's still commented out after 2019-12-31 and no one knows why, please delete it.
Atom WM_DELETE_WINDOW = XInternAtom( CNFGDisplay, "WM_DELETE_WINDOW", False );
XSetWMProtocols( CNFGDisplay, CNFGWindow, &WM_DELETE_WINDOW, 1 );
#ifdef CNFGOGL
glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx );
#endif
#ifdef CNFG_BATCH
#ifndef CNFGCONTEXTONLY
CNFGSetupBatchInternal();
#endif
#endif
return 0;
}
int CNFGHandleInput()
{
if( !CNFGWindow ) return 0;
static int ButtonsDown;
XEvent report;
int bKeyDirection = 1;
while( XPending( CNFGDisplay ) )
{
XNextEvent( CNFGDisplay, &report );
bKeyDirection = 1;
switch (report.type)
{
case NoExpose:
break;
case Expose:
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap );
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC );
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
HandleKey( CNFG_X11_EXPOSE, 0 );
break;
case KeyRelease:
{
bKeyDirection = 0;
//Tricky - handle key repeats cleanly.
if( XPending( CNFGDisplay ) )
{
XEvent nev;
XPeekEvent( CNFGDisplay, &nev );
if (nev.type == KeyPress && nev.xkey.time == report.xkey.time && nev.xkey.keycode == report.xkey.keycode )
bKeyDirection = 2;
}
}
case KeyPress:
g_x_global_key_state = report.xkey.state;
g_x_global_shift_key = XLookupKeysym(&report.xkey, 1);
HandleKey( XLookupKeysym(&report.xkey, 0), bKeyDirection );
break;
case ButtonRelease:
bKeyDirection = 0;
case ButtonPress:
HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection );
ButtonsDown = (ButtonsDown & (~(1<<report.xbutton.button))) | ( bKeyDirection << report.xbutton.button );
//Intentionall fall through -- we want to send a motion in event of a button as well.
case MotionNotify:
HandleMotion( report.xmotion.x, report.xmotion.y, ButtonsDown>>1 );
break;
case ClientMessage:
// Only subscribed to WM_DELETE_WINDOW, so return 0 to let user know of window exit
return 0;
break;
default:
break;
//printf( "Event: %d\n", report.type );
}
}
return 1;
}
#ifdef CNFGOGL
void CNFGSetVSync( int vson )
{
void (*glfn)( int );
glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalMESA" ); if( glfn ) glfn( vson );
glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalSGI" ); if( glfn ) glfn( vson );
glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalEXT" ); if( glfn ) glfn( vson );
}
#ifdef CNFGRASTERIZER
void CNFGSwapBuffersInternal()
#else
void CNFGSwapBuffers()
#endif
{
if( CNFGWindowInvisible ) return;
#ifndef CNFGRASTERIZER
#ifndef CNFGCONTEXTONLY
CNFGFlushRender();
#endif
#endif
#ifdef CNFG_HAS_XSHAPE
if( taint_shape )
{
XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet);
taint_shape = 0;
}
#endif //CNFG_HAS_XSHAPE
glXSwapBuffers( CNFGDisplay, CNFGWindow );
#ifdef FULL_SCREEN_STEAL_FOCUS
if( FullScreen )
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
#endif //FULL_SCREEN_STEAL_FOCUS
}
#else //CNFGOGL
#ifndef CNFGRASTERIZER
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
static int lw, lh;
if( lw != w || lh != h || !xi )
{
if( xi ) free( xi );
xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
lw = w;
lh = h;
}
//Draw image to pixmap (not a screen flip)
XPutImage(CNFGDisplay, CNFGPixmap, CNFGGC, xi, 0, 0, x, y, w, h );
}
#endif
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
{
static int lw, lh;
if( lw != w || lh != h )
{
if( xi ) free( xi );
xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
lw = w;
lh = h;
}
//Directly write image to screen (effectively a flip)
XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h );
}
#endif //CNFGOGL
#if !defined( CNFGOGL)
#define AGLF(x) x
#else
#define AGLF(x) static inline BACKEND_##x
#endif
#if defined( CNFGRASTERIZER )
//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin.
#ifdef CNFGRASTERIZER
//#include <stdlib.h>
#include <stdint.h>
uint32_t * CNFGBuffer = 0;
short CNFGBufferx;
short CNFGBuffery;
#ifdef CNFGOGL
void CNFGFlushRender()
{
}
#endif
void CNFGInternalResize( short x, short y )
{
CNFGBufferx = x;
CNFGBuffery = y;
if( CNFGBuffer ) free( CNFGBuffer );
CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 );
#ifdef CNFGOGL
void CNFGInternalResizeOGLBACKEND( short w, short h );
CNFGInternalResizeOGLBACKEND( x, y );
#endif
}
#ifdef __wasm__
static uint32_t SWAPS( uint32_t r )
{
uint32_t ret = (r&0xFF)<<24;
r>>=8;
ret |= (r&0xff)<<16;
r>>=8;
ret |= (r&0xff)<<8;
r>>=8;
ret |= (r&0xff)<<0;
return ret;
}
#elif !defined(CNFGOGL)
#define SWAPS(x) (x>>8)
#else
static uint32_t SWAPS( uint32_t r )
{
uint32_t ret = (r&0xFF)<<16;
r>>=8;
ret |= (r&0xff)<<8;
r>>=8;
ret |= (r&0xff);
r>>=8;
ret |= (r&0xff)<<24;
return ret;
}
#endif
uint32_t CNFGColor( uint32_t RGB )
{
CNFGLastColor = SWAPS(RGB);
return CNFGLastColor;
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
short tx, ty;
//float slope, lp;
float slope;
short dx = x2 - x1;
short dy = y2 - y1;
if( !CNFGBuffer ) return;
if( dx < 0 ) dx = -dx;
if( dy < 0 ) dy = -dy;
if( dx > dy )
{
short minx = (x1 < x2)?x1:x2;
short maxx = (x1 < x2)?x2:x1;
short miny = (x1 < x2)?y1:y2;
short maxy = (x1 < x2)?y2:y1;
float thisy = miny;
slope = (float)(maxy-miny) / (float)(maxx-minx);
for( tx = minx; tx <= maxx; tx++ )
{
ty = thisy;
if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue;
if( tx >= CNFGBufferx ) break;
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
thisy += slope;
}
}
else
{
short minx = (y1 < y2)?x1:x2;
short maxx = (y1 < y2)?x2:x1;
short miny = (y1 < y2)?y1:y2;
short maxy = (y1 < y2)?y2:y1;
float thisx = minx;
slope = (float)(maxx-minx) / (float)(maxy-miny);
for( ty = miny; ty <= maxy; ty++ )
{
tx = thisx;
if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue;
if( ty >= CNFGBuffery ) break;
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
thisx += slope;
}
}
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
short minx = (x1<x2)?x1:x2;
short miny = (y1<y2)?y1:y2;
short maxx = (x1>=x2)?x1:x2;
short maxy = (y1>=y2)?y1:y2;
short x, y;
if( minx < 0 ) minx = 0;
if( miny < 0 ) miny = 0;
if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1;
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
for( y = miny; y <= maxy; y++ )
{
uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx];
for( x = minx; x <= maxx; x++ )
{
(*CNFGBufferstart++) = CNFGLastColor;
}
}
}
void CNFGTackPoly( RDPoint * points, int verts )
{
short minx = 10000, miny = 10000;
short maxx =-10000, maxy =-10000;
short i, x, y;
//Just in case...
if( verts > 32767 ) return;
for( i = 0; i < verts; i++ )
{
RDPoint * p = &points[i];
if( p->x < minx ) minx = p->x;
if( p->y < miny ) miny = p->y;
if( p->x > maxx ) maxx = p->x;
if( p->y > maxy ) maxy = p->y;
}
if( miny < 0 ) miny = 0;
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
for( y = miny; y <= maxy; y++ )
{
short startfillx = maxx;
short endfillx = minx;
//Figure out what line segments intersect this line.
for( i = 0; i < verts; i++ )
{
short pl = i + 1;
if( pl == verts ) pl = 0;
RDPoint ptop;
RDPoint pbot;
ptop.x = points[i].x;
ptop.y = points[i].y;
pbot.x = points[pl].x;
pbot.y = points[pl].y;
//printf( "Poly: %d %d\n", pbot.y, ptop.y );
if( pbot.y < ptop.y )
{
RDPoint ptmp;
ptmp.x = pbot.x;
ptmp.y = pbot.y;
pbot.x = ptop.x;
pbot.y = ptop.y;
ptop.x = ptmp.x;
ptop.y = ptmp.y;
}
//Make sure this line segment is within our range.
//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y );
if( ptop.y <= y && pbot.y >= y )
{
short diffy = pbot.y - ptop.y;
uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math.
short diffx = pbot.x - ptop.x;
short isectx;
if( diffy == 0 )
{
if( pbot.x < ptop.x )
{
if( startfillx > pbot.x ) startfillx = pbot.x;
if( endfillx < ptop.x ) endfillx = ptop.x;
}
else
{
if( startfillx > ptop.x ) startfillx = ptop.x;
if( endfillx < pbot.x ) endfillx = pbot.x;
}
}
else
{
//Inner part is scaled by 65536, outer part must be scaled back.
isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x;
if( isectx < startfillx ) startfillx = isectx;
if( isectx > endfillx ) endfillx = isectx;
}
//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx );
}
}
//printf( "%d %d %d\n", y, startfillx, endfillx );
if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1;
if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1;
if( startfillx < 0 ) startfillx = 0;
if( startfillx < 0 ) startfillx = 0;
unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx];
for( x = startfillx; x <= endfillx; x++ )
{
(*bufferstart++) = CNFGLastColor;
}
}
//exit(1);
}
void CNFGClearFrame()
{
int i, m;
uint32_t col = 0;
short x, y;
CNFGGetDimensions( &x, &y );
if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer )
{
CNFGBufferx = x;
CNFGBuffery = y;
CNFGBuffer = malloc( x * y * 8 );
}
m = x * y;
col = CNFGColor( CNFGBGColor );
for( i = 0; i < m; i++ )
{
CNFGBuffer[i] = col;
}
}
void CNFGTackPixel( short x, short y )
{
if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor;
}
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
{
int ox = x;
int stride = w;
if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
if( x < 0 ) { w += x; x = 0; }
if( y < 0 ) { h += y; y = 0; }
//Switch w,h to x2, y2
h += y;
w += x;
if( w >= CNFGBufferx ) { w = CNFGBufferx; }
if( h >= CNFGBuffery ) { h = CNFGBuffery; }
for( ; y < h-1; y++ )
{
x = ox;
uint32_t * indat = data;
uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x;
for( ; x < w-1; x++ )
{
uint32_t newm = *(indat++);
uint32_t oldm = *(outdat);
if( (newm & 0xff) == 0xff )
{
*(outdat++) = newm;
}
else
{
//Alpha blend.
int alfa = newm&0xff;
int onemalfa = 255-alfa;
#ifdef __wasm__
uint32_t newv = 255<<0; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
uint32_t newv = 255UL<<24; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
#elif defined( ANDROID ) || defined( __android__ )
uint32_t newv = 255<<16; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#elif defined( CNFGOGL ) //OGL, on X11
uint32_t newv = 255<<16; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
#else //X11
uint32_t newv = 255UL<<24; //Alpha, then RGB
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
#endif
*(outdat++) = newv;
}
}
data += stride;
}
}
void CNFGSwapBuffers()
{
CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery );
}
#endif
#undef AGLF
#define AGLF(x) static inline BACKEND_##x
#endif
uint32_t AGLF(CNFGColor)( uint32_t RGB )
{
CNFGLastColor = RGB;
unsigned char red = ( RGB >> 24 ) & 0xFF;
unsigned char grn = ( RGB >> 16 ) & 0xFF;
unsigned char blu = ( RGB >> 8 ) & 0xFF;
unsigned long color = (red<<16)|(grn<<8)|(blu);
XSetForeground(CNFGDisplay, CNFGGC, color);
return color;
}
void AGLF(CNFGClearFrame)()
{
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) );
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height );
}
void AGLF(CNFGSwapBuffers)()
{
#ifdef CNFG_HAS_XSHAPE
if( taint_shape )
{
XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet);
taint_shape = 0;
}
#endif
XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0);
XFlush(CNFGDisplay);
#ifdef FULL_SCREEN_STEAL_FOCUS
if( FullScreen )
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
#endif
}
void AGLF(CNFGTackSegment)( short x1, short y1, short x2, short y2 )
{
if( x1 == x2 && y1 == y2 )
{
//On some targets, zero-length lines will not show up.
//This is tricky - since this will also cause more draw calls for points on systems like GLAMOR.
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 );
}
else
{
//XXX HACK! See discussion here: https://github.com/cntools/cnping/issues/68
XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 );
XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2, x1, y1 );
}
}
void AGLF(CNFGTackPixel)( short x1, short y1 )
{
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 );
}
void AGLF(CNFGTackRectangle)( short x1, short y1, short x2, short y2 )
{
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 );
}
void AGLF(CNFGTackPoly)( RDPoint * points, int verts )
{
XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, verts, Convex, CoordModeOrigin );
}
void AGLF(CNFGInternalResize)( short x, short y ) { }
void AGLF(CNFGSetLineWidth)( short width )
{
XSetLineAttributes(CNFGDisplay, CNFGGC, width, LineSolid, CapRound, JoinRound);
}
#endif // _CNFGXDRIVER_C
#endif
/*
Copyright (c) 2010-2021 <>< Charles Lohr, and several others!
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 X CONSORTIUM 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.
*/
#ifndef _CNFG_C
#define _CNFG_C
#ifndef CNFGHTTPSERVERONLY
#ifdef _CNFG_FANCYFONT
#include "TextTool/FontData.h"
#endif
//TODO: Refactor to remove malloc reliance.
#ifndef __clang__
#include <malloc.h>
#endif
int CNFGPenX, CNFGPenY;
uint32_t CNFGBGColor;
uint32_t CNFGLastColor;
//uint32_t CNFGDialogColor; //background for boxes [DEPRECATED]
// The following two arrays are generated by Fonter/fonter.cpp
const unsigned short RawdrawFontCharMap[256] = {
65535, 0, 8, 16, 24, 31, 41, 50, 51, 65535, 65535, 57, 66, 65535, 75, 83,
92, 96, 100, 108, 114, 123, 132, 137, 147, 152, 158, 163, 169, 172, 178, 182,
65535, 186, 189, 193, 201, 209, 217, 226, 228, 232, 236, 244, 248, 250, 252, 253,
255, 261, 266, 272, 278, 283, 289, 295, 300, 309, 316, 318, 321, 324, 328, 331,
337, 345, 352, 362, 368, 375, 382, 388, 396, 402, 408, 413, 422, 425, 430, 435,
442, 449, 458, 466, 472, 476, 480, 485, 492, 500, 507, 512, 516, 518, 522, 525,
527, 529, 536, 541, 546, 551, 557, 564, 572, 578, 581, 586, 593, 595, 604, 610,
615, 621, 627, 632, 638, 642, 648, 653, 660, 664, 670, 674, 680, 684, 690, 694,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
700, 703, 711, 718, 731, 740, 744, 754, 756, 760, 766, 772, 775, 777, 785, 787,
792, 798, 803, 811, 813, 820, 827, 828, 831, 833, 838, 844, 853, 862, 874, 880,
889, 898, 908, 919, 928, 939, 951, 960, 969, 978, 988, 997, 1005, 1013, 1022, 1030,
1039, 1047, 1054, 1061, 1070, 1079, 1086, 1090, 1099, 1105, 1111, 1118, 1124, 1133, 1140, 1150,
1159, 1168, 1178, 1189, 1198, 1209, 1222, 1231, 1239, 1247, 1256, 1264, 1268, 1272, 1277, 1281,
1290, 1300, 1307, 1314, 1322, 1331, 1338, 1342, 1349, 1357, 1365, 1374, 1382, 1390, 1397, 65535, };
const unsigned char RawdrawFontCharData[1405] = {
0x00, 0x09, 0x20, 0x29, 0x03, 0x23, 0x14, 0x8b, 0x00, 0x09, 0x20, 0x29, 0x04, 0x24, 0x13, 0x8c,
0x01, 0x21, 0x23, 0x14, 0x03, 0x09, 0x11, 0x9a, 0x11, 0x22, 0x23, 0x14, 0x03, 0x02, 0x99, 0x01,
0x21, 0x23, 0x09, 0x03, 0x29, 0x03, 0x09, 0x12, 0x9c, 0x03, 0x2b, 0x13, 0x1c, 0x23, 0x22, 0x11,
0x02, 0x8b, 0x9a, 0x1a, 0x01, 0x21, 0x23, 0x03, 0x89, 0x03, 0x21, 0x2a, 0x21, 0x19, 0x03, 0x14,
0x23, 0x9a, 0x01, 0x10, 0x21, 0x12, 0x09, 0x12, 0x1c, 0x03, 0xab, 0x02, 0x03, 0x1b, 0x02, 0x1a,
0x13, 0x10, 0xa9, 0x01, 0x2b, 0x03, 0x29, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x22, 0x04, 0x88,
0x20, 0x02, 0x24, 0xa8, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14, 0xab, 0x00, 0x0b, 0x0c, 0x20,
0x2b, 0xac, 0x00, 0x28, 0x00, 0x02, 0x2a, 0x10, 0x1c, 0x20, 0xac, 0x01, 0x21, 0x23, 0x03, 0x09,
0x20, 0x10, 0x14, 0x8c, 0x03, 0x23, 0x24, 0x04, 0x8b, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14,
0x2b, 0x04, 0xac, 0x01, 0x18, 0x21, 0x10, 0x9c, 0x03, 0x1c, 0x23, 0x1c, 0x10, 0x9c, 0x02, 0x22,
0x19, 0x22, 0x9b, 0x02, 0x2a, 0x02, 0x19, 0x02, 0x9b, 0x01, 0x02, 0xaa, 0x02, 0x22, 0x11, 0x02,
0x13, 0xaa, 0x11, 0x22, 0x02, 0x99, 0x02, 0x13, 0x22, 0x8a, 0x10, 0x1b, 0x9c, 0x10, 0x09, 0x20,
0x99, 0x10, 0x1c, 0x20, 0x2c, 0x01, 0x29, 0x03, 0xab, 0x21, 0x10, 0x01, 0x23, 0x14, 0x0b, 0x10,
0x9c, 0x00, 0x09, 0x23, 0x2c, 0x04, 0x03, 0x21, 0xa8, 0x21, 0x10, 0x01, 0x12, 0x03, 0x14, 0x2b,
0x02, 0xac, 0x10, 0x99, 0x10, 0x01, 0x03, 0x9c, 0x10, 0x21, 0x23, 0x9c, 0x01, 0x2b, 0x11, 0x1b,
0x21, 0x0b, 0x02, 0xaa, 0x02, 0x2a, 0x11, 0x9b, 0x04, 0x9b, 0x02, 0xaa, 0x9c, 0x03, 0xa9, 0x00,
0x20, 0x24, 0x04, 0x08, 0x9a, 0x01, 0x10, 0x1c, 0x04, 0xac, 0x01, 0x10, 0x21, 0x22, 0x04, 0xac,
0x00, 0x20, 0x24, 0x0c, 0x12, 0xaa, 0x00, 0x02, 0x2a, 0x20, 0xac, 0x20, 0x00, 0x02, 0x22, 0x24,
0x8c, 0x20, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x00, 0x20, 0x21, 0x12, 0x9c, 0x00, 0x0c, 0x00, 0x20,
0x2c, 0x04, 0x2c, 0x02, 0xaa, 0x00, 0x02, 0x22, 0x20, 0x08, 0x22, 0x8c, 0x19, 0x9b, 0x19, 0x13,
0x8c, 0x20, 0x02, 0xac, 0x01, 0x29, 0x03, 0xab, 0x00, 0x22, 0x8c, 0x01, 0x10, 0x21, 0x12, 0x1b,
0x9c, 0x21, 0x01, 0x04, 0x24, 0x22, 0x12, 0x13, 0xab, 0x04, 0x01, 0x10, 0x21, 0x2c, 0x02, 0xaa,
0x00, 0x04, 0x14, 0x23, 0x12, 0x0a, 0x12, 0x21, 0x10, 0x88, 0x23, 0x14, 0x03, 0x01, 0x10, 0xa9,
0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x88, 0x00, 0x04, 0x2c, 0x00, 0x28, 0x02, 0x9a, 0x00, 0x0c,
0x00, 0x28, 0x02, 0x9a, 0x21, 0x10, 0x01, 0x03, 0x14, 0x23, 0x22, 0x9a, 0x00, 0x0c, 0x20, 0x2c,
0x02, 0xaa, 0x00, 0x28, 0x10, 0x1c, 0x04, 0xac, 0x00, 0x20, 0x23, 0x14, 0x8b, 0x00, 0x0c, 0x02,
0x12, 0x21, 0x28, 0x12, 0x23, 0xac, 0x00, 0x04, 0xac, 0x04, 0x00, 0x11, 0x20, 0xac, 0x04, 0x00,
0x2a, 0x20, 0xac, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x89, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12,
0x8a, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x04, 0x9b, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12,
0x02, 0xac, 0x21, 0x10, 0x01, 0x23, 0x14, 0x8b, 0x00, 0x28, 0x10, 0x9c, 0x00, 0x04, 0x24, 0xa8,
0x00, 0x03, 0x14, 0x23, 0xa8, 0x00, 0x04, 0x2c, 0x14, 0x1b, 0x24, 0xa8, 0x00, 0x01, 0x23, 0x2c,
0x04, 0x03, 0x21, 0xa8, 0x00, 0x01, 0x12, 0x1c, 0x12, 0x21, 0xa8, 0x00, 0x20, 0x02, 0x04, 0xac,
0x10, 0x00, 0x04, 0x9c, 0x01, 0xab, 0x10, 0x20, 0x24, 0x9c, 0x01, 0x10, 0xa9, 0x04, 0xac, 0x00,
0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x8a, 0x00, 0x04, 0x24, 0x22, 0x8a, 0x24, 0x04, 0x03,
0x12, 0xaa, 0x20, 0x24, 0x04, 0x02, 0xaa, 0x24, 0x04, 0x02, 0x22, 0x23, 0x9b, 0x04, 0x09, 0x02,
0x1a, 0x01, 0x10, 0xa9, 0x23, 0x12, 0x03, 0x14, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x12,
0x23, 0xac, 0x19, 0x12, 0x9c, 0x2a, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x13, 0x2a, 0x13,
0xac, 0x10, 0x9c, 0x02, 0x0c, 0x02, 0x1b, 0x12, 0x1c, 0x12, 0x23, 0xac, 0x02, 0x0c, 0x03, 0x12,
0x23, 0xac, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0d, 0x04, 0x24, 0x22, 0x8a, 0x02, 0x04, 0x2c,
0x25, 0x22, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0xaa, 0x22, 0x02, 0x03, 0x23, 0x24, 0x8c, 0x11, 0x1c,
0x02, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0xaa, 0x02, 0x03, 0x14, 0x23, 0xaa, 0x02, 0x03, 0x14,
0x1a, 0x13, 0x24, 0xaa, 0x02, 0x2c, 0x04, 0xaa, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x8d, 0x02, 0x22,
0x04, 0xac, 0x20, 0x10, 0x14, 0x2c, 0x12, 0x8a, 0x10, 0x19, 0x13, 0x9c, 0x00, 0x10, 0x14, 0x0c,
0x12, 0xaa, 0x01, 0x10, 0x11, 0xa8, 0x03, 0x04, 0x24, 0x23, 0x12, 0x8b, 0x18, 0x11, 0x9c, 0x21,
0x10, 0x01, 0x02, 0x13, 0x2a, 0x10, 0x9b, 0x11, 0x00, 0x04, 0x24, 0x2b, 0x02, 0x9a, 0x01, 0x0a,
0x11, 0x29, 0x22, 0x2b, 0x03, 0x1b, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x11, 0x28, 0x11, 0x1c,
0x02, 0x2a, 0x03, 0xab, 0x10, 0x1a, 0x13, 0x9d, 0x20, 0x00, 0x02, 0x11, 0x2a, 0x02, 0x13, 0x22,
0x24, 0x8c, 0x08, 0xa8, 0x20, 0x10, 0x11, 0xa9, 0x10, 0x29, 0x20, 0x21, 0x11, 0x98, 0x11, 0x02,
0x1b, 0x21, 0x12, 0xab, 0x01, 0x21, 0xaa, 0x12, 0xaa, 0x10, 0x20, 0x21, 0x19, 0x12, 0x18, 0x11,
0xaa, 0x00, 0xa8, 0x01, 0x10, 0x21, 0x12, 0x89, 0x02, 0x2a, 0x11, 0x1b, 0x03, 0xab, 0x01, 0x10,
0x21, 0x03, 0xab, 0x01, 0x10, 0x21, 0x12, 0x0a, 0x12, 0x23, 0x8b, 0x11, 0xa8, 0x02, 0x0d, 0x04,
0x14, 0x2b, 0x22, 0xac, 0x14, 0x10, 0x01, 0x1a, 0x10, 0x20, 0xac, 0x9a, 0x14, 0x15, 0x8d, 0x20,
0xa9, 0x10, 0x20, 0x21, 0x11, 0x98, 0x01, 0x12, 0x0b, 0x11, 0x22, 0x9b, 0x00, 0x09, 0x02, 0x28,
0x12, 0x13, 0x2b, 0x22, 0xac, 0x00, 0x09, 0x02, 0x28, 0x12, 0x22, 0x13, 0x14, 0xac, 0x00, 0x10,
0x11, 0x09, 0x11, 0x02, 0x28, 0x12, 0x13, 0x2b, 0x22, 0xac, 0x18, 0x11, 0x12, 0x03, 0x14, 0xab,
0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b,
0x01, 0x98, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x01, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22,
0x2c, 0x03, 0x2b, 0x01, 0x10, 0x11, 0xa8, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x08, 0xa8,
0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x00, 0x20, 0x11, 0x88, 0x00, 0x0c, 0x02, 0x2a, 0x00,
0x19, 0x10, 0x1c, 0x10, 0x28, 0x14, 0xac, 0x23, 0x14, 0x03, 0x01, 0x10, 0x29, 0x14, 0x15, 0x8d,
0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x00, 0x99, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b,
0x11, 0xa8, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x01, 0x10, 0xa9, 0x02, 0x2a, 0x02, 0x04,
0x2c, 0x03, 0x1b, 0x08, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x00, 0x99, 0x02, 0x2a, 0x12,
0x1c, 0x04, 0x2c, 0x11, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x01, 0x10, 0xa9, 0x02, 0x2a,
0x12, 0x1c, 0x04, 0x2c, 0x28, 0x88, 0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x08, 0x02, 0x9a, 0x04,
0x02, 0x24, 0x2a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x00, 0x99, 0x02, 0x22,
0x24, 0x04, 0x0a, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x11, 0x28, 0x00, 0x99, 0x02, 0x22,
0x24, 0x04, 0x0a, 0x01, 0x10, 0x11, 0xa8, 0x01, 0x21, 0x24, 0x04, 0x09, 0x08, 0xa8, 0x01, 0x2b,
0x03, 0xa9, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x03, 0xa9, 0x01, 0x04, 0x24, 0x29, 0x11,
0xa8, 0x01, 0x04, 0x24, 0x29, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x01, 0x04,
0x24, 0x29, 0x08, 0xa8, 0x01, 0x02, 0x13, 0x1c, 0x13, 0x22, 0x29, 0x11, 0xa8, 0x00, 0x0c, 0x01,
0x11, 0x22, 0x13, 0x8b, 0x00, 0x0d, 0x00, 0x10, 0x21, 0x1a, 0x02, 0x22, 0x24, 0x8c, 0x02, 0x04,
0x24, 0x2a, 0x23, 0x12, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x11, 0xa8,
0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12,
0x0a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x09, 0xa9, 0x02, 0x04,
0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0x21, 0x89, 0x02, 0x1b, 0x02, 0x04, 0x2c, 0x12, 0x1c,
0x12, 0x2a, 0x13, 0x2b, 0x22, 0xab, 0x03, 0x04, 0x2c, 0x03, 0x12, 0x2a, 0x14, 0x15, 0x8d, 0x24,
0x04, 0x02, 0x22, 0x23, 0x1b, 0x00, 0x99, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x11, 0xa8, 0x24,
0x04, 0x02, 0x22, 0x23, 0x1b, 0x01, 0x10, 0xa9, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x09, 0xa9,
0x12, 0x1c, 0x00, 0x99, 0x12, 0x1c, 0x11, 0xa8, 0x12, 0x1c, 0x01, 0x10, 0xa9, 0x12, 0x1c, 0x09,
0xa9, 0x00, 0x2a, 0x11, 0x28, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0x23, 0x2c,
0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x22, 0x0a,
0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01,
0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x09, 0xa9, 0x19, 0x02, 0x2a, 0x9b, 0x02, 0x04,
0x24, 0x22, 0x0a, 0x04, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x00, 0x99, 0x02, 0x04, 0x14,
0x2b, 0x24, 0x2a, 0x11, 0xa8, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x02, 0x04,
0x14, 0x2b, 0x24, 0x2a, 0x09, 0xa9, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x11, 0xa8, 0x00, 0x0c,
0x02, 0x11, 0x22, 0x13, 0x8a, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x09, 0xa9, };
//Set this if you are only using CNFG to create an OpenGL context.
#ifndef CNFGCONTEXTONLY
uint32_t CNFGDialogColor;
void CNFGDrawBox( short x1, short y1, short x2, short y2 )
{
uint32_t lc = CNFGLastColor;
CNFGColor( CNFGDialogColor );
CNFGTackRectangle( x1, y1, x2, y2 );
CNFGColor( lc );
CNFGTackSegment( x1, y1, x2, y1 );
CNFGTackSegment( x2, y1, x2, y2 );
CNFGTackSegment( x2, y2, x1, y2 );
CNFGTackSegment( x1, y2, x1, y1 );
}
void CNFGDrawText( const char * text, short scale )
{
const unsigned char * lmap;
float iox = (float)CNFGPenX; //x offset
float ioy = (float)CNFGPenY; //y offset
int place = 0;
unsigned short index;
int bQuit = 0;
while( text[place] )
{
unsigned char c = text[place];
switch( c )
{
case 9: // tab
iox += 12 * scale;
break;
case 10: // linefeed
iox = (float)CNFGPenX;
ioy += 6 * scale;
break;
default:
index = RawdrawFontCharMap[c];
if( index == 65535 )
{
iox += 3 * scale;
break;
}
lmap = &RawdrawFontCharData[index];
short penx = 0, peny = 0;
unsigned char start_seg = 1;
do
{
unsigned char data = (*(lmap++));
short x1 = (short)(((data >> 4) & 0x07)*scale + iox);
short y1 = (short)((data & 0x07)*scale + ioy);
if( start_seg )
{
penx = x1;
peny = y1;
start_seg = 0;
if( data & 0x08 )
CNFGTackPixel( x1, y1 );
}
else
{
CNFGTackSegment( penx, peny, x1, y1 );
penx = x1;
peny = y1;
}
if( data & 0x08 ) start_seg = 1;
bQuit = data & 0x80;
} while( !bQuit );
iox += 3 * scale;
}
place++;
}
}
#ifndef FONT_CREATION_TOOL
#ifdef _CNFG_FANCYFONT
void CNFGDrawNiceText(const char* text, short scale)
{
const unsigned char* lmap;
float iox = (float)CNFGPenX; //x offset
float ioy = (float)CNFGPenY; //y offset
int place = 0;
unsigned short index;
int bQuit = 0;
int segmentEnd = 0;
while (text[place]) {
unsigned char c = text[place];
switch (c)
{
case 9: // tab
iox += 16 * scale;
break;
case 10: // linefeed
iox = (float)CNFGPenX;
ioy += 6 * scale;
break;
default:
index = CharIndex[c];
if (index == 0) {
iox += 4 * scale;
break;
}
lmap = &FontData[index];
short charWidth = ((*lmap) & 0xE0) >> 5; //0b11100000
short xbase = ((*lmap) & 0x18) >> 3; //0b00011000
short ybase = (*lmap) & 0x07; //0b00000111
lmap++;
do {
int x1 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale); //0b00111000
int y1 = (((*lmap) & 0x07) * scale + ioy + ybase * scale);
segmentEnd = *lmap & 0x40;
int x2 = 0;
int y2 = 0;
lmap++;
if (segmentEnd) {
x2 = x1;
y2 = y1;
}
else {
x2 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale);
y2 = (((*lmap) & 0x07) * scale + ioy + ybase * scale);
}
CNFGTackSegment(x1, y1, x2, y2);
bQuit = *(lmap - 1) & 0x80;
} while (!bQuit);
iox += (charWidth + 2) * scale;
//iox += 8 * scale;
}
place++;
}
}
#endif
#endif
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize )
{
int charsx = 0;
int charsy = 1;
int charsline = 0;
const char * s;
for( s = text; *s; s++ )
{
if( *s == '\n' )
{
charsline = 0;
if( *(s+1) )
charsy++;
}
else
{
charsline++;
if( charsline > charsx )
charsx = charsline;
}
}
*w = charsx * textsize * 3-1*textsize;
*h = charsy * textsize * 6;
}
#if defined( CNFG_BATCH )
//This is the path by which we convert rawdraw functionality
//into nice batched triangle streams.
//Just FYI we use floats for geometry instead of shorts becase it is harder
//to triangularize a diagonal line int triangles with shorts and have it look good.
void CNFGEmitBackendTriangles( const float * fv, const uint32_t * col, int nr_verts );
//If on WASM, sqrtf is implied. On other platforms, need sqrtf from math.h
#ifdef __wasm__
float sqrtf( float f );
#define cnfg_sqrtf sqrtf
#elif defined( __TINYC__ ) && defined( WIN32 )
#define cnfg_sqrtf sqrt
#else
#define cnfg_sqrtf sqrtf
#include <math.h>
#endif
//Geometry batching system - so we can batch geometry and deliver it all at once.
float CNFGVertDataV[CNFG_BATCH*3];
uint32_t CNFGVertDataC[CNFG_BATCH];
int CNFGVertPlace;
static float wgl_last_width_over_2 = .5f;
static void EmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 )
{
//Because quads are really useful, but it's best to keep them all triangles if possible.
//This lets us draw arbitrary quads.
if( CNFGVertPlace >= CNFG_BATCH-6 ) CNFGFlushRender();
float * fv = &CNFGVertDataV[CNFGVertPlace*3];
fv[0] = cx0; fv[1] = cy0;
fv[3] = cx1; fv[4] = cy1;
fv[6] = cx2; fv[7] = cy2;
fv[9] = cx2; fv[10] = cy2;
fv[12] = cx1; fv[13] = cy1;
fv[15] = cx3; fv[16] = cy3;
uint32_t * col = &CNFGVertDataC[CNFGVertPlace];
uint32_t color = CNFGLastColor;
col[0] = color; col[1] = color; col[2] = color; col[3] = color; col[4] = color; col[5] = color;
CNFGVertPlace += 6;
}
#if !defined( CNFGRASTERIZER ) && !defined( CNFGHTTP )
void CNFGTackPixel( short x1, short y1 )
{
x1++; y1++;
const float l2 = wgl_last_width_over_2;
const float l2u = wgl_last_width_over_2+0.5f;
EmitQuad( x1-l2u, y1-l2u, x1+l2, y1-l2u, x1-l2u, y1+l2, x1+l2, y1+l2 );
}
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
{
float ix1 = x1;
float iy1 = y1;
float ix2 = x2;
float iy2 = y2;
float dx = ix2-ix1;
float dy = iy2-iy1;
float imag = 1.f/(float)(cnfg_sqrtf(dx*dx+dy*dy));
dx *= imag;
dy *= imag;
float orthox = dy*wgl_last_width_over_2;
float orthoy =-dx*wgl_last_width_over_2;
ix2 += dx/2 + 0.5f;
iy2 += dy/2 + 0.5f;
ix1 -= dx/2 - 0.5f;
iy1 -= dy/2 - 0.5f;
//This logic is incorrect. XXX FIXME.
EmitQuad( (ix1 - orthox), (iy1 - orthoy), (ix1 + orthox), (iy1 + orthoy), (ix2 - orthox), (iy2 - orthoy), ( ix2 + orthox), ( iy2 + orthoy) );
}
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
{
float ix1 = x1;
float iy1 = y1;
float ix2 = x2;
float iy2 = y2;
EmitQuad( ix1,iy1,ix2,iy1,ix1,iy2,ix2,iy2 );
}
void CNFGTackPoly( RDPoint * points, int verts )
{
int i;
int tris = verts-2;
if( CNFGVertPlace >= CNFG_BATCH-tris*3 ) CNFGFlushRender();
uint32_t color = CNFGLastColor;
short * ptrsrc = (short*)points;
for( i = 0; i < tris; i++ )
{
float * fv = &CNFGVertDataV[CNFGVertPlace*3];
fv[0] = ptrsrc[0];
fv[1] = ptrsrc[1];
fv[3] = ptrsrc[i*2+2];
fv[4] = ptrsrc[i*2+3];
fv[6] = ptrsrc[i*2+4];
fv[7] = ptrsrc[i*2+5];
uint32_t * col = &CNFGVertDataC[CNFGVertPlace];
col[0] = color;
col[1] = color;
col[2] = color;
CNFGVertPlace += 3;
}
}
uint32_t CNFGColor( uint32_t RGB )
{
return CNFGLastColor = RGB;
}
void CNFGSetLineWidth( short width )
{
wgl_last_width_over_2 = width/2.0f;// + 0.5;
}
#endif
#if !defined( __wasm__ ) && !defined( CNFGHTTP )
//In WASM, Javascript takes over this functionality.
//Shader compilation errors go to stderr.
#include <stdio.h>
#ifndef GL_VERTEX_SHADER
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_LINK_STATUS 0x8B82
#define GL_TEXTURE_2D 0x0DE1
#define GL_CLAMP_TO_EDGE 0x812F
#define LGLchar char
#else
#define LGLchar GLchar
#endif
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
#define CNFGOGL_NEED_EXTENSION
#include <GL/gl.h>
#endif
#ifdef CNFGOGL_NEED_EXTENSION
// If we are going to be defining our own function pointer call
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
// Make sure to use __stdcall on Windows
#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \
typedef ret (__stdcall *CNFGTYPE##name)( __VA_ARGS__ ); \
ret (__stdcall *CNFG##name)( __VA_ARGS__ );
#else
#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \
typedef ret (*CNFGTYPE##name)( __VA_ARGS__ ); \
ret (*CNFG##name)( __VA_ARGS__ );
#endif
#else
//If we are going to be defining the real call
#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \
ret name (__VA_ARGS__);
#endif
#ifdef __cplusplus
extern "C" {
#endif
int (*MyFunc)( int program, const LGLchar *name );
CHEWTYPEDEF( GLint, glGetUniformLocation, return, (program,name), GLuint program, const LGLchar *name )
CHEWTYPEDEF( void, glEnableVertexAttribArray, , (index), GLuint index )
CHEWTYPEDEF( void, glUseProgram, , (program), GLuint program )
CHEWTYPEDEF( void, glGetProgramInfoLog, , (program,maxLength, length, infoLog), GLuint program, GLsizei maxLength, GLsizei *length, LGLchar *infoLog )
CHEWTYPEDEF( void, glGetProgramiv, , (program,pname,params), GLuint program, GLenum pname, GLint *params )
CHEWTYPEDEF( void, glBindAttribLocation, , (program,index,name), GLuint program, GLuint index, const LGLchar *name )
CHEWTYPEDEF( void, glGetShaderiv, , (shader,pname,params), GLuint shader, GLenum pname, GLint *params )
CHEWTYPEDEF( GLuint, glCreateShader, return, (e), GLenum e )
CHEWTYPEDEF( void, glVertexAttribPointer, , (index,size,type,normalized,stride,pointer), GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer )
CHEWTYPEDEF( void, glShaderSource, , (shader,count,string,length), GLuint shader, GLsizei count, const LGLchar *const*string, const GLint *length )
CHEWTYPEDEF( void, glAttachShader, , (program,shader), GLuint program, GLuint shader )
CHEWTYPEDEF( void, glCompileShader, ,(shader), GLuint shader )
CHEWTYPEDEF( void, glGetShaderInfoLog , , (shader,maxLength, length, infoLog), GLuint shader, GLsizei maxLength, GLsizei *length, LGLchar *infoLog )
CHEWTYPEDEF( GLuint, glCreateProgram, return, () , void )
CHEWTYPEDEF( void, glLinkProgram, , (program), GLuint program )
CHEWTYPEDEF( void, glDeleteShader, , (shader), GLuint shader )
CHEWTYPEDEF( void, glUniform4f, , (location,v0,v1,v2,v3), GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 )
CHEWTYPEDEF( void, glUniform1i, , (location,i0), GLint location, GLint i0 )
CHEWTYPEDEF( void, glActiveTexture, , (texture), GLenum texture )
#ifndef CNFGOGL_NEED_EXTENSION
#define CNFGglGetUniformLocation glGetUniformLocation
#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray
#define CNFGglUseProgram glUseProgram
#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray
#define CNFGglUseProgram glUseProgram
#define CNFGglGetProgramInfoLog glGetProgramInfoLog
#define CNFGglGetProgramiv glGetProgramiv
#define CNFGglShaderSource glShaderSource
#define CNFGglCreateShader glCreateShader
#define CNFGglAttachShader glAttachShader
#define CNFGglGetShaderiv glGetShaderiv
#define CNFGglCompileShader glCompileShader
#define CNFGglGetShaderInfoLog glGetShaderInfoLog
#define CNFGglCreateProgram glCreateProgram
#define CNFGglLinkProgram glLinkProgram
#define CNFGglDeleteShader glDeleteShader
#define CNFGglUniform4f glUniform4f
#define CNFGglBindAttribLocation glBindAttribLocation
#define CNFGglVertexAttribPointer glVertexAttribPointer
#define CNFGglUniform1i glUniform1i
#define CNFGglActiveTexture glActiveTexture
#endif
#ifdef __cplusplus
};
#endif
#ifdef CNFGOGL_NEED_EXTENSION
#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 )
//From https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions
void * CNFGGetProcAddress(const char *name)
{
void *p = (void *)wglGetProcAddress(name);
if(p == 0 ||
(p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
(p == (void*)-1) )
{
static HMODULE module;
if( !module ) module = LoadLibraryA("opengl32.dll");
p = (void *)GetProcAddress(module, name);
}
// We were unable to load the required openGL function
if (!p) {
fprintf(stderr,"[rawdraw][warn]: Unable to load openGL extension \"%s\"\n", name);
}
return p;
}
#else
#include <dlfcn.h>
void * CNFGGetProcAddress(const char *name)
{
//Tricky use RTLD_NEXT first so we don't accidentally link against ourselves.
void * v1 = dlsym( (void*)((intptr_t)-1) /*RTLD_NEXT = -1*/ /*RTLD_DEFAULT = 0*/, name );
//printf( "%s = %p\n", name, v1 );
if( !v1 ) v1 = dlsym( 0, name );
return v1;
}
#endif
// Try and load openGL extension functions required for rawdraw
static void CNFGLoadExtensionsInternal()
{
CNFGglGetUniformLocation = (CNFGTYPEglGetUniformLocation) CNFGGetProcAddress( "glGetUniformLocation" );
CNFGglEnableVertexAttribArray = (CNFGTYPEglEnableVertexAttribArray)CNFGGetProcAddress( "glEnableVertexAttribArray" );
CNFGglUseProgram = (CNFGTYPEglUseProgram)CNFGGetProcAddress( "glUseProgram" );
CNFGglGetProgramInfoLog = (CNFGTYPEglGetProgramInfoLog)CNFGGetProcAddress( "glGetProgramInfoLog" );
CNFGglBindAttribLocation = (CNFGTYPEglBindAttribLocation)CNFGGetProcAddress( "glBindAttribLocation" );
CNFGglGetProgramiv = (CNFGTYPEglGetProgramiv)CNFGGetProcAddress( "glGetProgramiv" );
CNFGglGetShaderiv = (CNFGTYPEglGetShaderiv)CNFGGetProcAddress( "glGetShaderiv" );
CNFGglVertexAttribPointer = (CNFGTYPEglVertexAttribPointer)CNFGGetProcAddress( "glVertexAttribPointer" );
CNFGglCreateShader = (CNFGTYPEglCreateShader)CNFGGetProcAddress( "glCreateShader" );
CNFGglShaderSource = (CNFGTYPEglShaderSource)CNFGGetProcAddress( "glShaderSource" );
CNFGglAttachShader = (CNFGTYPEglAttachShader)CNFGGetProcAddress( "glAttachShader" );
CNFGglCompileShader = (CNFGTYPEglCompileShader)CNFGGetProcAddress( "glCompileShader" );
CNFGglGetShaderInfoLog = (CNFGTYPEglGetShaderInfoLog)CNFGGetProcAddress( "glGetShaderInfoLog" );
CNFGglDeleteShader = (CNFGTYPEglDeleteShader)CNFGGetProcAddress( "glDeleteShader" );
CNFGglLinkProgram = (CNFGTYPEglLinkProgram)CNFGGetProcAddress( "glLinkProgram" );
CNFGglCreateProgram = (CNFGTYPEglCreateProgram)CNFGGetProcAddress( "glCreateProgram" );
CNFGglUniform4f = (CNFGTYPEglUniform4f)CNFGGetProcAddress( "glUniform4f" );
CNFGglUniform1i = (CNFGTYPEglUniform1i)CNFGGetProcAddress( "glUniform1i" );
CNFGglActiveTexture = (CNFGTYPEglActiveTexture)CNFGGetProcAddress("glActiveTexture");
// Check if any of these functions didn't get loaded
uint8_t not_all_functions_loaded =
!CNFGglGetUniformLocation || !CNFGglEnableVertexAttribArray || !CNFGglUseProgram ||
!CNFGglGetProgramInfoLog || !CNFGglBindAttribLocation || !CNFGglGetProgramiv ||
!CNFGglVertexAttribPointer || !CNFGglCreateShader || !CNFGglShaderSource ||
!CNFGglAttachShader || !CNFGglCompileShader || !CNFGglGetShaderInfoLog ||
!CNFGglDeleteShader || !CNFGglLinkProgram || !CNFGglCreateProgram ||
!CNFGglUniform4f || !CNFGglUniform1i || !CNFGglActiveTexture;
if (not_all_functions_loaded) {
fprintf(
stderr,
"[rawdraw][err]: Unable to load all openGL extensions required for rawdraw\n"
"\tPlease update your graphics drivers or unexpected crashes may occur.\n"
);
}
// Give a very stern warning if unable to create or compile shaders
if (!CNFGglCreateShader || !CNFGglCompileShader) {
fprintf(
stderr,
"[rawdraw][err]: Unable to create or compile shaders, this will cause a fatal error if "
"openGL is used.\n"
"\tUpdate your video graphics drivers or switch to software graphics.\n"
);
}
}
#else
static void CNFGLoadExtensionsInternal() { }
#endif
GLuint gRDShaderProg = -1;
GLuint gRDBlitProg = -1;
GLuint gRDShaderProgUX = -1;
GLuint gRDBlitProgUX = -1;
GLuint gRDBlitProgUT = -1;
GLuint gRDBlitProgTex = -1;
GLuint gRDLastResizeW;
GLuint gRDLastResizeH;
GLuint CNFGGLInternalLoadShader( const char * vertex_shader, const char * fragment_shader )
{
GLuint fragment_shader_object = 0;
GLuint vertex_shader_object = 0;
GLuint program = 0;
int ret;
vertex_shader_object = CNFGglCreateShader(GL_VERTEX_SHADER);
if (!vertex_shader_object) {
fprintf( stderr, "Error: glCreateShader(GL_VERTEX_SHADER) "
"failed: 0x%08X\n", glGetError());
goto fail;
}
CNFGglShaderSource(vertex_shader_object, 1, &vertex_shader, NULL);
CNFGglCompileShader(vertex_shader_object);
CNFGglGetShaderiv(vertex_shader_object, GL_COMPILE_STATUS, &ret);
if (!ret) {
fprintf( stderr,"Error: vertex shader compilation failed!\n");
CNFGglGetShaderiv(vertex_shader_object, GL_INFO_LOG_LENGTH, &ret);
if (ret > 1) {
//TODO: Refactor to remove malloc reliance.
#ifndef __clang__
char * log = (char*)alloca(ret);
CNFGglGetShaderInfoLog(vertex_shader_object, ret, NULL, log);
fprintf( stderr, "%s", log);
#endif
}
goto fail;
}
fragment_shader_object = CNFGglCreateShader(GL_FRAGMENT_SHADER);
if (!fragment_shader_object) {
fprintf( stderr, "Error: glCreateShader(GL_FRAGMENT_SHADER) "
"failed: 0x%08X\n", glGetError());
goto fail;
}
CNFGglShaderSource(fragment_shader_object, 1, &fragment_shader, NULL);
CNFGglCompileShader(fragment_shader_object);
CNFGglGetShaderiv(fragment_shader_object, GL_COMPILE_STATUS, &ret);
if (!ret) {
fprintf( stderr, "Error: fragment shader compilation failed!\n");
CNFGglGetShaderiv(fragment_shader_object, GL_INFO_LOG_LENGTH, &ret);
if (ret > 1) {
//TODO: Refactor to remove malloc reliance.
#ifndef __clang__
char * log = (char*)malloc(ret);
CNFGglGetShaderInfoLog(fragment_shader_object, ret, NULL, log);
fprintf( stderr, "%s", log);
free( log );
#endif
}
goto fail;
}
program = CNFGglCreateProgram();
if (!program) {
fprintf( stderr, "Error: failed to create program!\n");
goto fail;
}
CNFGglAttachShader(program, vertex_shader_object);
CNFGglAttachShader(program, fragment_shader_object);
CNFGglBindAttribLocation(program, 0, "a0");
CNFGglBindAttribLocation(program, 1, "a1");
CNFGglLinkProgram(program);
CNFGglGetProgramiv(program, GL_LINK_STATUS, &ret);
if (!ret) {
fprintf( stderr, "Error: program linking failed!\n");
CNFGglGetProgramiv(program, GL_INFO_LOG_LENGTH, &ret);
if (ret > 1) {
//TODO: Refactor to remove malloc reliance.
#ifndef __clang__
char *log = (char*)alloca(ret);
CNFGglGetProgramInfoLog(program, ret, NULL, log);
fprintf( stderr, "%s", log);
#endif
}
goto fail;
}
return program;
fail:
if( !vertex_shader_object ) CNFGglDeleteShader( vertex_shader_object );
if( !fragment_shader_object ) CNFGglDeleteShader( fragment_shader_object );
if( !program ) CNFGglDeleteShader( program );
return -1;
}
#if defined( CNFGEWGL ) && !defined( CNFG_NO_PRECISION )
#define PRECISIONA "lowp"
#define PRECISIONB "mediump"
#else
#define PRECISIONA
#define PRECISIONB
#endif
void CNFGSetupBatchInternal()
{
short w, h;
CNFGLoadExtensionsInternal();
CNFGGetDimensions( &w, &h );
gRDShaderProg = CNFGGLInternalLoadShader(
"uniform vec4 xfrm;"
"attribute vec3 a0;"
"attribute vec4 a1;"
"varying " PRECISIONA " vec4 vc;"
"void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); vc = a1; }",
"varying " PRECISIONA " vec4 vc;"
"void main() { gl_FragColor = vec4(vc.abgr); }"
);
CNFGglUseProgram( gRDShaderProg );
gRDShaderProgUX = CNFGglGetUniformLocation ( gRDShaderProg , "xfrm" );
gRDBlitProg = CNFGGLInternalLoadShader(
"uniform vec4 xfrm;"
"attribute vec3 a0;"
"attribute vec4 a1;"
"varying " PRECISIONB " vec2 tc;"
"void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); tc = a1.xy; }",
"varying " PRECISIONB " vec2 tc;"
"uniform sampler2D tex;"
"void main() { gl_FragColor = texture2D(tex,tc)."
#if !defined( CNFGRASTERIZER )
"wzyx"
#else
"wxyz"
#endif
";}" );
CNFGglUseProgram( gRDBlitProg );
gRDBlitProgUX = CNFGglGetUniformLocation ( gRDBlitProg , "xfrm" );
gRDBlitProgUT = CNFGglGetUniformLocation ( gRDBlitProg , "tex" );
glGenTextures( 1, &gRDBlitProgTex );
CNFGglEnableVertexAttribArray(0);
CNFGglEnableVertexAttribArray(1);
glDisable(GL_DEPTH_TEST);
glDepthMask( GL_FALSE );
glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
CNFGVertPlace = 0;
}
#ifndef CNFGRASTERIZER
void CNFGInternalResize(short x, short y)
#else
void CNFGInternalResizeOGLBACKEND(short x, short y)
#endif
{
glViewport( 0, 0, x, y );
gRDLastResizeW = x;
gRDLastResizeH = y;
if (gRDShaderProg == 0xFFFFFFFF) { return; } // Prevent trying to set uniform if the shader isn't ready yet.
CNFGglUseProgram( gRDShaderProg );
CNFGglUniform4f( gRDShaderProgUX, 1.f/x, -1.f/y, -0.5f, 0.5f);
}
void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices )
{
CNFGglUseProgram( gRDShaderProg );
CNFGglUniform4f( gRDShaderProgUX, 1.f/gRDLastResizeW, -1.f/gRDLastResizeH, -0.5f, 0.5f);
CNFGglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
CNFGglVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
glDrawArrays( GL_TRIANGLES, 0, num_vertices);
}
#ifdef CNFGOGL
// this is here, so people don't have to include opengl
void CNFGDeleteTex( unsigned int tex )
{
glDeleteTextures(1, &tex);
}
unsigned int CNFGTexImage( uint32_t *data, int w, int h )
{
GLuint tex;
glGenTextures(1, &tex);
glEnable( GL_TEXTURE_2D );
CNFGglActiveTexture( 0 );
glBindTexture( GL_TEXTURE_2D, tex );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data );
return (unsigned int)tex;
}
void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h )
{
if( w == 0 || h == 0 ) return;
CNFGFlushRender();
CNFGglUseProgram( gRDBlitProg );
CNFGglUniform4f( gRDBlitProgUX,
1.f/gRDLastResizeW, -1.f/gRDLastResizeH,
-0.5f+x/(float)gRDLastResizeW, 0.5f-y/(float)gRDLastResizeH );
CNFGglUniform1i( gRDBlitProgUT, 0 );
glBindTexture(GL_TEXTURE_2D, tex);
const float verts[] = {
0,0, (float)w,0, (float)w,(float)h,
0,0, (float)w,(float)h, 0,(float)h, };
static const uint8_t tex_verts[] = {
0,0, 255,0, 255,255,
0,0, 255,255, 0,255 };
CNFGglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
CNFGglVertexAttribPointer(1, 2, GL_UNSIGNED_BYTE, GL_TRUE, 0, tex_verts);
glDrawArrays( GL_TRIANGLES, 0, 6);
}
#endif
#ifdef CNFGRASTERIZER
void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h )
#else
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
#endif
{
glEnable( GL_TEXTURE_2D );
CNFGglActiveTexture( 0 );
glBindTexture( GL_TEXTURE_2D, gRDBlitProgTex );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data );
CNFGBlitTex( gRDBlitProgTex, x, y, w, h );
}
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
{
#ifdef CNFGRASTERIZER
CNFGBlitImageInternal( data, 0, 0, w, h );
void CNFGSwapBuffersInternal();
CNFGSwapBuffersInternal();
#else
CNFGBlitImage( data, 0, 0, w, h );
#endif
}
#ifndef CNFGRASTERIZER
void CNFGFlushRender()
{
if( !CNFGVertPlace ) return;
CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace );
CNFGVertPlace = 0;
}
void CNFGClearFrame()
{
glClearColor( ((CNFGBGColor&0xff000000)>>24)/255.0f,
((CNFGBGColor&0xff0000)>>16)/255.0f,
(CNFGBGColor&0xff00)/65280.0f,
(CNFGBGColor&0xff)/255.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}
#endif
#endif //__wasm__
#else
void CNFGFlushRender() { }
#endif
#endif
#endif // CNFGHTTPSERVERONLY
#endif //_CNFG_C
#ifdef CNFG3D
//Copyright 2012-2017 <>< Charles Lohr
//You may license this file under the MIT/x11, NewBSD, or any GPL license.
//This is a series of tools useful for software rendering.
//Use of this file with OpenGL is untested.
#ifdef CNFG3D
#ifdef __wasm__
double sin( double v );
double cos( double v );
double tan( double v );
double sqrt( double v );
float sinf( float v );
float cosf( float v );
float tanf( float v );
float sqrtf( float v );
void tdMATCOPY( float * x, const float * y )
{
int i;
for( i = 0; i < 16; i++ ) x[i] = y[i];
}
#else
#include <string.h>
#include <stdio.h>
#endif
#ifdef CNFG3D_USE_OGL_MAJOR
#define m00 0
#define m10 1
#define m20 2
#define m30 3
#define m01 4
#define m11 5
#define m21 6
#define m31 7
#define m02 8
#define m12 9
#define m22 10
#define m32 11
#define m03 12
#define m13 13
#define m23 14
#define m33 15
#else
#define m00 0
#define m01 1
#define m02 2
#define m03 3
#define m10 4
#define m11 5
#define m12 6
#define m13 7
#define m20 8
#define m21 9
#define m22 10
#define m23 11
#define m30 12
#define m31 13
#define m32 14
#define m33 15
#endif
void tdIdentity( float * f )
{
f[m00] = 1; f[m01] = 0; f[m02] = 0; f[m03] = 0;
f[m10] = 0; f[m11] = 1; f[m12] = 0; f[m13] = 0;
f[m20] = 0; f[m21] = 0; f[m22] = 1; f[m23] = 0;
f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 1;
}
void tdZero( float * f )
{
f[m00] = 0; f[m01] = 0; f[m02] = 0; f[m03] = 0;
f[m10] = 0; f[m11] = 0; f[m12] = 0; f[m13] = 0;
f[m20] = 0; f[m21] = 0; f[m22] = 0; f[m23] = 0;
f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 0;
}
void tdTranslate( float * f, float x, float y, float z )
{
float ftmp[16];
tdIdentity(ftmp);
ftmp[m03] += x;
ftmp[m13] += y;
ftmp[m23] += z;
tdMultiply( f, ftmp, f );
}
void tdScale( float * f, float x, float y, float z )
{
#if 0
f[m00] *= x;
f[m01] *= x;
f[m02] *= x;
f[m03] *= x;
f[m10] *= y;
f[m11] *= y;
f[m12] *= y;
f[m13] *= y;
f[m20] *= z;
f[m21] *= z;
f[m22] *= z;
f[m23] *= z;
#endif
float ftmp[16];
tdIdentity(ftmp);
ftmp[m00] *= x;
ftmp[m11] *= y;
ftmp[m22] *= z;
tdMultiply( f, ftmp, f );
}
void tdRotateAA( float * f, float angle, float ix, float iy, float iz )
{
float ftmp[16];
float c = tdCOS( angle*tdDEGRAD );
float s = tdSIN( angle*tdDEGRAD );
float absin = tdSQRT( ix*ix + iy*iy + iz*iz );
float x = ix/absin;
float y = iy/absin;
float z = iz/absin;
ftmp[m00] = x*x*(1-c)+c;
ftmp[m01] = x*y*(1-c)-z*s;
ftmp[m02] = x*z*(1-c)+y*s;
ftmp[m03] = 0;
ftmp[m10] = y*x*(1-c)+z*s;
ftmp[m11] = y*y*(1-c)+c;
ftmp[m12] = y*z*(1-c)-x*s;
ftmp[m13] = 0;
ftmp[m20] = x*z*(1-c)-y*s;
ftmp[m21] = y*z*(1-c)+x*s;
ftmp[m22] = z*z*(1-c)+c;
ftmp[m23] = 0;
ftmp[m30] = 0;
ftmp[m31] = 0;
ftmp[m32] = 0;
ftmp[m33] = 1;
tdMultiply( f, ftmp, f );
}
void tdRotateQuat( float * f, float qw, float qx, float qy, float qz )
{
float ftmp[16];
//float qw2 = qw*qw;
float qx2 = qx*qx;
float qy2 = qy*qy;
float qz2 = qz*qz;
ftmp[m00] = 1 - 2*qy2 - 2*qz2;
ftmp[m01] = 2*qx*qy - 2*qz*qw;
ftmp[m02] = 2*qx*qz + 2*qy*qw;
ftmp[m03] = 0;
ftmp[m10] = 2*qx*qy + 2*qz*qw;
ftmp[m11] = 1 - 2*qx2 - 2*qz2;
ftmp[m12] = 2*qy*qz - 2*qx*qw;
ftmp[m13] = 0;
ftmp[m20] = 2*qx*qz - 2*qy*qw;
ftmp[m21] = 2*qy*qz + 2*qx*qw;
ftmp[m22] = 1 - 2*qx2 - 2*qy2;
ftmp[m23] = 0;
ftmp[m30] = 0;
ftmp[m31] = 0;
ftmp[m32] = 0;
ftmp[m33] = 1;
tdMultiply( f, ftmp, f );
}
void tdRotateEA( float * f, float x, float y, float z )
{
float ftmp[16];
//x,y,z must be negated for some reason
float X = -x*2*tdQ_PI/360; //Reduced calulation for speed
float Y = -y*2*tdQ_PI/360;
float Z = -z*2*tdQ_PI/360;
float cx = tdCOS(X);
float sx = tdSIN(X);
float cy = tdCOS(Y);
float sy = tdSIN(Y);
float cz = tdCOS(Z);
float sz = tdSIN(Z);
//Row major (unless CNFG3D_USE_OGL_MAJOR is selected)
//manually transposed
ftmp[m00] = cy*cz;
ftmp[m10] = (sx*sy*cz)-(cx*sz);
ftmp[m20] = (cx*sy*cz)+(sx*sz);
ftmp[m30] = 0;
ftmp[m01] = cy*sz;
ftmp[m11] = (sx*sy*sz)+(cx*cz);
ftmp[m21] = (cx*sy*sz)-(sx*cz);
ftmp[m31] = 0;
ftmp[m02] = -sy;
ftmp[m12] = sx*cy;
ftmp[m22] = cx*cy;
ftmp[m32] = 0;
ftmp[m03] = 0;
ftmp[m13] = 0;
ftmp[m23] = 0;
ftmp[m33] = 1;
tdMultiply( f, ftmp, f );
}
void tdMultiply( float * fin1, float * fin2, float * fout )
{
float fotmp[16];
int i, k;
#ifdef CNFG3D_USE_OGL_MAJOR
fotmp[m00] = fin1[m00] * fin2[m00] + fin1[m01] * fin2[m10] + fin1[m02] * fin2[m20] + fin1[m03] * fin2[m30];
fotmp[m01] = fin1[m00] * fin2[m01] + fin1[m01] * fin2[m11] + fin1[m02] * fin2[m21] + fin1[m03] * fin2[m31];
fotmp[m02] = fin1[m00] * fin2[m02] + fin1[m01] * fin2[m12] + fin1[m02] * fin2[m22] + fin1[m03] * fin2[m32];
fotmp[m03] = fin1[m00] * fin2[m03] + fin1[m01] * fin2[m13] + fin1[m02] * fin2[m23] + fin1[m03] * fin2[m33];
fotmp[m10] = fin1[m10] * fin2[m00] + fin1[m11] * fin2[m10] + fin1[m12] * fin2[m20] + fin1[m13] * fin2[m30];
fotmp[m11] = fin1[m10] * fin2[m01] + fin1[m11] * fin2[m11] + fin1[m12] * fin2[m21] + fin1[m13] * fin2[m31];
fotmp[m12] = fin1[m10] * fin2[m02] + fin1[m11] * fin2[m12] + fin1[m12] * fin2[m22] + fin1[m13] * fin2[m32];
fotmp[m13] = fin1[m10] * fin2[m03] + fin1[m11] * fin2[m13] + fin1[m12] * fin2[m23] + fin1[m13] * fin2[m33];
fotmp[m20] = fin1[m20] * fin2[m00] + fin1[m21] * fin2[m10] + fin1[m22] * fin2[m20] + fin1[m23] * fin2[m30];
fotmp[m21] = fin1[m20] * fin2[m01] + fin1[m21] * fin2[m11] + fin1[m22] * fin2[m21] + fin1[m23] * fin2[m31];
fotmp[m22] = fin1[m20] * fin2[m02] + fin1[m21] * fin2[m12] + fin1[m22] * fin2[m22] + fin1[m23] * fin2[m32];
fotmp[m23] = fin1[m20] * fin2[m03] + fin1[m21] * fin2[m13] + fin1[m22] * fin2[m23] + fin1[m23] * fin2[m33];
fotmp[m30] = fin1[m30] * fin2[m00] + fin1[m31] * fin2[m10] + fin1[m32] * fin2[m20] + fin1[m33] * fin2[m30];
fotmp[m31] = fin1[m30] * fin2[m01] + fin1[m31] * fin2[m11] + fin1[m32] * fin2[m21] + fin1[m33] * fin2[m31];
fotmp[m32] = fin1[m30] * fin2[m02] + fin1[m31] * fin2[m12] + fin1[m32] * fin2[m22] + fin1[m33] * fin2[m32];
fotmp[m33] = fin1[m30] * fin2[m03] + fin1[m31] * fin2[m13] + fin1[m32] * fin2[m23] + fin1[m33] * fin2[m33];
#else
for( i = 0; i < 16; i++ )
{
int xp = i & 0x03;
int yp = i & 0x0c;
fotmp[i] = 0;
for( k = 0; k < 4; k++ )
{
fotmp[i] += fin1[yp+k] * fin2[(k<<2)|xp];
}
}
#endif
tdMATCOPY( fout, fotmp );
}
#ifndef __wasm__
void tdPrint( const float * f )
{
int i;
printf( "{\n" );
#ifdef CNFG3D_USE_OGL_MAJOR
for( i = 0; i < 4; i++ )
{
printf( " %f, %f, %f, %f\n", f[0+i], f[4+i], f[8+i], f[12+i] );
}
#else
for( i = 0; i < 16; i+=4 )
{
printf( " %f, %f, %f, %f\n", f[0+i], f[1+i], f[2+i], f[3+i] );
}
#endif
printf( "}\n" );
}
#endif
void tdTransposeSelf( float * f )
{
float fout[16];
fout[m00] = f[m00]; fout[m01] = f[m10]; fout[m02] = f[m20]; fout[m03] = f[m30];
fout[m10] = f[m01]; fout[m11] = f[m11]; fout[m12] = f[m21]; fout[m13] = f[m31];
fout[m20] = f[m02]; fout[m21] = f[m12]; fout[m22] = f[m22]; fout[m23] = f[m32];
fout[m30] = f[m03]; fout[m31] = f[m13]; fout[m32] = f[m23]; fout[m33] = f[m33];
tdMATCOPY( f, fout );
}
void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out )
{
float f = 1./tdTAN(fovy * tdQ_PI / 360.0);
out[m00] = f/aspect; out[m01] = 0; out[m02] = 0; out[m03] = 0;
out[m10] = 0; out[m11] = f; out[m12] = 0; out[m13] = 0;
out[m20] = 0; out[m21] = 0;
out[m22] = (zFar + zNear)/(zNear - zFar);
out[m23] = 2*zFar*zNear /(zNear - zFar);
out[m30] = 0; out[m31] = 0; out[m32] = -1; out[m33] = 0;
}
void tdLookAt( float * m, float * eye, float * at, float * up )
{
float out[16];
float F[3] = { at[0] - eye[0], at[1] - eye[1], at[2] - eye[2] };
float fdiv = 1./tdSQRT( F[0]*F[0] + F[1]*F[1] + F[2]*F[2] );
float f[3] = { F[0]*fdiv, F[1]*fdiv, F[2]*fdiv };
float udiv = 1./tdSQRT( up[0]*up[0] + up[1]*up[1] + up[2]*up[2] );
float UP[3] = { up[0]*udiv, up[1]*udiv, up[2]*udiv };
float s[3];
float u[3];
tdCross( f, UP, s );
tdCross( s, f, u );
out[m00] = s[0]; out[m01] = s[1]; out[m02] = s[2]; out[m03] = 0;
out[m10] = u[0]; out[m11] = u[1]; out[m12] = u[2]; out[m13] = 0;
out[m20] = -f[0];out[m21] =-f[1]; out[m22] =-f[2]; out[m23] = 0;
out[m30] = 0; out[m31] = 0; out[m32] = 0; out[m33] = 1;
tdMultiply( m, out, m );
tdTranslate( m, -eye[0], -eye[1], -eye[2] );
}
void tdPTransform( const float * pin, float * f, float * pout )
{
float ptmp[2];
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + f[m03];
ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + f[m13];
pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + f[m23];
pout[0] = ptmp[0];
pout[1] = ptmp[1];
}
void tdVTransform( const float * pin, float * f, float * pout )
{
float ptmp[2];
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02];
ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12];
pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22];
pout[0] = ptmp[0];
pout[1] = ptmp[1];
}
void td4Transform( float * pin, float * f, float * pout )
{
float ptmp[3];
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + pin[3] * f[m03];
ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + pin[3] * f[m13];
ptmp[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + pin[3] * f[m23];
pout[3] = pin[0] * f[m30] + pin[1] * f[m31] + pin[2] * f[m32] + pin[3] * f[m33];
pout[0] = ptmp[0];
pout[1] = ptmp[1];
pout[2] = ptmp[2];
}
void td4RTransform( float * pin, float * f, float * pout )
{
float ptmp[3];
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m10] + pin[2] * f[m20] + pin[3] * f[m30];
ptmp[1] = pin[0] * f[m01] + pin[1] * f[m11] + pin[2] * f[m21] + pin[3] * f[m31];
ptmp[2] = pin[0] * f[m02] + pin[1] * f[m12] + pin[2] * f[m22] + pin[3] * f[m32];
pout[3] = pin[0] * f[m03] + pin[1] * f[m13] + pin[2] * f[m23] + pin[3] * f[m33];
pout[0] = ptmp[0];
pout[1] = ptmp[1];
pout[2] = ptmp[2];
}
void tdNormalizeSelf( float * vin )
{
float vsq = 1./tdSQRT(vin[0]*vin[0] + vin[1]*vin[1] + vin[2]*vin[2]);
vin[0] *= vsq;
vin[1] *= vsq;
vin[2] *= vsq;
}
void tdCross( float * va, float * vb, float * vout )
{
float vtmp[2];
vtmp[0] = va[1] * vb[2] - va[2] * vb[1];
vtmp[1] = va[2] * vb[0] - va[0] * vb[2];
vout[2] = va[0] * vb[1] - va[1] * vb[0];
vout[0] = vtmp[0];
vout[1] = vtmp[1];
}
float tdDistance( float * va, float * vb )
{
float dx = va[0]-vb[0];
float dy = va[1]-vb[1];
float dz = va[2]-vb[2];
return tdSQRT(dx*dx + dy*dy + dz*dz);
}
float tdDot( float * va, float * vb )
{
return va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2];
}
//Stack functionality.
static float gsMatricies[2][tdMATRIXMAXDEPTH][16];
float * gSMatrix = gsMatricies[0][0];
static int gsMMode;
static int gsMPlace[2];
void tdPush()
{
if( gsMPlace[gsMMode] > tdMATRIXMAXDEPTH - 2 )
return;
tdMATCOPY( gsMatricies[gsMMode][gsMPlace[gsMMode] + 1], gsMatricies[gsMMode][gsMPlace[gsMMode]] );
gsMPlace[gsMMode]++;
gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]];
}
void tdPop()
{
if( gsMPlace[gsMMode] < 1 )
return;
gsMPlace[gsMMode]--;
gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]];
}
void tdMode( int mode )
{
if( mode < 0 || mode > 1 )
return;
gsMMode = mode;
gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]];
}
static float translateX;
static float translateY;
static float scaleX;
static float scaleY;
void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy )
{
translateX = leftx;
translateY = bottomy;
scaleX = pixx/(rightx-leftx);
scaleY = pixy/(topy-bottomy);
}
void tdFinalPoint( float * pin, float * pout )
{
float tdin[4] = { pin[0], pin[1], pin[2], 1. };
float tmp[4];
td4Transform( tdin, gsMatricies[0][gsMPlace[0]], tmp );
// printf( "XFORM1Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] );
td4Transform( tmp, gsMatricies[1][gsMPlace[1]], tmp );
// printf( "XFORM2Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] );
pout[0] = (tmp[0]/tmp[3] - translateX) * scaleX;
pout[1] = (tmp[1]/tmp[3] - translateY) * scaleY;
pout[2] = tmp[2]/tmp[3];
// printf( "XFORMFOut: %f %f %f\n", pout[0], pout[1], pout[2] );
}
float tdNoiseAt( int x, int y )
{
return ((x*13241*y + y * 33455927)%9293) / 4646. - 1.0;
}
static inline float tdFade( float f )
{
float ft3 = f*f*f;
return ft3 * 10 - ft3 * f * 15 + 6 * ft3 * f * f;
}
float tdFLerp( float a, float b, float t )
{
float fr = tdFade( t );
return a * (1.-fr) + b * fr;
}
static inline float tdFNoiseAt( float x, float y )
{
int ix = x;
int iy = y;
float fx = x - ix;
float fy = y - iy;
float a = tdNoiseAt( ix, iy );
float b = tdNoiseAt( ix+1, iy );
float c = tdNoiseAt( ix, iy+1 );
float d = tdNoiseAt( ix+1, iy+1 );
float top = tdFLerp( a, b, fx );
float bottom = tdFLerp( c, d, fx );
return tdFLerp( top, bottom, fy );
}
float tdPerlin2D( float x, float y )
{
int ndepth = 5;
int depth;
float ret = 0;
for( depth = 0; depth < ndepth; depth++ )
{
float nx = x / (1<<(ndepth-depth-1));
float ny = y / (1<<(ndepth-depth-1));
ret += tdFNoiseAt( nx, ny ) / (1<<(depth+1));
}
return ret;
}
#endif
#endif
#endif
#endif