From 52e3e778ae4e470a61c971e49c2f8fc22c24fc73 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 12 Mar 2016 18:08:54 +1000 Subject: [PATCH 01/29] UI: add window.setFont command --- documentation/sbasic_ref.csv | 2 +- src/ui/ansiwidget.cpp | 22 +++++------- src/ui/ansiwidget.h | 1 + src/ui/screen.cpp | 4 +++ src/ui/screen.h | 6 ++-- src/ui/window.cpp | 69 +++++++++++++----------------------- 6 files changed, 41 insertions(+), 63 deletions(-) diff --git a/documentation/sbasic_ref.csv b/documentation/sbasic_ref.csv index e78ed1d8..ad1eda71 100644 --- a/documentation/sbasic_ref.csv +++ b/documentation/sbasic_ref.csv @@ -106,6 +106,7 @@ Graphics,function,TXTW,634,"TXTW (s)","Returns the text width of string s in pix Graphics,function,XPOS,635,"XPOS","Returns the current X position of the cursor in ""characters""." Graphics,function,YPOS,636,"YPOS","Returns the current Y position of the cursor in ""characters""." Language,command,CALL,637,"CALL (fp)","Invoke a sub or func by address pointer." +Language,command,THROW,1437,"THROW [info [, ...]]","The THROW command (previously known as RTE) is used to initiate a catch-able error. If there is no surrounding TRY/CATCH block, THROW can be used to abort the program." Language,function,IFF,638,"IFF expr","Inline version of IF. eg, animal = ""cat"": fur = IFF( animal = ""cat"", ""yes"", ""no""): ? fur" Language,keyword,AS,1424,"AS #fileN","See: OPEN" Language,keyword,BG,1428,"SOUND frq, dur [, vol] [BG]","Play sound in the background. This prevent the program from blocking while the sound plays." @@ -291,7 +292,6 @@ System,command,ENVIRON,808,"ENVIRON expr","Adds a variable to or deletes a varia System,command,PAUSE,809,"PAUSE [secs]","Pauses the execution for a specified length of time, or until user hit the keyboard." System,command,RANDOMIZE,810,"RANDOMIZE [int]","Seeds the random number generator." System,command,STKDUMP,812,"STKDUMP","Display internal execution stack." -System,command,THROW,811,"THROW [info [, ...]]","The THROW command (previously known as RTE) is used to initiate a catch-able error. If there is no surrounding TRY/CATCH block, THROW can be used to abort the program." System,command,TROFF,813,"TROFF","See TRON." System,command,TRON,814,"TRON","When trace mechanism is ON, displays each line number as the program is executed." System,function,ENV,815,"ENV expr","See ENVIRON." diff --git a/src/ui/ansiwidget.cpp b/src/ui/ansiwidget.cpp index 1e922480..914b5a5f 100755 --- a/src/ui/ansiwidget.cpp +++ b/src/ui/ansiwidget.cpp @@ -296,6 +296,13 @@ void AnsiWidget::setColor(long fg) { _back->setColor(fg); } +// sets the font +void AnsiWidget::setFont(int size, bool bold, bool italic) { + _back->setGraphicsRendition('m', bold ? 1 : 21, 0); + _back->setGraphicsRendition('m', italic ? 3 : 23, 0); + _back->updateFont(size); +} + // sets the text font size void AnsiWidget::setFontSize(int fontSize) { this->_fontSize = fontSize; @@ -512,20 +519,7 @@ bool AnsiWidget::doEscape(const char *&p, int textHeight) { p++; } - if (*p == ' ') { - p++; - switch (*p) { - case 'C': - // GSS Graphic Size Selection - _back->_fontSize = escValue; - _back->updateFont(); - break; - } - if (p[1] == ';') { - // advance to the separator - p++; - } - } else if (_back->setGraphicsRendition(*p, escValue, textHeight)) { + if (_back->setGraphicsRendition(*p, escValue, textHeight)) { _back->updateFont(); } diff --git a/src/ui/ansiwidget.h b/src/ui/ansiwidget.h index def35ef6..32198328 100755 --- a/src/ui/ansiwidget.h +++ b/src/ui/ansiwidget.h @@ -83,6 +83,7 @@ struct AnsiWidget { void setColor(long color); void setDirty() { _back->setDirty(); } void setAutoflush(bool autoflush) { _autoflush = autoflush; } + void setFont(int size, bool bold, bool italic); void setFontSize(int fontSize); void setPixel(int x, int y, int c); void setScroll(int x, int y) { _back->setScroll(x, y); } diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index e71dd6fd..17b7fa0d 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -669,6 +669,10 @@ void GraphicScreen::resize(int newWidth, int newHeight, int oldWidth, } } +void GraphicScreen::updateFont(int size) { + setFont(_bold, _italic, size > 0 ? size : _fontSize); +} + // handles the given escape character. Returns whether the font has changed bool GraphicScreen::setGraphicsRendition(const char c, int escValue, int lineHeight) { switch (c) { diff --git a/src/ui/screen.h b/src/ui/screen.h index 1c320449..e09fa4b4 100755 --- a/src/ui/screen.h +++ b/src/ui/screen.h @@ -44,7 +44,7 @@ struct Screen : public Shape { virtual void reset(int fontSize); virtual void resize(int newWidth, int newHeight, int oldWidth, int oldHeight, int lineHeight) = 0; - virtual void updateFont() = 0; + virtual void updateFont(int size=-1) = 0; virtual int getMaxHScroll() = 0; void add(Shape *button); @@ -109,7 +109,7 @@ struct GraphicScreen : public Screen { void setPixel(int x, int y, int c); void resize(int newWidth, int newHeight, int oldWidth, int oldHeight, int lineHeight); - void updateFont() { setFont(_bold, _italic, _fontSize); } + void updateFont(int size); int getPixel(int x, int y); int getMaxHScroll() { return 0; } @@ -327,7 +327,7 @@ struct TextScreen : public Screen { bool setGraphicsRendition(const char c, int escValue, int lineHeight); void setOver(Screen *over) { _over = over; } void setPixel(int x, int y, int c) {} - void updateFont() {} + void updateFont(int size) {} int getMaxHScroll() { return (_cols * _charWidth) - w(); } private: diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 728cb657..3611372c 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -16,6 +16,7 @@ #include "ui/system.h" extern System *g_system; +void create_func(var_p_t form, const char *name, method cb); #define WINDOW_SCREEN1 "graphicsScreen1" #define WINDOW_SCREEN2 "graphicsScreen2" @@ -27,6 +28,7 @@ extern System *g_system; #define WINDOW_MESSAGE "message" #define WINDOW_VKEYPAD "showKeypad" #define WINDOW_INSET "insetTextScreen" +#define WINDOW_SETFONT "setFont" // returns the next set of string variable arguments as a String list StringList *get_items() { @@ -90,6 +92,18 @@ void cmd_window_inset(var_s *self) { } } +void cmd_window_setfont(var_s *self) { + var_num_t size; + var_int_t bold, italic; + char *unit = NULL; + par_massget("FSII", &size, &unit, &bold, &italic); + if (unit != NULL && strcmp(unit, "em") == 0) { + size *= g_system->getOutput()->getFontSize(); + } + g_system->getOutput()->setFont(size, bold, italic); + pfree(unit); +} + void cmd_window_alert(var_s *self) { StringList *items = get_items(); if (!prog_error && items->size() > 0) { @@ -130,51 +144,16 @@ void cmd_window_message(var_s *self) { extern "C" void v_create_window(var_p_t var) { map_init(var); - - var_p_t v_select_screen1 = map_add_var(var, WINDOW_SCREEN1, 0); - v_select_screen1->type = V_FUNC; - v_select_screen1->v.fn.self = var; - v_select_screen1->v.fn.cb = cmd_window_select_screen1; - - var_p_t v_select_screen2 = map_add_var(var, WINDOW_SCREEN2, 0); - v_select_screen2->type = V_FUNC; - v_select_screen2->v.fn.self = var; - v_select_screen2->v.fn.cb = cmd_window_select_screen2; - - var_p_t v_select_screen3 = map_add_var(var, WINDOW_SCREEN3, 0); - v_select_screen3->type = V_FUNC; - v_select_screen3->v.fn.self = var; - v_select_screen3->v.fn.cb = cmd_window_select_screen3; - - var_p_t v_alert = map_add_var(var, WINDOW_ALERT, 0); - v_alert->type = V_FUNC; - v_alert->v.fn.self = var; - v_alert->v.fn.cb = cmd_window_alert; - - var_p_t v_ask = map_add_var(var, WINDOW_ASK, 0); - v_ask->type = V_FUNC; - v_ask->v.fn.self = var; - v_ask->v.fn.cb = cmd_window_ask; - - var_p_t v_message = map_add_var(var, WINDOW_MESSAGE, 0); - v_message->type = V_FUNC; - v_message->v.fn.self = var; - v_message->v.fn.cb = cmd_window_message; - - var_p_t v_menu = map_add_var(var, WINDOW_MENU, 0); - v_menu->type = V_FUNC; - v_menu->v.fn.self = var; - v_menu->v.fn.cb = cmd_window_menu; - - var_p_t v_vkeypad = map_add_var(var, WINDOW_VKEYPAD, 0); - v_vkeypad->type = V_FUNC; - v_vkeypad->v.fn.self = var; - v_vkeypad->v.fn.cb = cmd_window_show_keypad; - - var_p_t v_inset = map_add_var(var, WINDOW_INSET, 0); - v_inset->type = V_FUNC; - v_inset->v.fn.self = var; - v_inset->v.fn.cb = cmd_window_inset; + create_func(var, WINDOW_SCREEN1, cmd_window_select_screen1); + create_func(var, WINDOW_SCREEN2, cmd_window_select_screen2); + create_func(var, WINDOW_SCREEN3, cmd_window_select_screen3); + create_func(var, WINDOW_ALERT, cmd_window_alert); + create_func(var, WINDOW_ASK, cmd_window_ask); + create_func(var, WINDOW_MESSAGE, cmd_window_message); + create_func(var, WINDOW_MENU, cmd_window_menu); + create_func(var, WINDOW_VKEYPAD, cmd_window_show_keypad); + create_func(var, WINDOW_INSET, cmd_window_inset); + create_func(var, WINDOW_SETFONT, cmd_window_setfont); } extern "C" void dev_show_page() { From c465237f69f6210b4e187b49e910f55297633784 Mon Sep 17 00:00:00 2001 From: chrisws Date: Tue, 15 Mar 2016 06:27:36 +1000 Subject: [PATCH 02/29] COMMON: added TIMESTAMP keyword --- ChangeLog | 6 ++-- documentation/sbasic_ref.csv | 19 +++++------ src/common/blib_func.c | 9 +++-- src/common/device.h | 11 +++++++ src/common/eval.c | 33 +++++++++---------- src/common/file.c | 49 ++++++++++++++++++++-------- src/common/kw.h | 3 +- src/languages/keywords.en.c | 5 +-- src/platform/android/jni/runtime.cpp | 2 +- src/platform/sdl/editor.cpp | 15 ++++++--- src/platform/sdl/runtime.cpp | 2 ++ src/ui/strlib.cpp | 14 ++++++-- src/ui/system.cpp | 27 ++++++++++++--- src/ui/system.h | 5 +-- 14 files changed, 134 insertions(+), 66 deletions(-) diff --git a/ChangeLog b/ChangeLog index e7426c10..736bcfd3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,14 @@ 2016-02-18 - Removed TICKSPERSEC, TICKS now returns millisecond intervals + Removed TICKSPERSEC + Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON + Added TIMESTAMP to return the modified-datetime of a file + TICKS now returns millisecond intervals Refactor eval() for performance Fixed some memory leaks in the ui handler Editor now restores cursor when returning from run Added "industrial" editor theme from Shian (slot 2) Runtime errors now show source screen with red error highlighter Form refresh command now takes an boolean arg, true=push ui state to vars - Removed BALLOC, MALLOC and VADR keywords 2016-02-11 Added export to mobile command (SDL) diff --git a/documentation/sbasic_ref.csv b/documentation/sbasic_ref.csv index ad1eda71..ffee80bf 100644 --- a/documentation/sbasic_ref.csv +++ b/documentation/sbasic_ref.csv @@ -71,7 +71,6 @@ File,command,SEEK,597,"SEEK #fileN; pos","Sets file position for the next read/w File,command,TLOAD,598,"TLOAD file, BYREF var [, type]","Loads a text file into array variable. Each text-line is an array element. type 0 = load into array (default), 1 = load into string." File,command,TSAVE,599,"TSAVE file, var","Writes an array to a text file. Each array element is a text-line." File,command,WRITE,600,"WRITE #fileN; var1 [, ...]","Store variables to a file as binary data." -File,function,ACCESS,601,"ACCESS (file)","Returns the access rights of the file." File,function,BGETC,602,"BGETC (fileN)","Reads and returns a byte from file or device (Binary mode) ." File,function,EOF,603,"EOF (fileN)","Returns true if the file pointer is at end of the file. For COMx and SOCL VFS returns true if the connection is broken." File,function,EXIST,604,"EXIST (file)","Returns true if file exists." @@ -121,12 +120,9 @@ Language,keyword,ELSE,644,"ELSE","foo = 2: if foo==1: ? ""one"": ELSE: ? ""not o Language,keyword,ELSEIF,645,"ELSEIF","foo = 2: if foo==1: ? ""one"": ELSEIF foo==2: ? ""two"": fi" Language,keyword,END TRY,1427,"END TRY","The END TRY statement marks the end of a TRY/CATCH block." Language,keyword,ENDIF,646,"ENDIF","foo = 1: if foo==1: ? ""one"": ENDIF" -Language,keyword,EXEC,647,"EXEC file","Transfers control to another operating system program." Language,keyword,EXIT,648,"EXIT [FOR|LOOP|SUB|FUNC]","Exits a multi line function definition, a loop, or a subprogram. By default (if no parameter is specified) exits from last command block (loop, for-loop or routine)." -Language,keyword,EXPORT,649,"EXPORT thing","Export a SUB, FUNC or variable from a UNIT to be used by the unit consumer." Language,keyword,FI,650,"FI","Declares the end of an IF statement. Same as ENDIF." Language,keyword,FUNC,651,"FUNC foo (a, b)","Declares a function. Return a value by assigning a 'variable' with the same name as the function." -Language,keyword,IMPORT,652,"IMPORT","Import an exported UNIT variable, SUB or FUNC." Language,keyword,LOCAL,653,"LOCAL","Declare local variables inside a SUB or FUNC. Local variables no longer exist once the SUB or FUNC has returned." Language,keyword,NEXT,654,"NEXT","See FOR." Language,keyword,SELECT,655,"SELECT CASE expr","Perform multiple tests on the expression. Offers a more concise syntax to writing successive IF tests." @@ -136,7 +132,6 @@ Language,keyword,SUB,657,"SUB foo (a, b)","Declare a sub procedure. Sub's do not Language,keyword,THEN,658,"THEN","foo = 1: if foo==1 THEN: ? ""one"": fi" Language,keyword,TO,1420,"FOR t = 1 TO 10","Specifies the loop counter end in a FOR loop" Language,keyword,TRY,1425,"TRY","The TRY statement introduces a TRY/CATCH BLOCK" -Language,keyword,UNIT,659,"UNIT name","Units are a set of procedures, functions and/or variables that can be used by another program or unit." Language,keyword,UNTIL,660,"UNTIL","a = 0: repeat: a++: ? a: UNTIL a = 10" Language,keyword,USE,661,"USE","Used with various commands for passing a user-defined expression. eg SPLIT s,"" "",v USE TRIM(x). Trim each element of v." Language,keyword,USG,1423,"PRINT USG","Synonym for USING" @@ -156,7 +151,6 @@ Language,operator,NOT,673,"a NOT b","Invert expression result. Equivalent syntax Language,operator,OR,674,"a OR b","Logical OR. Right side is not evaluated if left side evaluates to True." Language,operator,XNOR,675,"a XNOR b","Bitwise exclusive NOT OR." Language,operator,XOR,676,"a XOR b","Bitwise exclusive OR. Equivalent syntax to: a ~ b" -Language,statement,CHAIN,677,"CHAIN source","Compile and run the given source. Source can be a file name, a line of code or an array of code. Use ENV to share variables with the parent process." Language,statement,CONST,678,"CONST name = expr","Declare a variable who's value does not change during program execution." Language,statement,END,679,"END","Declares the END of a SUB or FUNC." Language,statement,FOR,680,"FOR expr","Defines a FOR/NEXT loop. FOR counter = start TO end [STEP incr] ... NEXT. FOR element IN array (or map) ... NEXT." @@ -166,7 +160,6 @@ Language,statement,IF,683,"IF expr","Tests the expression and IF it evaluates to Language,statement,LABEL,684,"LABEL name","Defines a label. A label marks a position in the code." Language,statement,LET,685,"LET var=expr","Assigns a value to the variable." Language,statement,ON,686,"ON GOTO|GOSUB label1 [, ..., labelN]","Causes a branch to one of a list of labels." -Language,statement,OPTION,687,"OPTION keyword","Used to pass parameters to the run-time environment." Language,statement,REM,688,"REM comment","Code comments. Comments can also be introduced using # and single quote character. " Language,statement,REPEAT,689,"REPEAT ... UNTIL expr","Begins the definition of a REPEAT/UNTIL loop." Language,statement,RETURN,690,"RETURN","Execution branches to the command immediately following the most recent GOSUB command." @@ -287,17 +280,21 @@ String,function,UCASE,803,"UCASE (s)","Converts the string s to upper case." String,function,UPPER,804,"UPPER (s)","Converts the string s to upper case." String,function,VAL,805,"VAL (s)","Returns the numeric value of string s." System,command,DELAY,806,"DELAY ms","Delay for a specified amount of milliseconds. Note 'delay' depends on the system clock." -System,command,ENV,807,"ENV expr","See ENVIRON." -System,command,ENVIRON,808,"ENVIRON expr","Adds a variable to or deletes a variable from the current environment variable-table." +System,command,ENV,807,"ENV expr","Adds a variable to or deletes a variable from the current environment variable-table." System,command,PAUSE,809,"PAUSE [secs]","Pauses the execution for a specified length of time, or until user hit the keyboard." System,command,RANDOMIZE,810,"RANDOMIZE [int]","Seeds the random number generator." System,command,STKDUMP,812,"STKDUMP","Display internal execution stack." System,command,TROFF,813,"TROFF","See TRON." System,command,TRON,814,"TRON","When trace mechanism is ON, displays each line number as the program is executed." -System,function,ENV,815,"ENV expr","See ENVIRON." -System,function,ENVIRON,816,"ENVIRON expr","Returns the value of a specified entry in the current environment table. If the parameter is empty ("""") then returns an array of the environment variables (in var=value form)." +System,function,ENV,815,"ENV expr","Returns the value of a specified entry in the current environment table. If the parameter is empty ("""") then returns an array of the environment variables (in var=value form)." System,function,FRE,606,"FRE (x)","Returns system information. eg, 0 = free memory, " System,function,PROGLINE,817,"PROGLINE","Returns the current program line number." System,function,RUN,818,"RUN cmdstr","Loads a secondary copy of system's shell and, executes an program, or an shell command." System,function,TICKS,819,"TICKS","Returns the number of milliseconds that have elapsed between successive calls." System,function,TIMER,821,"TIMER","Returns the number of seconds from midnight." +System,keyword,EXEC,1443,"EXEC file","Transfers control to another operating system program." +System,keyword,EXPORT,1440,"EXPORT thing","Export a SUB, FUNC or variable from a UNIT to be used by the unit consumer." +System,keyword,IMPORT,1441,"IMPORT","Import an exported UNIT variable, SUB or FUNC." +System,keyword,UNIT,1446,"UNIT name","Declares the source module as a unit. Units are a set of procedures, functions and/or variables that can be used by another program or unit." +System,statement,CHAIN,1439,"CHAIN source","Compile and run the given source. Source can be a file name, a line of code or an array of code. Use ENV to share variables with the parent process." +System,statement,OPTION,1442,"OPTION keyword","Used to pass parameters to the run-time environment." diff --git a/src/common/blib_func.c b/src/common/blib_func.c index c0fcb6cc..fcccc8f7 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -918,7 +918,6 @@ void cmd_str1(long funcCode, var_t *arg, var_t *r) { r->v.p.ptr = cstrdup(arg->v.p.ptr); r->v.p.size = strlen(r->v.p.ptr) + 1; break; - case kwBCS: // // str <- BCS$(str) @@ -930,7 +929,6 @@ void cmd_str1(long funcCode, var_t *arg, var_t *r) { r->v.p.ptr = bstrdup(arg->v.p.ptr); r->v.p.size = strlen(r->v.p.ptr) + 1; break; - case kwOCT: // // str <- OCT$(n) @@ -1165,6 +1163,13 @@ void cmd_str1(long funcCode, var_t *arg, var_t *r) { } } break; + case kwTIMESTAMP: + // + // str <- TIMESTAMP(file) + // + r->v.p.size = dev_filemtime(arg->v.p.ptr, &r->v.p.ptr); + break; + default: rt_raise("Unsupported built-in function call %ld, please report this bug (5)", funcCode); }; diff --git a/src/common/device.h b/src/common/device.h index 94816a4c..89379f4f 100644 --- a/src/common/device.h +++ b/src/common/device.h @@ -756,6 +756,17 @@ int dev_faccess(const char *file); */ int dev_fattr(const char *file); +/** + * @ingroup dev_f + * + * returns the last-modified time for a file as a string + * + * @param file is the filename + * @param buffer the result text + * @return number of characters returned in buffer + */ +int dev_filemtime(const char *file, char **buffer); + /* * * FILE I/O diff --git a/src/common/eval.c b/src/common/eval.c index e0a5a64a..ca01e69e 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -743,14 +743,14 @@ static inline void eval_callf_str1(long fcode, var_t *r) { v_init(&vtmp); eval(&vtmp); if (!prog_error) { - r->type = V_STR; - r->v.p.ptr = NULL; - cmd_str1(fcode, &vtmp, r); - v_free(&vtmp); if (CODE_PEEK() != kwTYPE_LEVEL_END) { err_missing_rp(); } else { IP++; + r->type = V_STR; + r->v.p.ptr = NULL; + cmd_str1(fcode, &vtmp, r); + v_free(&vtmp); } } } @@ -807,12 +807,12 @@ static inline void eval_callf_num(long fcode, var_t *r) { v_init(&vtmp); eval(&vtmp); if (!prog_error) { - cmd_ns1(fcode, &vtmp, r); - v_free(&vtmp); if (CODE_PEEK() != kwTYPE_LEVEL_END) { err_missing_rp(); } else { IP++; + cmd_ns1(fcode, &vtmp, r); + v_free(&vtmp); } } } @@ -848,12 +848,12 @@ static inline void eval_callf_imathI1(long fcode, var_t *r) { v_init(&vtmp); eval(&vtmp); if (!prog_error) { - r->type = V_INT; - r->v.i = cmd_imath1(fcode, &vtmp); if (CODE_PEEK() != kwTYPE_LEVEL_END) { err_missing_rp(); } else { IP++; + r->type = V_INT; + r->v.i = cmd_imath1(fcode, &vtmp); } } } @@ -877,15 +877,13 @@ static inline void eval_callf_mathN1(long fcode, var_t *r) { v_init(&vtmp); eval(&vtmp); if (!prog_error) { - r->type = V_NUM; - r->v.n = cmd_math1(fcode, &vtmp); - v_free(&vtmp); - if (!prog_error) { - if (CODE_PEEK() != kwTYPE_LEVEL_END) { - err_missing_rp(); - } else { - IP++; - } + if (CODE_PEEK() != kwTYPE_LEVEL_END) { + err_missing_rp(); + } else { + IP++; + r->type = V_NUM; + r->v.n = cmd_math1(fcode, &vtmp); + v_free(&vtmp); } } } @@ -950,6 +948,7 @@ static inline void eval_callf(var_t *r) { case kwTRIM: case kwBCS: case kwCBS: + case kwTIMESTAMP: eval_callf_str1(fcode, r); break; case kwTRANSLATEF: diff --git a/src/common/file.c b/src/common/file.c index 537b8d82..1efc0eb1 100644 --- a/src/common/file.c +++ b/src/common/file.c @@ -34,7 +34,7 @@ typedef int FileHand; static dev_file_t file_table[OS_FILEHANDLES]; /** - * Basic wild-cards + * Basic wild-cards */ int wc_match(const char *mask, char *name) { if (mask == NULL) { @@ -101,7 +101,7 @@ dev_file_t *dev_getfileptr(const int handle) { result = NULL; } else { // BASIC handles start from 1 - int hnd = handle - 1; + int hnd = handle - 1; if (hnd < 0 || hnd >= OS_FILEHANDLES) { rt_raise(FSERR_HANDLE); result = NULL; @@ -180,9 +180,9 @@ int dev_fopen(int sb_handle, const char *name, int flags) { f->type = ft_stream; - // + // // special devices - // + // if (strlen(f->name) > 4) { if (f->name[4] == ':') { for (i = 0; i < 5; i++) { @@ -207,7 +207,7 @@ int dev_fopen(int sb_handle, const char *name, int flags) { f->type = ft_socket_client; } else if (strncasecmp(f->name, "HTTP:", 5) == 0) { f->type = ft_http_client; - } else if (strncmp(f->name, "SOUT:", 5) == 0 || + } else if (strncmp(f->name, "SOUT:", 5) == 0 || strncmp(f->name, "SDIN:", 5) == 0 || strncmp(f->name, "SERR:", 5) == 0) { f->type = ft_stream; @@ -238,9 +238,9 @@ int dev_fopen(int sb_handle, const char *name, int flags) { } } // device - // + // // open - // + // switch (f->type) { case ft_stream: return stream_open(f); @@ -638,7 +638,7 @@ char_p_t *dev_create_file_list(const char *wc, int *count) { char path[OS_PATHNAME_SIZE + 1]; int l, size; char_p_t *list; - + if (wc) { strcpy(path, wc); if ((p = strrchr(path, OS_DIRSEP)) == NULL) { @@ -663,15 +663,15 @@ char_p_t *dev_create_file_list(const char *wc, int *count) { } wc2[0] = '\0'; } - + *count = 0; size = 256; list = malloc(sizeof(char_p_t) * size); - + if ((dp = opendir(path)) == NULL) { return list; } - + while ((e = readdir(dp)) != NULL) { name = e->d_name; if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) { @@ -687,7 +687,7 @@ char_p_t *dev_create_file_list(const char *wc, int *count) { *count = *count + 1; } } - + closedir(dp); // common for all, if there are no files, return NULL @@ -748,7 +748,7 @@ int dev_fattr(const char *file) { if ((vfslib = sblmgr_getvfs(file)) != -1) { return sblmgr_vfsdirexec(lib_vfs_attr, vfslib, file + 5); } - + if (stat(file, &st) == 0) { r |= ((S_ISREG(st.st_mode)) ? VFS_ATTR_FILE : 0); r |= ((S_ISDIR(st.st_mode)) ? VFS_ATTR_DIR : 0); @@ -781,3 +781,26 @@ int dev_faccess(const char *file) { } return 0; } + +/** + * returns the last-modified time for a file as a string + */ +int dev_filemtime(const char *file, char **buffer) { + + int size = 0; + struct stat st; + + if (!opt_file_permitted) { + rt_raise(ERR_FILE_PERM); + } else if (stat(file, &st) == 0) { + // 2016-02-20 05:23 PM + size = 20; + *buffer = malloc(size); + size = strftime(*buffer, size, "%Y-%m-%d %I:%M %p", localtime(&st.st_mtime)); + } else { + *buffer = malloc(1); + *buffer[0] = '\0'; + err_throw(FSERR_NOT_FOUND); + } + return size; +} diff --git a/src/common/kw.h b/src/common/kw.h index cc4c7d24..caed2258 100644 --- a/src/common/kw.h +++ b/src/common/kw.h @@ -159,8 +159,6 @@ enum keyword { // line 50 kwCLOSE, kwSCRMODE, kwSEEK, - kwACCESS, - kwSHARED, kwTYPE, kwSPRINT, kwDO, @@ -402,6 +400,7 @@ enum func_keywords { kwARRAY, kwIMAGE, kwFORM, + kwTIMESTAMP, kwNULLFUNC }; diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index 8b514aa0..faa13b17 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -168,10 +168,8 @@ struct spopr_keyword_s spopr_table[] = { { "INPUT", kwINPUTSEP }, { "OUTPUT", kwOUTPUTSEP }, { "APPEND", kwAPPENDSEP }, -{ "ACCESS", kwACCESS }, { "USING", kwUSING }, { "USG", kwUSING }, -{ "SHARED", kwSHARED }, { "AS", kwAS }, { "TO", kwTO }, { "DO", kwDO }, @@ -208,7 +206,6 @@ struct func_keyword_s func_table[] = { { "SPC", kwSPACE }, { "TAB", kwTAB }, { "CAT", kwCAT }, -{ "ENVIRON", kwENVIRONF }, { "ENV", kwENVIRONF }, { "TRIM", kwTRIM }, { "STRING", kwSTRING }, @@ -348,6 +345,7 @@ struct func_keyword_s func_table[] = { { "ARRAY", kwARRAY }, { "FORM", kwFORM }, { "WINDOW", kwWINDOW }, +{ "TIMESTAMP", kwTIMESTAMP }, { "", 0 } }; @@ -357,7 +355,6 @@ struct func_keyword_s func_table[] = { struct proc_keyword_s proc_table[] = { { "CLS", kwCLS }, { "THROW", kwTHROW }, -{ "ENVIRON", kwENVIRON }, { "ENV", kwENVIRON }, { "LOCATE", kwLOCATE }, { "AT", kwAT }, diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 1795d9ef..ba294bf1 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -749,7 +749,7 @@ void System::completeKeyword(int index) { } } -void System::editSource(strlib::String &loadPath) { +void System::editSource(strlib::String loadPath) { logEntered(); strlib::String fileName; diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 286b4d5e..9f27d012 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -86,7 +86,7 @@ void exportBuffer(AnsiWidget *out, const char *text, String &dest, String &token out->setStatus(buffer); } -void System::editSource(String &loadPath) { +void System::editSource(String loadPath) { logEntered(); int w = _output->getWidth(); @@ -99,6 +99,7 @@ void System::editSource(String &loadPath) { TextEditInput *widget = editWidget; String dirtyFile; String cleanFile; + String recentFile; enum InputMode { kInit, kExportAddr, kExportToken } inputMode = kInit; @@ -131,6 +132,7 @@ void System::editSource(String &loadPath) { showCursor(kIBeam); setRecentFile(loadPath.c_str()); + setWindowTitle(loadPath); while (_state == kEditState) { MAEvent event = getNextEvent(); @@ -295,16 +297,21 @@ void System::editSource(String &loadPath) { if (editWidget->isDirty()) { saveFile(editWidget, loadPath); } - if (getRecentFile(loadPath, event.key - SB_KEY_ALT('1'))) { - loadSource(loadPath.c_str()); + if (getRecentFile(recentFile, event.key - SB_KEY_ALT('1')) && + loadSource(recentFile.c_str())) { editWidget->reload(_programSrc); dirty = !editWidget->isDirty(); setupStatus(dirtyFile, cleanFile, loadPath); - _modifiedTime = getModifiedTime(); + setLoadPath(recentFile); + setWindowTitle(recentFile); + loadPath = recentFile; if (helpWidget->messageMode() && helpWidget->isVisible()) { showRecentFiles(helpWidget, loadPath); } + } else { + _output->setStatus("Failed to load recent file"); } + _modifiedTime = getModifiedTime(); break; default: redraw = widget->edit(event.key, sw, charWidth); diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index b380fe78..ca2e920b 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -974,6 +974,8 @@ extern "C" void dev_trace_line(int lineNo) { break; } } + } else if (!g_breakPoints.size()) { + runtime->systemPrint("Trace line: %d", lineNo); } } else { runtime->setExit(true); diff --git a/src/ui/strlib.cpp b/src/ui/strlib.cpp index 826f8838..333ac6c4 100644 --- a/src/ui/strlib.cpp +++ b/src/ui/strlib.cpp @@ -121,9 +121,17 @@ void String::empty() { } bool String::equals(const String &s, bool ignoreCase) const { - return (_buffer == 0 ? s._buffer == 0 : ignoreCase ? - strcasecmp(_buffer, s._buffer) == 0 : - strcmp(_buffer, s._buffer) == 0); + bool result; + if (_buffer == s._buffer) { + result = true; + } else if (_buffer == NULL || s._buffer == NULL) { + result = _buffer == s._buffer; + } else if (ignoreCase) { + result = strcasecmp(_buffer, s._buffer) == 0; + } else { + result = strcmp(_buffer, s._buffer) == 0; + } + return result; } bool String::equals(const char *s, bool ignoreCase) const { diff --git a/src/ui/system.cpp b/src/ui/system.cpp index f1619caf..56ec52ec 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -127,7 +127,6 @@ bool System::execute(const char *bas) { setWindowTitle(bas); showCursor(kArrow); int result = ::sbasic_main(bas); - if (isRunning()) { _state = kActiveState; } @@ -138,6 +137,15 @@ bool System::execute(const char *bas) { return result; } +bool System::fileExists(strlib::String &path) { + bool result = false; + if (path.length() > 0) { + struct stat st_file; + result = stat(path.c_str(), &st_file) == 0; + } + return result; +} + int System::getPen(int code) { _output->flush(true); int result = 0; @@ -556,10 +564,10 @@ void System::runMain(const char *mainBasPath) { _loadPath = activePath; _state = kActiveState; } else { - if (_loadPath.length() > 0) { + if (fileExists(_loadPath)) { _mainBas = false; activePath = _loadPath; - setPath(_loadPath); + setupPath(); } else { _mainBas = true; _loadPath = mainBasPath; @@ -694,7 +702,8 @@ bool System::setParentPath() { return result; } -void System::setPath(const char *filename) { +void System::setupPath() { + const char *filename = _loadPath; if (strstr(filename, "://") == NULL) { const char *slash = strrchr(filename, '/'); if (!slash) { @@ -703,10 +712,18 @@ void System::setPath(const char *filename) { if (slash) { int len = slash - filename; if (len > 0) { - char path[1024]; + // change to the loadPath directory + char path[FILENAME_MAX + 1]; strncpy(path, filename, len); path[len] = 0; chdir(path); + struct stat st_file; + if (stat(_loadPath.c_str(), &st_file) < 0) { + // reset relative path back to full path + getcwd(path, FILENAME_MAX); + strcat(path, filename + len); + _loadPath = path; + } } } } diff --git a/src/ui/system.h b/src/ui/system.h index e5c93dd7..6b9d71e6 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -69,8 +69,9 @@ struct System { protected: void checkModifiedTime(); - void editSource(strlib::String &loadPath); + void editSource(strlib::String loadPath); bool execute(const char *bas); + bool fileExists(strlib::String &path); MAEvent getNextEvent() { return processEvents(1); } uint32_t getModifiedTime(); void handleEvent(MAEvent &event); @@ -81,7 +82,7 @@ struct System { void runMain(const char *mainBasPath); void runOnce(const char *startupBas); void saveFile(TextEditInput *edit, strlib::String &path); - void setPath(const char *filename); + void setupPath(); bool setParentPath(); void setDimensions(); void showCompletion(bool success); From 6a062fe270c47db47cdada540ddf36922db6b7d3 Mon Sep 17 00:00:00 2001 From: chrisws Date: Tue, 15 Mar 2016 06:37:55 +1000 Subject: [PATCH 03/29] SDL: fix capslock handling --- src/platform/sdl/runtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index ca2e920b..abdaebea 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -415,7 +415,7 @@ void Runtime::pollEvents(bool blocking) { case SDL_TEXTINPUT: // pre-transformed/composted text mod = SDL_GetModState(); - if (!mod || (mod & KMOD_SHIFT)) { + if (!mod || (mod & (KMOD_SHIFT|KMOD_CAPS))) { // ALT + CTRL keys handled in SDL_KEYDOWN for (int i = 0; ev.text.text[i] != 0; i++) { MAEvent *keyEvent = new MAEvent(); From f850a9ee9c9184be4b2c39c4d990358da7c8d1b3 Mon Sep 17 00:00:00 2001 From: chrisws Date: Wed, 16 Mar 2016 06:41:51 +1000 Subject: [PATCH 04/29] COMMON: Removed system constants BPP and VIDADR, OSNAME --- ChangeLog | 1 + configure.ac | 2 ++ .../examples/Predefined_Variables.bas | 1 - .../distro-examples/examples/System_info.bas | 1 - src/common/brun.c | 1 - src/common/scan.c | 3 -- src/common/sys.h | 14 +++++++- src/common/var.h | 32 ++++++++----------- src/languages/keywords.en.c | 10 ++---- src/platform/sdl/editor.cpp | 2 +- src/platform/sdl/runtime.cpp | 1 - src/platform/unix/dev_null.c | 1 - 12 files changed, 33 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 736bcfd3..fc6f8ec3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Removed TICKSPERSEC Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON Added TIMESTAMP to return the modified-datetime of a file + Removed system constants BPP and VIDADR, OSNAME TICKS now returns millisecond intervals Refactor eval() for performance Fixed some memory leaks in the ui handler diff --git a/configure.ac b/configure.ac index 4369793a..f38d401f 100644 --- a/configure.ac +++ b/configure.ac @@ -364,6 +364,8 @@ checkDebugMode checkForWindows PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -Wall -Wno-unused-result" +BUILD_DATE=`date -R` +AC_DEFINE_UNQUOTED([BUILD_DATE],["$BUILD_DATE"],[Build date]) AC_SUBST(PACKAGE_CFLAGS) AC_SUBST(PACKAGE_LIBS) diff --git a/samples/distro-examples/examples/Predefined_Variables.bas b/samples/distro-examples/examples/Predefined_Variables.bas index 61cdd813..2632b698 100644 --- a/samples/distro-examples/examples/Predefined_Variables.bas +++ b/samples/distro-examples/examples/Predefined_Variables.bas @@ -1,6 +1,5 @@ ? cat(1);"Predefined Variables";cat(0) ? "OS VER =0x"; HEX$(osver) -? "OS NAME="; osname ? "SB VER =0x"; HEX$(sbver) ? "PI ="; pi ? "XMAX ="; xmax diff --git a/samples/distro-examples/examples/System_info.bas b/samples/distro-examples/examples/System_info.bas index 86e5f3df..67f18ebc 100644 --- a/samples/distro-examples/examples/System_info.bas +++ b/samples/distro-examples/examples/System_info.bas @@ -7,7 +7,6 @@ t$=chr$(9) ?b1;"System Information";b0 ? if sbver>=0x506 - ? "OS:",osname; ? "SB:",sbver ? "Display",xmax+1;"x";ymax+1 ? "Colors",2^bpp diff --git a/src/common/brun.c b/src/common/brun.c index 1d62f4d0..73ffc07e 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -281,7 +281,6 @@ void exec_setup_predefined_variables() { setsysvar_num(SYSVAR_PI, SB_PI); setsysvar_int(SYSVAR_XMAX, os_graf_mx - 1); setsysvar_int(SYSVAR_YMAX, os_graf_my - 1); - setsysvar_int(SYSVAR_BPP, os_graphics ? os_color_depth : 4); setsysvar_int(SYSVAR_TRUE, -1); setsysvar_int(SYSVAR_FALSE, 0); setsysvar_int(SYSVAR_LINECHART, 1); diff --git a/src/common/scan.c b/src/common/scan.c index bfcb1834..1dc131e7 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3459,12 +3459,10 @@ void comp_init() { bc_create(&comp_data); // create system variables - comp_vartable[comp_var_getID(LCN_SV_OSNAME)].dolar_sup = 1; comp_var_getID(LCN_SV_SBVER); comp_var_getID(LCN_SV_PI); comp_var_getID(LCN_SV_XMAX); comp_var_getID(LCN_SV_YMAX); - comp_var_getID(LCN_SV_BPP); comp_var_getID(LCN_SV_TRUE); comp_var_getID(LCN_SV_FALSE); comp_var_getID(LCN_SV_LINECHART); @@ -3475,7 +3473,6 @@ void comp_init() { comp_var_getID(LCN_SV_X); // USE keyword comp_var_getID(LCN_SV_Y); // USE keyword comp_var_getID(LCN_SV_Z); // USE keyword - comp_var_getID(LCN_SV_VADR); } /* diff --git a/src/common/sys.h b/src/common/sys.h index e3754bb8..ae66b9a9 100644 --- a/src/common/sys.h +++ b/src/common/sys.h @@ -63,8 +63,20 @@ typedef long int var_int_t; #define OS_FILEHANDLES 256 #define OS_DIRSEP '/' +#if defined(_Win32) + #define SB_VERSYS " Win32 " +#else + #define SB_VERSYS " Unix " +#endif + // SB's constants -#define SB_STR_VER VERSION +#if defined(_SDL) + #define SB_STR_VER VERSION " SDL" SB_VERSYS BUILD_DATE +#elif defined (_ANDROID) + #define SB_STR_VER VERSION " Android " BUILD_DATE +#else + #define SB_STR_VER VERSION " Console" SB_VERSYS BUILD_DATE +#endif #define SB_DWORD_VER 0x908 // 00 (major) 08 (minor) 03 (patch) #define SB_PANICMSG_SIZE 1023 #define SB_ERRMSG_SIZE 2048 diff --git a/src/common/var.h b/src/common/var.h index 1f3b7887..82e83dfe 100644 --- a/src/common/var.h +++ b/src/common/var.h @@ -42,24 +42,20 @@ /* * predefined system variables - index */ -#define SYSVAR_OSNAME 0 /**< system variable, OSNAME$ @ingroup var */ -#define SYSVAR_SBVER 1 /**< system variable, SBVER @ingroup var */ -#define SYSVAR_PI 2 /**< system variable, PI @ingroup var */ -#define SYSVAR_XMAX 3 /**< system variable, XMAX @ingroup var */ -#define SYSVAR_YMAX 4 /**< system variable, YMAX @ingroup var */ -#define SYSVAR_BPP 5 /**< system variable, BPP @ingroup var */ -#define SYSVAR_TRUE 6 /**< system variable, TRUE @ingroup var */ -#define SYSVAR_FALSE 7 /**< system variable, FALSE @ingroup var */ -#define SYSVAR_LINECHART 8 /**< system variable, LINECHART @ingroup var */ -#define SYSVAR_BARCHART 9 /**< system variable, BARCHART @ingroup var */ -#define SYSVAR_PWD 10 /**< system variable, PWD$ @ingroup var */ -#define SYSVAR_HOME 11 /**< system variable, HOME$ @ingroup var */ -#define SYSVAR_COMMAND 12 /**< system variable, COMMAND$ @ingroup var */ -#define SYSVAR_X 13 /**< system variable, X @ingroup var */ -#define SYSVAR_Y 14 /**< system variable, Y @ingroup var */ -#define SYSVAR_Z 15 /**< system variable, Z @ingroup var */ -#define SYSVAR_VIDADR 16 /**< system variable, VIDADR @ingroup var */ -#define SYSVAR_COUNT 17 /**< number of system variables @ingroup var */ +#define SYSVAR_SBVER 0 /**< system variable, SBVER @ingroup var */ +#define SYSVAR_PI 1 /**< system variable, PI @ingroup var */ +#define SYSVAR_XMAX 2 /**< system variable, XMAX @ingroup var */ +#define SYSVAR_YMAX 3 /**< system variable, YMAX @ingroup var */ +#define SYSVAR_TRUE 4 /**< system variable, TRUE @ingroup var */ +#define SYSVAR_FALSE 5 /**< system variable, FALSE @ingroup var */ +#define SYSVAR_LINECHART 6 /**< system variable, LINECHART @ingroup var */ +#define SYSVAR_BARCHART 7 /**< system variable, BARCHART @ingroup var */ +#define SYSVAR_PWD 8 /**< system variable, PWD$ @ingroup var */ +#define SYSVAR_HOME 9 /**< system variable, HOME$ @ingroup var */ +#define SYSVAR_COMMAND 10 /**< system variable, COMMAND$ @ingroup var */ +#define SYSVAR_X 11 /**< system variable, X @ingroup var */ +#define SYSVAR_Y 12 /**< system variable, Y @ingroup var */ +#define SYSVAR_Z 13 /**< system variable, Z @ingroup var */ /** * @ingroup var diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index faa13b17..2eec6d01 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -478,27 +478,21 @@ struct proc_keyword_s proc_table[] = { #define LCN_ANTIALIAS "ANTIALIAS" /* system variables */ -#define LCN_SV_OSNAME "OSNAME" #define LCN_SV_SBVER "SBVER" #define LCN_SV_PI "PI" #define LCN_SV_XMAX "XMAX" #define LCN_SV_YMAX "YMAX" -#define LCN_SV_BPP "BPP" // Bits Per Pixel #define LCN_SV_TRUE "TRUE" #define LCN_SV_FALSE "FALSE" #define LCN_SV_LINECHART "LINECHART" #define LCN_SV_BARCHART "BARCHART" -#define LCN_SV_CWD "CWD" // Current Working Directory -#define LCN_SV_HOME "HOME" // home directory (user's personal directory) +#define LCN_SV_CWD "CWD" +#define LCN_SV_HOME "HOME" #define LCN_SV_COMMAND "COMMAND" - -// x,y,z (USE keyword parameters) #define LCN_SV_X "X" #define LCN_SV_Y "Y" #define LCN_SV_Z "Z" -#define LCN_SV_VADR "VIDADR" // video-ram address - // fast cut of comments (pp) #define LCN_REM_1 ":rem " #define LCN_REM_2 ":rem\t" diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 9f27d012..20cfa495 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -301,7 +301,7 @@ void System::editSource(String loadPath) { loadSource(recentFile.c_str())) { editWidget->reload(_programSrc); dirty = !editWidget->isDirty(); - setupStatus(dirtyFile, cleanFile, loadPath); + setupStatus(dirtyFile, cleanFile, recentFile); setLoadPath(recentFile); setWindowTitle(recentFile); loadPath = recentFile; diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index abdaebea..fd7d82c2 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -819,7 +819,6 @@ void flush_queue() { // sbasic implementation // int osd_devinit(void) { - setsysvar_str(SYSVAR_OSNAME, "SDL"); runtime->setRunning(true); osd_clear_sound_queue(); return 1; diff --git a/src/platform/unix/dev_null.c b/src/platform/unix/dev_null.c index 0461f49a..56cf5bae 100644 --- a/src/platform/unix/dev_null.c +++ b/src/platform/unix/dev_null.c @@ -16,7 +16,6 @@ int osd_devinit() { os_color_depth = 1; // bits per pixel = monochrome os_graf_mx = 80; // screen width in "pixels" (characters = os_graf_my = 25; // screen height in "pixels" (characters = - setsysvar_str(SYSVAR_OSNAME, "CONSOLE"); osd_cls(); return 1; } From ded5d54d3ae120fe5e8fc6aa2b71f53594ca72ce Mon Sep 17 00:00:00 2001 From: chrisws Date: Wed, 16 Mar 2016 20:48:15 +1000 Subject: [PATCH 05/29] COMMON: cleanup constants --- ChangeLog | 2 +- documentation/build_kwp.cpp | 9 +++++++++ documentation/sbasic_ref.csv | 16 +++++++++++++--- src/common/brun.c | 2 -- src/common/file.c | 1 - src/common/scan.c | 2 -- src/common/var.h | 15 +++++++-------- src/languages/keywords.en.c | 2 -- 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index fc6f8ec3..a2efbb6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,7 +2,7 @@ Removed TICKSPERSEC Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON Added TIMESTAMP to return the modified-datetime of a file - Removed system constants BPP and VIDADR, OSNAME + Removed system constants BPP and VIDADR, OSNAME, LINECHART, BARCHART TICKS now returns millisecond intervals Refactor eval() for performance Fixed some memory leaks in the ui handler diff --git a/documentation/build_kwp.cpp b/documentation/build_kwp.cpp index 103b1c90..2daa48f1 100644 --- a/documentation/build_kwp.cpp +++ b/documentation/build_kwp.cpp @@ -95,6 +95,15 @@ bool isKeyword(const char *keyword) { } } + const char* constants[] = { + "SBVER", "PI", "XMAX", "YMAX", "TRUE", "FALSE", "CWD", "HOME", "COMMAND", "" + }; + for (int i = 0; !result && constants[i] != '\0'; i++) { + if (strcasecmp(constants[i], keyword) == 0) { + result = true; + } + } + return result; } diff --git a/documentation/sbasic_ref.csv b/documentation/sbasic_ref.csv index ffee80bf..487277fe 100644 --- a/documentation/sbasic_ref.csv +++ b/documentation/sbasic_ref.csv @@ -49,7 +49,10 @@ Date,command,TIMEHMS,574,"TIMEHMS hms| timer, BYREF h, BYREF m, BYREF s","Conver Date,function,DATE,575,"DATE","Returns the current date as string ""DD/MM/YYYY""." Date,function,DATEFMT,576,"DATEFMT (format, dmy| (d,m,y)| julian_date)","Returns formatted date string." Date,function,JULIAN,577,"JULIAN (dmy| (d,m,y))","Returns the Julian date. (dates must be greater than 1/1/100 AD)." +Date,function,TICKS,1448,"TICKS","Returns the number of milliseconds that have elapsed between successive calls." Date,function,TIME,578,"TIME","Returns the current time as string ""HH:MM:SS""." +Date,function,TIMER,1449,"TIMER","Returns the number of seconds from midnight." +Date,function,TIMESTAMP,1450,"TIMESTAMP filename","Returns the file last modified date and time." Date,function,WEEKDAY,579,"WEEKDAY (dmy| (d,m,y)| julian_date)","Returns the day of the week (0 = Sunday)." File,command,ACCESS,580,"ACCESS (file)","Returns the access rights of the file." File,command,BLOAD,582,"BLOAD filename[, address]","Loads a specified memory image file into memory." @@ -106,6 +109,15 @@ Graphics,function,XPOS,635,"XPOS","Returns the current X position of the cursor Graphics,function,YPOS,636,"YPOS","Returns the current Y position of the cursor in ""characters""." Language,command,CALL,637,"CALL (fp)","Invoke a sub or func by address pointer." Language,command,THROW,1437,"THROW [info [, ...]]","The THROW command (previously known as RTE) is used to initiate a catch-able error. If there is no surrounding TRY/CATCH block, THROW can be used to abort the program." +Language,constant,COMMAND,1459,"COMMAND","SmallBASIC startup command line options." +Language,constant,CWD,1456,"CWD","Current working directory" +Language,constant,FALSE,1457,"FALSE","FALSE" +Language,constant,HOME,1458,"HOME","User HOME folder" +Language,constant,PI,1452,"PI","Holds PI" +Language,constant,SBVER,1451,"SBVER","Version and build information" +Language,constant,TRUE,1455,"TRUE","TRUE" +Language,constant,XMAX,1453,"XMAX","Holds the screen width in pixels" +Language,constant,YMAX,1454,"YMAX","Holds the screen height in pixels." Language,function,IFF,638,"IFF expr","Inline version of IF. eg, animal = ""cat"": fur = IFF( animal = ""cat"", ""yes"", ""no""): ? fur" Language,keyword,AS,1424,"AS #fileN","See: OPEN" Language,keyword,BG,1428,"SOUND frq, dur [, vol] [BG]","Play sound in the background. This prevent the program from blocking while the sound plays." @@ -140,7 +152,7 @@ Language,operator,AND,662,"a AND b","Logical AND. Right side is not evaluated if Language,operator,BAND,663,"a BAND b","Bitwise AND." Language,operator,BOR,664,"a BOR b","Bitwise OR." Language,operator,EQV,665,"a EQV b","Bitwise equivalence." -Language,operator,IMP,666,"a IMP b","Bitwise IMP." +Language,operator,IMP,666,"a IMP b","Used to perform a logical implication on two expressions." Language,operator,IN,667,"a IN b","Returns an index (1 Based) to the matching element when b is an array. Returns TRUE if the value is contained within b as a string." Language,operator,LIKE,668,"a LIKE b","Regular-expression operator. Compares the left part of the expression with the right side regex pattern." Language,operator,MDL,669,"MDL","Modulus." @@ -290,8 +302,6 @@ System,function,ENV,815,"ENV expr","Returns the value of a specified entry in th System,function,FRE,606,"FRE (x)","Returns system information. eg, 0 = free memory, " System,function,PROGLINE,817,"PROGLINE","Returns the current program line number." System,function,RUN,818,"RUN cmdstr","Loads a secondary copy of system's shell and, executes an program, or an shell command." -System,function,TICKS,819,"TICKS","Returns the number of milliseconds that have elapsed between successive calls." -System,function,TIMER,821,"TIMER","Returns the number of seconds from midnight." System,keyword,EXEC,1443,"EXEC file","Transfers control to another operating system program." System,keyword,EXPORT,1440,"EXPORT thing","Export a SUB, FUNC or variable from a UNIT to be used by the unit consumer." System,keyword,IMPORT,1441,"IMPORT","Import an exported UNIT variable, SUB or FUNC." diff --git a/src/common/brun.c b/src/common/brun.c index 73ffc07e..3dbfbacd 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -283,8 +283,6 @@ void exec_setup_predefined_variables() { setsysvar_int(SYSVAR_YMAX, os_graf_my - 1); setsysvar_int(SYSVAR_TRUE, -1); setsysvar_int(SYSVAR_FALSE, 0); - setsysvar_int(SYSVAR_LINECHART, 1); - setsysvar_int(SYSVAR_BARCHART, 2); setsysvar_str(SYSVAR_PWD, dev_getcwd()); setsysvar_str(SYSVAR_COMMAND, opt_command); diff --git a/src/common/file.c b/src/common/file.c index 1efc0eb1..22f91d31 100644 --- a/src/common/file.c +++ b/src/common/file.c @@ -786,7 +786,6 @@ int dev_faccess(const char *file) { * returns the last-modified time for a file as a string */ int dev_filemtime(const char *file, char **buffer) { - int size = 0; struct stat st; diff --git a/src/common/scan.c b/src/common/scan.c index 1dc131e7..d6fd47c4 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -3465,8 +3465,6 @@ void comp_init() { comp_var_getID(LCN_SV_YMAX); comp_var_getID(LCN_SV_TRUE); comp_var_getID(LCN_SV_FALSE); - comp_var_getID(LCN_SV_LINECHART); - comp_var_getID(LCN_SV_BARCHART); comp_vartable[comp_var_getID(LCN_SV_CWD)].dolar_sup = 1; comp_vartable[comp_var_getID(LCN_SV_HOME)].dolar_sup = 1; comp_vartable[comp_var_getID(LCN_SV_COMMAND)].dolar_sup = 1; diff --git a/src/common/var.h b/src/common/var.h index 82e83dfe..1a7a0725 100644 --- a/src/common/var.h +++ b/src/common/var.h @@ -48,14 +48,13 @@ #define SYSVAR_YMAX 3 /**< system variable, YMAX @ingroup var */ #define SYSVAR_TRUE 4 /**< system variable, TRUE @ingroup var */ #define SYSVAR_FALSE 5 /**< system variable, FALSE @ingroup var */ -#define SYSVAR_LINECHART 6 /**< system variable, LINECHART @ingroup var */ -#define SYSVAR_BARCHART 7 /**< system variable, BARCHART @ingroup var */ -#define SYSVAR_PWD 8 /**< system variable, PWD$ @ingroup var */ -#define SYSVAR_HOME 9 /**< system variable, HOME$ @ingroup var */ -#define SYSVAR_COMMAND 10 /**< system variable, COMMAND$ @ingroup var */ -#define SYSVAR_X 11 /**< system variable, X @ingroup var */ -#define SYSVAR_Y 12 /**< system variable, Y @ingroup var */ -#define SYSVAR_Z 13 /**< system variable, Z @ingroup var */ +#define SYSVAR_PWD 6 /**< system variable, PWD$ @ingroup var */ +#define SYSVAR_HOME 7 /**< system variable, HOME$ @ingroup var */ +#define SYSVAR_COMMAND 8 /**< system variable, COMMAND$ @ingroup var */ +#define SYSVAR_X 9 /**< system variable, X @ingroup var */ +#define SYSVAR_Y 10 /**< system variable, Y @ingroup var */ +#define SYSVAR_Z 11 /**< system variable, Z @ingroup var */ +#define SYSVAR_COUNT 12 /** * @ingroup var diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index 2eec6d01..92488c8d 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -484,8 +484,6 @@ struct proc_keyword_s proc_table[] = { #define LCN_SV_YMAX "YMAX" #define LCN_SV_TRUE "TRUE" #define LCN_SV_FALSE "FALSE" -#define LCN_SV_LINECHART "LINECHART" -#define LCN_SV_BARCHART "BARCHART" #define LCN_SV_CWD "CWD" #define LCN_SV_HOME "HOME" #define LCN_SV_COMMAND "COMMAND" From 306b5ee5ae2f9ca6096860b5b952e043c234942f Mon Sep 17 00:00:00 2001 From: chrisws Date: Thu, 17 Mar 2016 19:12:54 +1000 Subject: [PATCH 06/29] UI: tweak hash to avoid false highlighting --- documentation/build_kwp.cpp | 8 ++++---- documentation/mkref.bas | 16 ++++++++++++++++ src/ui/textedit.cpp | 4 ++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/documentation/build_kwp.cpp b/documentation/build_kwp.cpp index 2daa48f1..a1bb2a9c 100644 --- a/documentation/build_kwp.cpp +++ b/documentation/build_kwp.cpp @@ -232,8 +232,8 @@ uint32_t getHash(const char *key) { uint32_t hash, i; for (hash = i = 0; key[i] != '\0'; i++) { hash += tolower(key[i]); - hash += (hash << 3); - hash ^= (hash >> 1); + hash += (hash << 4); + hash ^= (hash >> 2); } return hash; } @@ -273,7 +273,7 @@ int main(int argc, char *argv[]) { HelpItem *item = (*it); if (strcasecmp(item->package, "Language") == 0) { count++; - fprintf(stdout, " %uu,\n", getHash(item->keyword)); + fprintf(stdout, " %uu, //%s\n", getHash(item->keyword), item->keyword); } } fprintf(stdout, "};\n"); @@ -285,7 +285,7 @@ int main(int argc, char *argv[]) { HelpItem *item = (*it); if (strcasecmp(item->package, "Language") != 0) { count++; - fprintf(stdout, " %uu,\n", getHash(item->keyword)); + fprintf(stdout, " %uu, //%s\n", getHash(item->keyword), item->keyword); } } fprintf(stdout, "};\n"); diff --git a/documentation/mkref.bas b/documentation/mkref.bas index fe6f9feb..10949771 100755 --- a/documentation/mkref.bas +++ b/documentation/mkref.bas @@ -23,6 +23,8 @@ in_map = array(in_str) if (len(args) == 2 && args(1) == "txt") then mk_text_reference(in_map) +elif (len(args) == 2 && args(1) == "bas") then + mk_bas(in_map) else mk_help(in_map) fi @@ -56,6 +58,20 @@ func fix_comments(comments, keyword) fix_comments = comments end +sub mk_bas(byref in_map) + local i, row, group, keyword + local in_map_len = len(in_map) - 1 + for i = 0 to in_map_len + row = in_map(i).body_value + keyword = get_field(row, "keyword=", true) + group = get_field(row, "group=", true) + ? group + " " + keyword + while (i + 1 < in_map_len && in_map(i).entity_id == in_map(i + 1).entity_id) + i++ + wend + next i +end + sub mk_help(byref in_map) local i, row, group, type, keyword, syntax, brief local in_map_len = len(in_map) - 1 diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index c8a94b25..ea4b288f 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -1165,8 +1165,8 @@ uint32_t TextEditInput::getHash(const char *str, int offs, int &count) { break; } result += tolower(str[offs + count]); - result += (result << 3); - result ^= (result >> 1); + result += (result << 4); + result ^= (result >> 2); } } return result; From 103f43938bedd87cd679caf9f8c448508a24d43a Mon Sep 17 00:00:00 2001 From: chrisws Date: Thu, 17 Mar 2016 19:51:43 +1000 Subject: [PATCH 07/29] SDL: fix recent file error --- src/platform/sdl/editor.cpp | 27 +++++++++++++++------------ src/ui/system.cpp | 4 +++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 20cfa495..70ca661c 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -297,19 +297,22 @@ void System::editSource(String loadPath) { if (editWidget->isDirty()) { saveFile(editWidget, loadPath); } - if (getRecentFile(recentFile, event.key - SB_KEY_ALT('1')) && - loadSource(recentFile.c_str())) { - editWidget->reload(_programSrc); - dirty = !editWidget->isDirty(); - setupStatus(dirtyFile, cleanFile, recentFile); - setLoadPath(recentFile); - setWindowTitle(recentFile); - loadPath = recentFile; - if (helpWidget->messageMode() && helpWidget->isVisible()) { - showRecentFiles(helpWidget, loadPath); + if (getRecentFile(recentFile, event.key - SB_KEY_ALT('1'))) { + if (loadSource(recentFile.c_str())) { + editWidget->reload(_programSrc); + dirty = !editWidget->isDirty(); + setupStatus(dirtyFile, cleanFile, recentFile); + setLoadPath(recentFile); + setWindowTitle(recentFile); + loadPath = recentFile; + if (helpWidget->messageMode() && helpWidget->isVisible()) { + showRecentFiles(helpWidget, loadPath); + } + } else { + String message("Failed to load recent file: "); + message.append(recentFile); + _output->setStatus(message); } - } else { - _output->setStatus("Failed to load recent file"); } _modifiedTime = getModifiedTime(); break; diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 56ec52ec..e498ddb6 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -139,7 +139,9 @@ bool System::execute(const char *bas) { bool System::fileExists(strlib::String &path) { bool result = false; - if (path.length() > 0) { + if (path.indexOf("://", 1) != -1) { + result = true; + } else if (path.length() > 0) { struct stat st_file; result = stat(path.c_str(), &st_file) == 0; } From e8285006091623aa4415a1cdf44b264a4189f890 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Mar 2016 17:01:40 +1000 Subject: [PATCH 08/29] COMMON: fix problem with escape chars using format --- ChangeLog | 5 +++++ ide/android/assets/main.bas | 4 ++-- samples/distro-examples/tests/eval-test.bas | 19 +++++++++++++++++++ src/common/scan.c | 10 +++++----- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index a2efbb6e..0841f4fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,13 +3,18 @@ Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON Added TIMESTAMP to return the modified-datetime of a file Removed system constants BPP and VIDADR, OSNAME, LINECHART, BARCHART + SBVER now includes build information TICKS now returns millisecond intervals Refactor eval() for performance Fixed some memory leaks in the ui handler Editor now restores cursor when returning from run + Editor now displays the correct filename in the title bar Added "industrial" editor theme from Shian (slot 2) Runtime errors now show source screen with red error highlighter Form refresh command now takes an boolean arg, true=push ui state to vars + Added window.setFont command to set font size, bold and italic. example: + w = window():w.setFont(10, "pt", false, true) + Fix problem with escaped chars using FORMAT 2016-02-11 Added export to mobile command (SDL) diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index 61e4d80b..f06c2d4f 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -71,7 +71,7 @@ sub do_about() print "(_ ._ _ _.|||_) /\ (_ |/ " print "__)| | |(_||||_)/--\__)|\_" print - print "Version 0.12.5" + print "Version 0.12.6" print print "Copyright (c) 2002-2015 Chris Warren-Smith" print "Copyright (c) 1999-2006 Nic Christopoulos" + chr(10) @@ -386,7 +386,7 @@ sub main local frm frm.inputs << bn_files frm.inputs << bn_online - if (osname != "SDL") then + if (instr(sbver, "SDL") == 0) then frm.inputs << bn_setup endif frm.inputs << bn_about diff --git a/samples/distro-examples/tests/eval-test.bas b/samples/distro-examples/tests/eval-test.bas index 638a0cc6..7d4a48c6 100644 --- a/samples/distro-examples/tests/eval-test.bas +++ b/samples/distro-examples/tests/eval-test.bas @@ -98,3 +98,22 @@ for i = 1 to end_el step 0.1 next expected = 1 + ((end_el-1) * 10) if (expected != steps) then throw "incorrect step count:" + steps + " " + expected + +rem test fix for http://smallbasic.sourceforge.net/?q=node/1444 +func assertEq(a, b) + if a != b + throw "result: '" + a + "' != expected: '" + b + "'" + endif +end + +assertEq format("\\ \\", "abcde"), "abc" +assertEq format(" \\ \\", "abcde"), " abc" +assertEq format("\\ \\ ", "abcde"), "abc " +assertEq format(" \\ \\ ", "abcde"), " abc " +assertEq format("\\ $ \\", "abcde"), "ab$cd" +assertEq format(" \\$\\", "abcde"), " a$b" +assertEq format("\\$\\ ", "abcde"), "a$b " +assertEq format(" \\$\\ ", "abcde"), " a$b " +assertEq format(" \\$\\ ", "abcde"), " a$b " +assertEq format("\\abcde\\", "ABbcde"), "AabcdeB" + diff --git a/src/common/scan.c b/src/common/scan.c index d6fd47c4..4d4270dc 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -492,8 +492,8 @@ char *get_param_sect(char *text, const char *delim, char *dest) { while (*p) { if (quotes) { - if (*p == '\\' && *(p + 1) == '\"') { - // add the escaped quote and continue + if (*p == '\\' && (*(p + 1) == '\"' || *(p + 1) == '\\')) { + // add the escaped quote or slash and continue *d++ = *p++; } else if (*p == '\"') { quotes = 0; @@ -1791,7 +1791,7 @@ void bc_store_exports(const char *slist) { if (comp_exptable.count) { offset = comp_exptable.count; comp_exptable.count += count; - comp_exptable.elem = (unit_sym_t **)realloc(comp_exptable.elem, + comp_exptable.elem = (unit_sym_t **)realloc(comp_exptable.elem, comp_exptable.count * sizeof(unit_sym_t **)); } else { offset = 0; @@ -3684,8 +3684,8 @@ char *comp_format_text(const char *source) { } } else { // in quotes - if (*p == '\\' && *(p + 1) == '\"') { - // add the escaped quote and continue + if (*p == '\\' && (*(p + 1) == '\"' || *(p + 1) == '\\')) { + // add the escaped quote or slash and continue *ps++ = *p++; } else if (*p == '\"' || *p == '\n') { // new line auto-ends the quoted string From c6775ab847c4befb7b681681e1bd313bfad1f8eb Mon Sep 17 00:00:00 2001 From: chrisws Date: Mon, 21 Mar 2016 21:12:51 +1000 Subject: [PATCH 09/29] COMMON: Fix problem with XNOR command result --- ChangeLog | 4 +- samples/distro-examples/tests/eval-test.bas | 97 +++++++++++++++++--- samples/distro-examples/tests/output/ref.out | 2 +- src/common/brun.c | 2 +- src/common/eval.c | 6 +- src/common/sys.h | 16 +++- src/languages/keywords.en.c | 2 +- 7 files changed, 105 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0841f4fe..27c2f6b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,8 @@ 2016-02-18 Removed TICKSPERSEC Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON - Added TIMESTAMP to return the modified-datetime of a file Removed system constants BPP and VIDADR, OSNAME, LINECHART, BARCHART + Added TIMESTAMP to return the modified-datetime of a file SBVER now includes build information TICKS now returns millisecond intervals Refactor eval() for performance @@ -15,6 +15,8 @@ Added window.setFont command to set font size, bold and italic. example: w = window():w.setFont(10, "pt", false, true) Fix problem with escaped chars using FORMAT + Fix problem with XNOR command result + TRUE is now always returned as 1 2016-02-11 Added export to mobile command (SDL) diff --git a/samples/distro-examples/tests/eval-test.bas b/samples/distro-examples/tests/eval-test.bas index 7d4a48c6..555fa2b7 100644 --- a/samples/distro-examples/tests/eval-test.bas +++ b/samples/distro-examples/tests/eval-test.bas @@ -100,20 +100,93 @@ expected = 1 + ((end_el-1) * 10) if (expected != steps) then throw "incorrect step count:" + steps + " " + expected rem test fix for http://smallbasic.sourceforge.net/?q=node/1444 -func assertEq(a, b) +func assertEq(a, b, pline) if a != b - throw "result: '" + a + "' != expected: '" + b + "'" + throw "Line: " + pline + " result: '" + a + "' != expected: '" + b + "'" endif end -assertEq format("\\ \\", "abcde"), "abc" -assertEq format(" \\ \\", "abcde"), " abc" -assertEq format("\\ \\ ", "abcde"), "abc " -assertEq format(" \\ \\ ", "abcde"), " abc " -assertEq format("\\ $ \\", "abcde"), "ab$cd" -assertEq format(" \\$\\", "abcde"), " a$b" -assertEq format("\\$\\ ", "abcde"), "a$b " -assertEq format(" \\$\\ ", "abcde"), " a$b " -assertEq format(" \\$\\ ", "abcde"), " a$b " -assertEq format("\\abcde\\", "ABbcde"), "AabcdeB" +assertEq format("\\ \\", "abcde"), "abc", PROGLINE +assertEq format(" \\ \\", "abcde"), " abc", PROGLINE +assertEq format("\\ \\ ", "abcde"), "abc ", PROGLINE +assertEq format(" \\ \\ ", "abcde"), " abc ",PROGLINE +assertEq format("\\ $ \\", "abcde"), "ab$cd",PROGLINE +assertEq format(" \\$\\", "abcde"), " a$b", PROGLINE +assertEq format("\\$\\ ", "abcde"), "a$b ", PROGLINE +assertEq format(" \\$\\ ", "abcde"), " a$b ",PROGLINE +assertEq format(" \\$\\ ", "abcde"), " a$b ",PROGLINE +assertEq format("\\abcde\\", "ABbcde"), "AabcdeB",PROGLINE +rem http://smallbasic.sourceforge.net/?q=node/1445 +rem http://smallbasic.sourceforge.net/?q=node/1461 +rem https://en.wikipedia.org/wiki/XOR_gate +rem https://en.wikipedia.org/wiki/XNOR_gate + +rem Logical +assertEq 0 OR 0, FALSE, PROGLINE +assertEq 0 OR 1, TRUE, PROGLINE +assertEq 1 OR 0, TRUE, PROGLINE +assertEq 1 OR 1, TRUE, PROGLINE +assertEq 0 == 0, TRUE, PROGLINE +assertEq 0 == 1, FALSE, PROGLINE +assertEq 1 == 0, FALSE, PROGLINE +assertEq 1 == 1, TRUE, PROGLINE +assertEq 0 != 0, FALSE, PROGLINE +assertEq 0 != 1, TRUE, PROGLINE +assertEq 1 != 0, TRUE, PROGLINE +assertEq 1 != 1, FALSE, PROGLINE +assertEq 0 AND 0, FALSE, PROGLINE +assertEq 0 AND 1, FALSE, PROGLINE +assertEq 1 AND 0, FALSE, PROGLINE +assertEq 1 AND 1, TRUE, PROGLINE + +rem Bitwise +assertEq 0 BOR 0, 0, PROGLINE +assertEq 7 BOR 6, 7, PROGLINE +assertEq 100 BOR 0, 100, PROGLINE +assertEq 1 BOR 1, 1, PROGLINE +assertEq 0 BAND 0, 0, PROGLINE +assertEq 0 BAND 1, 0, PROGLINE +assertEq 1 BAND 0, 0, PROGLINE +assertEq 1 BAND 1, 1, PROGLINE +assertEq 0 NAND 0, 0xffffffffffffffff, PROGLINE +assertEq 0 NAND 1, 0xffffffffffffffff, PROGLINE +assertEq 1 NAND 0, 0xffffffffffffffff, PROGLINE +assertEq 1 NAND 1, 0xfffffffffffffffe, PROGLINE +assertEq 0 NOR 0, 0xffffffffffffffff, PROGLINE +assertEq 0 NOR 1, 0xfffffffffffffffe, PROGLINE +assertEq 1 NOR 0, 0xfffffffffffffffe, PROGLINE +assertEq 1 NOR 1, 0xfffffffffffffffe, PROGLINE +assertEq 0 XOR 0, 0, PROGLINE +assertEq 0 XOR 1, 1, PROGLINE +assertEq 1 XOR 0, 1, PROGLINE +assertEq 1 XOR 1, 0, PROGLINE +assertEq 0 XNOR 0, 0xffffffffffffffff, PROGLINE +assertEq 0 XNOR 1, 0xfffffffffffffffe, PROGLINE +assertEq 1 XNOR 0, 0xfffffffffffffffe, PROGLINE +assertEq 1 XNOR 1, 0xffffffffffffffff, PROGLINE + +' mixed +assertEq FALSE, 0, PROGLINE +assertEq TRUE, 1, PROGLINE +assertEq !FALSE, 1, PROGLINE +assertEq 0 AND 0, 0, PROGLINE +assertEq 0 AND 1, 0, PROGLINE +assertEq 1 AND 1, 1, PROGLINE +assertEq 0 OR 0, 0, PROGLINE +assertEq 0 OR 1, 1, PROGLINE +assertEq 1 OR 1, 1, PROGLINE +assertEq NOT 0, 1, PROGLINE +assertEq NOT 1, 0, PROGLINE +assertEq 0 <> 0, 0, PROGLINE +assertEq 0 <> 1, 1, PROGLINE +assertEq 1 <> 1, 0, PROGLINE + +assertEq 0 IMP 0, 255, PROGLINE +assertEq 0 IMP 1, 255, PROGLINE +assertEq 1 IMP 0, 254, PROGLINE +assertEq 1 IMP 1, 255, PROGLINE +assertEq 0 EQV 0, 255, PROGLINE +assertEq 0 EQV 1, 254, PROGLINE +assertEq 1 EQV 0, 254, PROGLINE +assertEq 1 EQV 1, 255, PROGLINE diff --git a/samples/distro-examples/tests/output/ref.out b/samples/distro-examples/tests/output/ref.out index f9ec54ea..560f580a 100644 --- a/samples/distro-examples/tests/output/ref.out +++ b/samples/distro-examples/tests/output/ref.out @@ -7,7 +7,7 @@ a<>b a<>b {"name":"kitchen"} {"name":"hall"} {"fridge":"empty","name":"Kitchen"} -{"name":"toilet","occupied":-1} +{"name":"toilet","occupied":1} {"fridge":"empty","name":"Kitchen"} {"name":"hall"} toilet diff --git a/src/common/brun.c b/src/common/brun.c index 3dbfbacd..daf784b8 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -281,7 +281,7 @@ void exec_setup_predefined_variables() { setsysvar_num(SYSVAR_PI, SB_PI); setsysvar_int(SYSVAR_XMAX, os_graf_mx - 1); setsysvar_int(SYSVAR_YMAX, os_graf_my - 1); - setsysvar_int(SYSVAR_TRUE, -1); + setsysvar_int(SYSVAR_TRUE, 1); setsysvar_int(SYSVAR_FALSE, 0); setsysvar_str(SYSVAR_PWD, dev_getcwd()); setsysvar_str(SYSVAR_COMMAND, opt_command); diff --git a/src/common/eval.c b/src/common/eval.c index ca01e69e..2a6c3c79 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -466,9 +466,9 @@ static inline void oper_unary(var_t *r, byte op) { static inline void oper_log(var_t *r, var_t *left, byte op) { var_int_t li; var_int_t ri; - int i; var_int_t a, b; var_int_t ba, bb; + int i; if (op != OPLOG_IN) { li = v_igetval(left); @@ -480,10 +480,10 @@ static inline void oper_log(var_t *r, var_t *left, byte op) { switch (op) { case OPLOG_AND: - ri = (li && ri) ? -1 : 0; + ri = (li && ri) ? 1 : 0; break; case OPLOG_OR: - ri = (li || ri) ? -1 : 0; + ri = (li || ri) ? 1 : 0; break; case OPLOG_EQV: a = li; diff --git a/src/common/sys.h b/src/common/sys.h index ae66b9a9..e726ebd9 100644 --- a/src/common/sys.h +++ b/src/common/sys.h @@ -64,18 +64,24 @@ typedef long int var_int_t; #define OS_DIRSEP '/' #if defined(_Win32) - #define SB_VERSYS " Win32 " + #define SB_VERSYS "Win" #else - #define SB_VERSYS " Unix " + #define SB_VERSYS "Unix" #endif -// SB's constants +#if UINTPTR_MAX == 0xffffffff + #define SB_BIT_SZ "_32 " +#else + #define SB_BIT_SZ "_64 " +#endif + + // SB's constants #if defined(_SDL) - #define SB_STR_VER VERSION " SDL" SB_VERSYS BUILD_DATE + #define SB_STR_VER VERSION " SDL " SB_VERSYS SB_BIT_SZ BUILD_DATE #elif defined (_ANDROID) #define SB_STR_VER VERSION " Android " BUILD_DATE #else - #define SB_STR_VER VERSION " Console" SB_VERSYS BUILD_DATE + #define SB_STR_VER VERSION " Console " SB_VERSYS SB_BIT_SZ BUILD_DATE #endif #define SB_DWORD_VER 0x908 // 00 (major) 08 (minor) 03 (patch) #define SB_PANICMSG_SIZE 1023 diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index 92488c8d..d1535a67 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -145,7 +145,7 @@ struct opr_keyword_s opr_table[] = { { "IMP", kwTYPE_LOGOPR, OPLOG_IMP }, { "NAND", kwTYPE_LOGOPR, OPLOG_NAND }, { "NOR", kwTYPE_LOGOPR, OPLOG_NOR }, -{ "XNOR", kwTYPE_LOGOPR, OPLOG_NOR }, +{ "XNOR", kwTYPE_LOGOPR, OPLOG_XNOR }, { "IN", kwTYPE_CMPOPR, OPLOG_IN }, { "LIKE", kwTYPE_CMPOPR, OPLOG_LIKE }, { "", 0, 0 } From 14a405055c64aed5cef0bc7bab4b9d8ca3aaee79 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 25 Mar 2016 09:22:41 +1000 Subject: [PATCH 10/29] COMMON: fix IMP and EQV --- samples/distro-examples/tests/eval-test.bas | 29 +++++++++++++------ src/common/eval.c | 31 +++++++++++++-------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/samples/distro-examples/tests/eval-test.bas b/samples/distro-examples/tests/eval-test.bas index 555fa2b7..f19ae06a 100644 --- a/samples/distro-examples/tests/eval-test.bas +++ b/samples/distro-examples/tests/eval-test.bas @@ -182,11 +182,24 @@ assertEq 0 <> 0, 0, PROGLINE assertEq 0 <> 1, 1, PROGLINE assertEq 1 <> 1, 0, PROGLINE -assertEq 0 IMP 0, 255, PROGLINE -assertEq 0 IMP 1, 255, PROGLINE -assertEq 1 IMP 0, 254, PROGLINE -assertEq 1 IMP 1, 255, PROGLINE -assertEq 0 EQV 0, 255, PROGLINE -assertEq 0 EQV 1, 254, PROGLINE -assertEq 1 EQV 0, 254, PROGLINE -assertEq 1 EQV 1, 255, PROGLINE +' The IMP operator returns 0 if the first operand is 1 and its second operand is 0. In all other +' cases, it returns 1. The operation is perform on each bit position in the two arguments. +' !a || b +' 1100 +' 1010 +' 1011 +assertEq 0xcccccccccccccccc Imp 0xaaaaaaaaaaaaaaaa, 0xbbbbbbbbbbbbbbbb, PROGLINE +assertEq 0xcccccccc Imp 0xaaaaaaaa, 0xbbbbbbbb, PROGLINE +assertEq 0xcccc Imp 0xaaaa, 0xbbbb, PROGLINE +assertEq 0xcc Imp 0xaa, 0xbb, PROGLINE +assertEq 0xc Imp 0xa, 0xb, PROGLINE + +' The Eqv operator returns 1 if and only if both inputs are equal. +' 1100 +' 1010 +' 1001 +assertEq 0xcccccccccccccccc Eqv 0xaaaaaaaaaaaaaaaa, 0x9999999999999999, PROGLINE +assertEq 0xcccccccc Eqv 0xaaaaaaaa, 0x99999999, PROGLINE +assertEq 0xcccc Eqv 0xaaaa, 0x9999, PROGLINE +assertEq 0xcc Eqv 0xaa, 0x99, PROGLINE +assertEq 0xc Eqv 0xa, 0x9, PROGLINE diff --git a/src/common/eval.c b/src/common/eval.c index 2a6c3c79..0388c384 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -467,8 +467,7 @@ static inline void oper_log(var_t *r, var_t *left, byte op) { var_int_t li; var_int_t ri; var_int_t a, b; - var_int_t ba, bb; - int i; + int i, set; if (op != OPLOG_IN) { li = v_igetval(left); @@ -489,11 +488,15 @@ static inline void oper_log(var_t *r, var_t *left, byte op) { a = li; b = ri; ri = 0; - for (i = 0; i < sizeof(var_int_t); i++) { - ba = a & (1 << i); - bb = b & (1 << i); - if ((ba && bb) || (!ba && !bb)) { - ri |= (1 << i); + set = 0; + for (i = (sizeof(var_int_t) * 8) - 1; i >= 0; i--) { + int ba = ((a >> i) & 1); + int bb = ((b >> i) & 1); + if (ba || bb) { + set = 1; + } + if (set && ba == bb) { + ri |= (((var_int_t)1) << i); } } break; @@ -501,11 +504,15 @@ static inline void oper_log(var_t *r, var_t *left, byte op) { a = li; b = ri; ri = 0; - for (i = 0; i < sizeof(var_int_t); i++) { - ba = a & (1 << i); - bb = b & (1 << i); - if (!(ba && !bb)) { - ri |= (1 << i); + set = 0; + for (i = (sizeof(var_int_t) * 8) - 1; i >= 0; i--) { + int ba = ((a >> i) & 1); + int bb = ((b >> i) & 1); + if (ba || bb) { + set = 1; + } + if (set && (!ba || bb)) { + ri |= (((var_int_t)1) << i); } } break; From 6f78b12515b31b0cfd5bc5e64edfa9e3c39816f1 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 25 Mar 2016 22:21:38 +1000 Subject: [PATCH 11/29] COMMON: fix map bool --- src/common/var_map.c | 4 ++-- src/platform/sdl/display.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/var_map.c b/src/common/var_map.c index 39e79683..e5293d48 100644 --- a/src/common/var_map.c +++ b/src/common/var_map.c @@ -209,10 +209,10 @@ int map_get_bool(var_p_t base, const char *name) { if (var != NULL) { switch (var->type) { case V_INT: - result = (var->v.i == -1); + result = (var->v.i != 0); break; case V_NUM: - result = (var->v.n == -1); + result = (var->v.n != 0); break; case V_STR: result = (strncasecmp(var->v.p.ptr, "true", 4)); diff --git a/src/platform/sdl/display.cpp b/src/platform/sdl/display.cpp index 0375103a..1f74a057 100644 --- a/src/platform/sdl/display.cpp +++ b/src/platform/sdl/display.cpp @@ -139,6 +139,8 @@ bool Graphics::construct(const char *font, const char *boldFont) { _drawTarget = _screen; maSetColor(DEFAULT_BACKGROUND); } + } else { + result = false; } return result; } From 10d6261ad6c30fe92c52591a877ca4d37ed9cede Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 26 Mar 2016 07:41:17 +1000 Subject: [PATCH 12/29] COMMON: bump version --- configure.ac | 4 ++-- debian/changelog | 5 +++++ ide/android/assets/main.bas | 2 +- src/ui/form.cpp | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index f38d401f..f47bfb56 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [0.12.5]) +AC_INIT([smallbasic], [0.12.6]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET @@ -364,7 +364,7 @@ checkDebugMode checkForWindows PACKAGE_CFLAGS="${PACKAGE_CFLAGS} -Wall -Wno-unused-result" -BUILD_DATE=`date -R` +BUILD_DATE=`date +"%a, %d %b %Y"` AC_DEFINE_UNQUOTED([BUILD_DATE],["$BUILD_DATE"],[Build date]) AC_SUBST(PACKAGE_CFLAGS) diff --git a/debian/changelog b/debian/changelog index 48e51c12..c1f8b11f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +smallbasic (0.12.6) unstable; urgency=low + * Various - see web site + + -- Chris Warren-Smith Sat, 26 Mar 2016 09:45:25 +1000 + smallbasic (0.12.3) unstable; urgency=low * Various - see web site diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index f06c2d4f..84e0fd42 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -71,7 +71,7 @@ sub do_about() print "(_ ._ _ _.|||_) /\ (_ |/ " print "__)| | |(_||||_)/--\__)|\_" print - print "Version 0.12.6" + print "Version "; sbver print print "Copyright (c) 2002-2015 Chris Warren-Smith" print "Copyright (c) 1999-2006 Nic Christopoulos" + chr(10) diff --git a/src/ui/form.cpp b/src/ui/form.cpp index d5d2686e..e2a56c2d 100644 --- a/src/ui/form.cpp +++ b/src/ui/form.cpp @@ -144,7 +144,8 @@ void cmd_form_do_events(var_s *self) { focusInput->edit(event.key, sw, charWidth)) { dev_clrkb(); out->setDirty(); - } else if (event.key == SB_KEY_MK_PUSH || event.key == SB_KEY_MK_RELEASE) { + } else if (event.key == SB_KEY_MK_PUSH || event.key == SB_KEY_MK_RELEASE || + SB_KEY_CTRL(SB_KEY_UP) || SB_KEY_CTRL(SB_KEY_DOWN)) { // no exit on mouse events dev_clrkb(); } else { From 18af421601e9abdd0cd4e7a31f9a44a5e3ff44f6 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 27 Mar 2016 09:42:07 +1000 Subject: [PATCH 13/29] COMMON: update jsmn --- src/common/var_map.c | 2 +- src/lib/jsmn.c | 67 +++++++++++++++++++++++++++++++------------- src/lib/jsmn.h | 15 ++++++---- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/src/common/var_map.c b/src/common/var_map.c index e5293d48..70d9e923 100644 --- a/src/common/var_map.c +++ b/src/common/var_map.c @@ -710,7 +710,7 @@ void map_from_str(var_p_t dest) { jsmntok_t *tokens = malloc(sizeof(jsmntok_t) * num_tokens); const char *js = arg.v.p.ptr; size_t len = arg.v.p.size; - jsmnerr_t result; + int result; jsmn_parser parser; jsmn_init(&parser); diff --git a/src/lib/jsmn.c b/src/lib/jsmn.c index 56381396..e7765eb1 100644 --- a/src/lib/jsmn.c +++ b/src/lib/jsmn.c @@ -1,11 +1,9 @@ -#include - #include "jsmn.h" /** * Allocates a fresh unused token from the token pull. */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *tok; if (parser->toknext >= num_tokens) { @@ -23,7 +21,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, /** * Fills token type and boundaries. */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end) { token->type = type; token->start = start; @@ -34,7 +32,7 @@ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, /** * Fills next available token with JSON primitive. */ -static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; int start; @@ -81,9 +79,9 @@ static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, } /** - * Filsl next token with JSON string. + * Fills next token with JSON string. */ -static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, +static int jsmn_parse_string(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; @@ -113,7 +111,8 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, } /* Backslash: Quoted symbol expected */ - if (c == '\\') { + if (c == '\\' && parser->pos + 1 < len) { + int i; parser->pos++; switch (js[parser->pos]) { /* Allowed escaped symbols */ @@ -123,8 +122,7 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, /* Allows escaped symbol \uXXXX */ case 'u': parser->pos++; - int i = 0; - for(; i < 4 && js[parser->pos] != '\0'; i++) { + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { /* If it isn't a hex character we have an error */ if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ @@ -150,12 +148,12 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, /** * Parse JSON string and fill tokens. */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens) { - jsmnerr_t r; + int r; int i; jsmntok_t *token; - int count = 0; + int count = parser->toknext; for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { char c; @@ -234,13 +232,42 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++; break; - case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } break; #ifdef JSMN_STRICT /* In strict mode primitives are: numbers and booleans */ case '-': case '0': case '1' : case '2': case '3' : case '4': case '5': case '6': case '7' : case '8': case '9': case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } #else /* In non-strict mode every unquoted value is a primitive */ default: @@ -260,10 +287,12 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, } } - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } } } @@ -271,7 +300,7 @@ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, } /** - * Creates a new parser based over a given buffer with an array of tokens + * Creates a new parser based over a given buffer with an array of tokens * available. */ void jsmn_init(jsmn_parser *parser) { diff --git a/src/lib/jsmn.h b/src/lib/jsmn.h index c8f388cd..01ca99c8 100644 --- a/src/lib/jsmn.h +++ b/src/lib/jsmn.h @@ -1,6 +1,8 @@ #ifndef __JSMN_H_ #define __JSMN_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -13,20 +15,21 @@ extern "C" { * o Other primitive: number, boolean (true/false) or null */ typedef enum { - JSMN_PRIMITIVE = 0, + JSMN_UNDEFINED = 0, JSMN_OBJECT = 1, JSMN_ARRAY = 2, - JSMN_STRING = 3 + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 } jsmntype_t; -typedef enum { +enum jsmnerr { /* Not enough tokens were provided */ JSMN_ERROR_NOMEM = -1, /* Invalid character inside JSON string */ JSMN_ERROR_INVAL = -2, /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3, -} jsmnerr_t; + JSMN_ERROR_PART = -3 +}; /** * JSON token description. @@ -63,7 +66,7 @@ void jsmn_init(jsmn_parser *parser); * Run JSON parser. It parses a JSON data string into and array of tokens, each describing * a single JSON object. */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens); #ifdef __cplusplus From 2a87e8ad870b48f839f659d162eba828726c8047 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 27 Mar 2016 13:14:01 +1000 Subject: [PATCH 14/29] COMMON: fix inkey handling --- src/common/blib_func.c | 25 +++++++++++++------- src/common/device.h | 2 +- src/common/keymap.c | 18 +++++++------- src/common/keymap.h | 5 ++-- src/platform/android/jni/runtime.cpp | 1 - src/platform/sdl/runtime.cpp | 4 +++- src/ui/system.cpp | 35 ++++------------------------ src/ui/system.h | 4 ---- 8 files changed, 35 insertions(+), 59 deletions(-) diff --git a/src/common/blib_func.c b/src/common/blib_func.c index fcccc8f7..3b1b67e8 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -1179,7 +1179,7 @@ void cmd_str1(long funcCode, var_t *arg, var_t *r) { // str <- FUNC (void) // void cmd_str0(long funcCode, var_t *r) { - word ch; + dword ch; char tmp[3]; struct tm tms; time_t now; @@ -1191,16 +1191,27 @@ void cmd_str0(long funcCode, var_t *r) { // str <- INKEY$ // if (!dev_kbhit()) { - dev_events(2); + dev_events(1); } if (dev_kbhit()) { ch = dev_getch(); - // MultiByte - dev_getchr() must return the extended code (2 bytes char) if ((ch & 0xFF00) == 0xFF00) { - // hardware keys + // keypad or mouse keys tmp[0] = '\033'; tmp[1] = ch & 0xFF; tmp[2] = '\0'; + } else if ((ch & SB_KEY_CTRL_ALT(0)) == SB_KEY_CTRL_ALT(0)) { + tmp[0] = '\4'; + tmp[1] = ch & 0xFF; + tmp[2] = '\0'; + } else if ((ch & SB_KEY_ALT_SHIFT(0)) == SB_KEY_ALT_SHIFT(0)) { + tmp[0] = '\5'; + tmp[1] = ch & 0xFF; + tmp[2] = '\0'; + } else if ((ch & SB_KEY_SHIFT_CTRL(0)) == SB_KEY_SHIFT_CTRL(0)) { + tmp[0] = '\6'; + tmp[1] = ch & 0xFF; + tmp[2] = '\0'; } else if ((ch & SB_KEY_CTRL(0)) == SB_KEY_CTRL(0)) { tmp[0] = '\1'; tmp[1] = ch & 0xFF; @@ -1209,7 +1220,7 @@ void cmd_str0(long funcCode, var_t *r) { tmp[0] = '\2'; tmp[1] = ch & 0xFF; tmp[2] = '\0'; - } else if ((ch & SB_KEY_CTRL_ALT(0)) == SB_KEY_CTRL_ALT(0)) { + } else if ((ch & SB_KEY_SHIFT(0)) == SB_KEY_SHIFT(0)) { tmp[0] = '\3'; tmp[1] = ch & 0xFF; tmp[2] = '\0'; @@ -1225,7 +1236,6 @@ void cmd_str0(long funcCode, var_t *r) { tmp[1] = '\0'; } break; - case enc_big5: // China if (IsBig5Font(ch)) { tmp[0] = ch >> 8; @@ -1236,7 +1246,6 @@ void cmd_str0(long funcCode, var_t *r) { tmp[1] = '\0'; } break; - case enc_gmb: // Generic multibyte if (IsGMBFont(ch)) { tmp[0] = ch >> 8; @@ -1247,13 +1256,11 @@ void cmd_str0(long funcCode, var_t *r) { tmp[1] = '\0'; } break; - case enc_unicode: // Unicode tmp[0] = ch >> 8; tmp[1] = ch & 0xFF; tmp[2] = '\0'; break; - default: // Europe 8bit tmp[0] = ch; tmp[1] = '\0'; diff --git a/src/common/device.h b/src/common/device.h index 89379f4f..d7de2f0a 100644 --- a/src/common/device.h +++ b/src/common/device.h @@ -197,7 +197,7 @@ int dev_restore(void); * * @param ch the key-code */ -void dev_pushkey(word ch); +void dev_pushkey(dword ch); /** * @ingroup dev_i diff --git a/src/common/keymap.c b/src/common/keymap.c index 4a45e2c1..069af077 100644 --- a/src/common/keymap.c +++ b/src/common/keymap.c @@ -15,7 +15,7 @@ #include "common/keymap.h" // Keyboard buffer -word keybuff[PCKBSIZE]; +dword keybuff[PCKBSIZE]; int keyhead; int keytail; @@ -60,7 +60,7 @@ void keymap_free() { /** * DEFINEKEY command handler to add a keymap */ -void keymap_add(int key, bcip_t ip) { +void keymap_add(dword key, bcip_t ip) { key_map_s* km = (key_map_s*) malloc(sizeof (key_map_s)); km->next = 0; km->ip = ip; @@ -81,7 +81,7 @@ void keymap_add(int key, bcip_t ip) { /** * invokes the handler for the given key */ -int keymap_invoke(word key) { +int keymap_invoke(dword key) { int result = 0; key_map_s* head = keymap; while (head) { @@ -121,13 +121,11 @@ void dev_clrkb() { /** * stores a key in keyboard buffer */ -void dev_pushkey(word key) { - if (key <= SB_KEY_MK_LAST) { - keybuff[keytail] = key; - keytail++; - if (keytail >= PCKBSIZE) { - keytail = 0; - } +void dev_pushkey(dword key) { + keybuff[keytail] = key; + keytail++; + if (keytail >= PCKBSIZE) { + keytail = 0; } keymap_invoke(key); diff --git a/src/common/keymap.h b/src/common/keymap.h index 222456fb..d15f6d1a 100644 --- a/src/common/keymap.h +++ b/src/common/keymap.h @@ -56,6 +56,7 @@ extern "C" { #define SB_KEY_CTRL_ALT(c) (0xF3000000 + (c)) #define SB_KEY_SHIFT(c) (0xF4000000 + (c)) #define SB_KEY_SHIFT_CTRL(c) (0xF5000000 + (c)) +#define SB_KEY_ALT_SHIFT(c) (0xF6000000 + (c)) // keypad #define SB_KEY_KP_DIV 0xFFDA @@ -85,8 +86,8 @@ extern "C" { void keymap_init(); void keymap_free(); -void keymap_add(int key, bcip_t ip); -int keymap_invoke(word key); +void keymap_add(dword key, bcip_t ip); +int keymap_invoke(dword key); int keymap_kbhit(); int keymap_kbpeek(); diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index ba294bf1..9aacad8a 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -632,7 +632,6 @@ MAEvent Runtime::processEvents(int waitFlag) { break; default: pollEvents(false); - checkLoadError(); } MAEvent event; diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index fd7d82c2..030a71e9 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -362,6 +362,9 @@ void Runtime::handleKeyEvent(MAEvent &event) { } else if ((event.nativeKey & KMOD_CTRL) && (event.nativeKey & KMOD_SHIFT)) { event.key = SB_KEY_SHIFT_CTRL(event.key); + } else if ((event.nativeKey & KMOD_ALT) && + (event.nativeKey & KMOD_SHIFT)) { + event.key = SB_KEY_ALT_SHIFT(event.key); } else if (event.nativeKey & KMOD_CTRL) { event.key = SB_KEY_CTRL(event.key); } else if (event.nativeKey & KMOD_ALT) { @@ -548,7 +551,6 @@ MAEvent Runtime::processEvents(int waitFlag) { break; default: pollEvents(false); - checkLoadError(); } MAEvent event; diff --git a/src/ui/system.cpp b/src/ui/system.cpp index e498ddb6..03825b3c 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -53,8 +53,6 @@ #define FONT_SCALE_INTERVAL 10 #define FONT_MIN 20 #define FONT_MAX 200 -#define EVENT_CHECK_EVERY 2000 -#define EVENT_MAX_BURN_TIME 30 System *g_system; @@ -75,15 +73,12 @@ System::System() : _output(NULL), _state(kInitState), _cache(MAX_CACHE), - _lastEventTime(0), - _eventTicks(0), _touchX(-1), _touchY(-1), _touchCurX(-1), _touchCurY(-1), _initialFontSize(0), _fontScale(100), - _overruns(0), _userScreenId(-1), _systemMenu(NULL), _mainBas(false), @@ -591,9 +586,6 @@ void System::runMain(const char *mainBasPath) { bool success = execute(_loadPath); bool networkFile = (_loadPath.indexOf("://", 1) != -1); - if (!isClosing() && _overruns) { - systemPrint("\nOverruns: %d\n", _overruns); - } if (!isBack() && !isClosing() && (opt_ide != IDE_INTERNAL || success || networkFile)) { // when editing, only pause here when successful, otherwise the editor shows @@ -750,9 +742,6 @@ void System::setRunning(bool running) { _loadPath.indexOf("://", 1) != -1) { _loadPath.empty(); } - _lastEventTime = maGetMilliSecondCount(); - _eventTicks = 0; - _overruns = 0; _userScreenId = -1; } else if (!isClosing() && !isRestart() && !isBack()) { _state = kActiveState; @@ -771,20 +760,6 @@ void System::showCompletion(bool success) { _output->flush(true); } -// detect when we have been called too frequently -void System::checkLoadError() { - int now = maGetMilliSecondCount(); - _eventTicks++; - if (now - _lastEventTime >= EVENT_CHECK_EVERY) { - // next time inspection interval - if (_eventTicks >= EVENT_MAX_BURN_TIME) { - _overruns++; - } - _lastEventTime = now; - _eventTicks = 0; - } -} - void System::showMenu() { logEntered(); @@ -856,12 +831,10 @@ void System::showMenu() { } #endif } else { - if (_overruns == 0) { - items->add(new String("Console")); - items->add(new String("View source")); - _systemMenu[index++] = MENU_CONSOLE; - _systemMenu[index++] = MENU_SOURCE; - } + items->add(new String("Console")); + items->add(new String("View source")); + _systemMenu[index++] = MENU_CONSOLE; + _systemMenu[index++] = MENU_SOURCE; if (!isEditing()) { items->add(new String("Restart")); _systemMenu[index++] = MENU_RESTART; diff --git a/src/ui/system.h b/src/ui/system.h index 6b9d71e6..13203e2a 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -86,7 +86,6 @@ struct System { bool setParentPath(); void setDimensions(); void showCompletion(bool success); - void checkLoadError(); void printErrorLine(); void printSource(); void printSourceLine(char *text, int line, bool last); @@ -118,15 +117,12 @@ struct System { strlib::String _loadPath; strlib::String _activeFile; Cache _cache; - int _lastEventTime; - int _eventTicks; int _touchX; int _touchY; int _touchCurX; int _touchCurY; int _initialFontSize; int _fontScale; - int _overruns; int _userScreenId; int *_systemMenu; bool _mainBas; From f82026957c35ff2329004ab60a17ab58ed58290c Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 27 Mar 2016 13:42:43 +1000 Subject: [PATCH 15/29] UI: fix file manager --- ChangeLog | 9 +++++++-- ide/android/assets/main.bas | 23 ++++++++++++++++++----- src/ui/form.cpp | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index 27c2f6b0..87817a10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,13 +10,18 @@ Editor now restores cursor when returning from run Editor now displays the correct filename in the title bar Added "industrial" editor theme from Shian (slot 2) + Fixed editor highlighting Runtime errors now show source screen with red error highlighter Form refresh command now takes an boolean arg, true=push ui state to vars Added window.setFont command to set font size, bold and italic. example: w = window():w.setFont(10, "pt", false, true) - Fix problem with escaped chars using FORMAT - Fix problem with XNOR command result + Fixed problem with escaped chars using FORMAT + Fixed problem with XNOR command result TRUE is now always returned as 1 + Fixed problem with IMP and EQV command result + Fixed problem with INKEY command + Fixed capslock handling + Added file manager to main shell program 2016-02-11 Added export to mobile command (SDL) diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index 84e0fd42..9b5260f8 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -203,9 +203,10 @@ func getFiles() getFiles = result end -sub createNewFile(byref f, byref wnd) +sub createNewFile(byref f, byref wnd, byref selectedFile) f.refresh(true) local newFile = f.inputs(idxEdit).value + if (len(newFile) == 0) then exit sub endIf @@ -220,6 +221,19 @@ sub createNewFile(byref f, byref wnd) text << "REM SmallBASIC" text << "REM created: " + date tsave newFile, text + + local f_list = getFiles() + local f_list_len=len(f_list) - 1 + local i + for i = 0 to f_list_len + if (f_list(i) == newFile) then + f.inputs(idxFiles).selectedIndex = i + exit for + endif + next i + f.inputs(idxFiles).value = f_list + f.refresh(false) + selectedFile = newfile endif catch e wnd.alert("Error creating file: " + e) @@ -227,7 +241,7 @@ sub createNewFile(byref f, byref wnd) end sub manageFiles() - local f, wnd, bn_edit, bn_files + local f, wnd, bn_edit, bn_files, selectedFile const renameId = "__bn_rename__" const deleteId = "__bn_delete__" const newId = "__bn_new__" @@ -327,7 +341,7 @@ sub manageFiles() tload selectedFile, buffer wnd.graphicsScreen2() cls - color 2,0 + color 7,0 len_buffer = len(buffer) - 1 for i = 0 to len_buffer print buffer(i) @@ -350,8 +364,7 @@ sub manageFiles() case deleteId deleteFile() case newId - createNewFile(f, wnd) - reloadList(0) + createNewFile(f, wnd, selectedFile) case viewId viewFile() case closeId diff --git a/src/ui/form.cpp b/src/ui/form.cpp index e2a56c2d..7bd5923e 100644 --- a/src/ui/form.cpp +++ b/src/ui/form.cpp @@ -110,7 +110,7 @@ void cmd_form_refresh(var_s *self) { var_t arg; v_init(&arg); eval(&arg); - bool setVars = v_getint(&arg) == -1; + bool setVars = v_getint(&arg) != 0; v_free(&arg); g_system->getOutput()->updateInputs(self, setVars); } From d5b87a5e80ef7b94b2c9499021ae84a2229bb91c Mon Sep 17 00:00:00 2001 From: chrisws Date: Tue, 29 Mar 2016 06:57:57 +1000 Subject: [PATCH 16/29] COMMON: fix try-catch --- ide/android/assets/main.bas | 75 ++++++++++++++++++------------------ src/common/blib.c | 29 ++++++++------ src/common/blib.h | 1 + src/common/brun.c | 3 +- src/common/sberr.c | 77 ++++++++++++++++++++++++------------- src/common/scan.c | 46 ++++------------------ src/common/smbas.h | 1 - src/common/tasks.h | 1 - src/common/var.h | 7 ++++ 9 files changed, 122 insertions(+), 118 deletions(-) diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index 9b5260f8..68e9f647 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -203,43 +203,6 @@ func getFiles() getFiles = result end -sub createNewFile(byref f, byref wnd, byref selectedFile) - f.refresh(true) - local newFile = f.inputs(idxEdit).value - - if (len(newFile) == 0) then - exit sub - endIf - if (lower(right(newFile, 4)) != ".bas") then - newFile += ".bas" - endIf - try - if (exist(newFile)) then - wnd.alert("File " + newFile + " already exists", "Duplicate File") - else - dim text - text << "REM SmallBASIC" - text << "REM created: " + date - tsave newFile, text - - local f_list = getFiles() - local f_list_len=len(f_list) - 1 - local i - for i = 0 to f_list_len - if (f_list(i) == newFile) then - f.inputs(idxFiles).selectedIndex = i - exit for - endif - next i - f.inputs(idxFiles).value = f_list - f.refresh(false) - selectedFile = newfile - endif - catch e - wnd.alert("Error creating file: " + e) - end try -end - sub manageFiles() local f, wnd, bn_edit, bn_files, selectedFile const renameId = "__bn_rename__" @@ -352,6 +315,42 @@ sub manageFiles() endIf end + sub createNewFile() + f.refresh(true) + local newFile = f.inputs(idxEdit).value + + if (len(newFile) == 0) then + exit sub + endIf + if (lower(right(newFile, 4)) != ".bas") then + newFile += ".bas" + endIf + try + if (exist(newFile)) then + wnd.alert("File " + newFile + " already exists", "Duplicate File") + else + dim text + text << "REM SmallBASIC" + text << "REM created: " + date + tsave newFile, text + local f_list = getFiles() + local f_list_len=len(f_list) - 1 + local i + for i = 0 to f_list_len + if (f_list(i) == newFile) then + f.inputs(idxFiles).selectedIndex = i + exit for + endif + next i + f.inputs(idxFiles).value = f_list + f.refresh(false) + selectedFile = newfile + endif + catch e + wnd.alert("Error creating file: " + e) + end try + end + createUI() reloadList(0) wnd = window() @@ -364,7 +363,7 @@ sub manageFiles() case deleteId deleteFile() case newId - createNewFile(f, wnd, selectedFile) + createNewFile() case viewId viewFile() case closeId diff --git a/src/common/blib.c b/src/common/blib.c index bb3a107e..9d4b0a1d 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -2747,21 +2747,28 @@ void cmd_definekey(void) { v_free(&var); } +/** + * Try handler for try/catch + */ +void cmd_try() { + stknode_t node; + node.x.vtry.catch_ip = code_getaddr(); + node.type = kwTRY; + code_push(&node); +} + /** * Handler for unthrown/uncaught exceptions */ void cmd_catch() { - bcip_t end_try_ip = code_getaddr(); - bcip_t outer_catch_ip = code_getaddr(); - - // skip level code - prog_ip += 1; - - // skip to end-try - code_jump(end_try_ip); - - // restore outer try/catch level - prog_catch_ip = outer_catch_ip; + stknode_t node; + code_pop_and_free(&node); + if (node.type != kwTRY) { + err_stackmess(); + } else { + // skip to end-try + code_jump(code_getaddr()); + } } /** diff --git a/src/common/blib.h b/src/common/blib.h index 95ccdc8b..e0d33522 100644 --- a/src/common/blib.h +++ b/src/common/blib.h @@ -72,6 +72,7 @@ void cmd_poke16(void); void cmd_poke32(void); void cmd_bcopy(void); void cmd_calladr(void); +void cmd_try(); void cmd_catch(); void cmd_call_vfunc(); void cmd_timer(); diff --git a/src/common/brun.c b/src/common/brun.c index daf784b8..98ec031c 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -1046,7 +1046,7 @@ void bc_loop(int isf) { cmd_run(0); break; case kwTRY: - prog_catch_ip = code_getaddr(); + cmd_try(); IF_ERR_BREAK; continue; case kwCATCH: @@ -1268,7 +1268,6 @@ int brun_create_task(const char *filename, byte *preloaded_bc, int libf) { prog_stack_alloc = SB_EXEC_STACK_SIZE; prog_stack = malloc(sizeof(stknode_t) * prog_stack_alloc); prog_stack_count = 0; - prog_catch_ip = INVALID_ADDR; prog_timer = NULL; // create eval's stack diff --git a/src/common/sberr.c b/src/common/sberr.c index a9df4ce1..a391f02c 100644 --- a/src/common/sberr.c +++ b/src/common/sberr.c @@ -325,18 +325,7 @@ void err_form_input() { * the DONE message */ void inf_done() { -#if USE_TERM_IO - if (!isatty(STDOUT_FILENO)) { - fprintf(stdout, "\n* %s *\n", WORD_DONE); - } - else { -#endif - dev_printf("\n\033[0m\033[80m\033[7m * %s * \033[0m\n", WORD_DONE); - -#if USE_TERM_IO -} -#endif } // the BREAK message @@ -355,7 +344,6 @@ int err_throw_catch(const char *err) { var_t *arg; var_t v_catch; int caught = 1; - switch (code_peek()) { case kwTYPE_VAR: arg = code_getvarptr(); @@ -378,31 +366,68 @@ int err_throw_catch(const char *err) { return caught; } +// returns the position for the most TRY in the stack +int err_find_try(int position) { + int result = -1; + while (position) { + if (prog_stack[--position].type == kwTRY) { + result = position; + break; + } + } + return result; +} + // throw string void err_throw_str(const char *err) { int caught = 0; - if (!prog_error && prog_catch_ip != INVALID_ADDR && prog_catch_ip > prog_ip) { + int throw_sp = prog_stack_count; + int try_sp = err_find_try(throw_sp); + int reset_sp; + + if (!prog_error && try_sp != -1 && prog_stack[try_sp].x.vtry.catch_ip > prog_ip) { + bcip_t catch_ip = prog_stack[try_sp].x.vtry.catch_ip; // position after kwCATCH - code_jump(prog_catch_ip + 1); + code_jump(catch_ip + 1); + // skip "end try" address code_getaddr(); - // restore outer level - prog_catch_ip = code_getaddr(); - // get the stack level - byte level = code_getnext(); + + // fetch next catch in the current block + catch_ip = code_getaddr(); caught = err_throw_catch(err); - while (!caught && prog_catch_ip != INVALID_ADDR) { - code_jump(prog_catch_ip + 1); - code_getaddr(); - prog_catch_ip = code_getaddr(); - level = code_getnext(); - caught = err_throw_catch(err); + reset_sp = try_sp; + + while (!caught && catch_ip != INVALID_ADDR) { + // find in the current block + while (!caught && catch_ip != INVALID_ADDR) { + code_jump(catch_ip + 1); + code_getaddr(); + catch_ip = code_getaddr(); + caught = err_throw_catch(err); + } + // find in the next outer block + if (!caught && try_sp != -1) { + try_sp = err_find_try(try_sp); + if (try_sp != -1) { + reset_sp = try_sp; + catch_ip = prog_stack[try_sp].x.vtry.catch_ip; + code_jump(catch_ip + 1); + code_getaddr(); + catch_ip = code_getaddr(); + caught = err_throw_catch(err); + } + } } // cleanup the stack - while (prog_stack_count > level) { - code_pop_and_free(NULL); + stknode_t node; + for (int sp = throw_sp; sp > reset_sp; sp--) { + code_pop_and_free(&node); + } + if (node.type != kwTRY) { + err_stackmess(); } } if (!caught) { diff --git a/src/common/scan.c b/src/common/scan.c index 4d4270dc..bf3b71da 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -2446,7 +2446,6 @@ int comp_text_line_command(long idx, int decl, int sharp, char *last_cmd) { case kwCATCH: comp_push(comp_prog.count); bc_add_ctrl(&comp_prog, idx, 0, 0); - bc_add_code(&comp_prog, comp_block_level); comp_expression(comp_bc_parm, 0); break; @@ -2783,10 +2782,8 @@ bcip_t comp_next_bc_cmd(bcip_t ip) { case kwCASE: case kwCASE_ELSE: case kwENDSELECT: - ip += BC_CTRLSZ; - break; case kwCATCH: - ip += BC_CTRLSZ + 1; + ip += BC_CTRLSZ; break; case kwTYPE_EVAL_SC: ip += 2; // kwTYPE_LOGOPR+op @@ -2869,34 +2866,6 @@ bcip_t comp_search_bc_stack_backward(bcip_t start, code_t code, byte level, bid_ return INVALID_ADDR; } -/* - * search stack for the next inner catch - */ -bcip_t comp_search_inner_catch(bcip_t start, byte level) { - bcip_t result = INVALID_ADDR; - if (level > 1) { - byte nextLevel = level - 1; - do { - bcip_t i; - comp_pass_node_t *node; - for (i = start; i < comp_sp; i++) { - node = comp_stack.elem[i]; - if (node->level == nextLevel) { - if (comp_prog.ptr[node->pos] == kwTRY) { - // next CATCH has own block - break; - } else if (comp_prog.ptr[node->pos] == kwCATCH) { - result = node->pos; - break; - } - } - } - --nextLevel; - } while (result == INVALID_ADDR && nextLevel > 0); - } - return result; -} - /* * inspect the byte-code at the given location */ @@ -3383,17 +3352,16 @@ void comp_pass2_scan() { print_pass2_stack(i, kwENDTRY, node->level); return; } + // address of the end-try memcpy(comp_prog.ptr + node->pos + 1, &true_ip, ADDRSZ); + // address of the next catch in the same block false_ip = comp_search_bc_stack(i + 1, kwCATCH, node->level, node->block_id); - if (false_ip != INVALID_ADDR && false_ip < true_ip) { - // another catch in the same block - memcpy(comp_prog.ptr + node->pos + (ADDRSZ + 1), &false_ip, ADDRSZ); - } else { - // store the address of the next outer catch (or store INVALID_ADDR if not found) - false_ip = comp_search_inner_catch(i + 1, node->level); - memcpy(comp_prog.ptr + node->pos + (ADDRSZ + 1), &false_ip, ADDRSZ); + if (false_ip > true_ip) { + // not valid if found after end-try + false_ip = INVALID_ADDR; } + memcpy(comp_prog.ptr + node->pos + (ADDRSZ + 1), &false_ip, ADDRSZ); break; }; } diff --git a/src/common/smbas.h b/src/common/smbas.h index aaf92cec..ef9ce684 100644 --- a/src/common/smbas.h +++ b/src/common/smbas.h @@ -174,7 +174,6 @@ EXTERN char gsb_last_errmsg[SB_ERRMSG_SIZE + 1]; /**< last error message */ #define prog_symtable ctask->sbe.exec.symtable #define prog_exptable ctask->sbe.exec.exptable #define prog_uds_tab_ip ctask->sbe.exec.uds_tab_ip -#define prog_catch_ip ctask->sbe.exec.catch_ip #define prog_timer ctask->sbe.exec.timer #define comp_extfunctable ctask->sbe.comp.extfunctable #define comp_extfunccount ctask->sbe.comp.extfunccount diff --git a/src/common/tasks.h b/src/common/tasks.h index a25a9f00..66f8c498 100644 --- a/src/common/tasks.h +++ b/src/common/tasks.h @@ -148,7 +148,6 @@ typedef struct { bc_lib_rec_t *libtable; /**< import-libraries table */ bc_symbol_rec_t *symtable; /**< import-symbols table */ unit_sym_t *exptable; /**< export-symbols table */ - bcip_t catch_ip; /** try/catch ip */ timer_s *timer; /** timer linked list */ } exec; } sbe; diff --git a/src/common/var.h b/src/common/var.h index 1a7a0725..88229241 100644 --- a/src/common/var.h +++ b/src/common/var.h @@ -209,6 +209,13 @@ struct stknode_s { word vcheck; /**< checks (1=BYVAL ONLY, 3=BYVAL|BYREF, 2=BYREF ONLY) */ var_t *res; /**< variable pointer (for BYVAL this is a clone) */ } param; + + /** + * try/catch + */ + struct { + bcip_t catch_ip; + } vtry; } x; }; typedef struct stknode_s stknode_t; From 015587910d871dddb4a8c85372a00fabf9205f45 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Tue, 29 Mar 2016 20:14:12 +1000 Subject: [PATCH 17/29] COMMON: fix try-catch --- ChangeLog | 3 +- samples/distro-examples/tests/trycatch.bas | 43 ++++++++++++++++++++++ src/common/blib.c | 16 +------- src/common/blib_func.c | 10 ++--- src/common/pproc.h | 10 +++++ src/common/proc.c | 17 +++++++++ src/common/sberr.c | 4 +- 7 files changed, 81 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index 87817a10..4c9e3d90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2016-02-18 +2016-03-29 (0.12.6) Removed TICKSPERSEC Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON Removed system constants BPP and VIDADR, OSNAME, LINECHART, BARCHART @@ -22,6 +22,7 @@ Fixed problem with INKEY command Fixed capslock handling Added file manager to main shell program + Fixed issues with TRY/CATCH 2016-02-11 Added export to mobile command (SDL) diff --git a/samples/distro-examples/tests/trycatch.bas b/samples/distro-examples/tests/trycatch.bas index dcad8423..cb34155e 100644 --- a/samples/distro-examples/tests/trycatch.bas +++ b/samples/distro-examples/tests/trycatch.bas @@ -88,3 +88,46 @@ try catch "okay" REM okay end try + +sub s1(err) + try + s2(err) + catch "err1" + ? "err1" + catch "err2" + ? "err2" + catch "err3" + ? "err3" + end try +end +sub s2(err) + try + s3(err) + catch "err4" + ? "err4" + catch "err5" + ? "err5" + catch "err6" + ? "err6" + end try +end +sub s3(err) + if 1==1 then + if 1==1 then + if 1==1 then + throw err + endif + endif + endif +end + +caughtError = FALSE +try + s1("some string") +catch "some" + caughtError = TRUE +end try + +if (!caughtError) then + throw "Error not caught!!!" +endif diff --git a/src/common/blib.c b/src/common/blib.c index 9d4b0a1d..4fc8ac12 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -2009,22 +2009,10 @@ void cmd_locate() { * PAUSE [secs] */ void cmd_pause() { - int x = 0, evc; + int evc; long start, now; - byte code = code_peek(); - if (code == kwTYPE_VAR) { - x = par_getint(); - if (prog_error) { - return; - } - } else if (code == kwTYPE_INT) { - code_skipnext(); - x = code_getint(); - } else if (code == kwTYPE_NUM) { - code_skipnext(); - x = code_getreal(); - } + var_int_t x = par_getval(0); if (x == 0) { while (dev_kbhit() == 0) { switch (dev_events(2)) { diff --git a/src/common/blib_func.c b/src/common/blib_func.c index 3b1b67e8..0380cd9e 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -82,7 +82,7 @@ void cmd_pen() { /* * ARRAY ROUTINES - First element - * funcCode is the function code, r is the return value of the + * funcCode is the function code, r is the return value of the * function, elem_p is the element */ void dar_first(long funcCode, var_t *r, var_t *elem_p) { @@ -114,7 +114,7 @@ void dar_first(long funcCode, var_t *r, var_t *elem_p) { /* * ARRAY ROUTINES - Next (each) element - * funcCode is the function code, r is the return value of + * funcCode is the function code, r is the return value of * the function, elem_p is the current element */ void dar_next(long funcCode, var_t *r, var_t *elem_p) { @@ -160,7 +160,7 @@ void dar_next(long funcCode, var_t *r, var_t *elem_p) { /* * ARRAY ROUTINES - Last element - * funcCode is the function code, r is the return value of + * funcCode is the function code, r is the return value of * the function, elem_p is the element */ void dar_final(long funcCode, var_t *r, int count) { @@ -689,7 +689,7 @@ var_int_t cmd_fre(var_int_t arg) { while (n != 0 && ch == ' ') { n = read(memfd, &ch, sizeof(ch)); } - + // read the value long long val = 0; while (n != 0 && isdigit(ch)) { @@ -1191,7 +1191,7 @@ void cmd_str0(long funcCode, var_t *r) { // str <- INKEY$ // if (!dev_kbhit()) { - dev_events(1); + dev_events(par_getval(2)); } if (dev_kbhit()) { ch = dev_getch(); diff --git a/src/common/pproc.h b/src/common/pproc.h index d89f6264..5a33168a 100644 --- a/src/common/pproc.h +++ b/src/common/pproc.h @@ -196,6 +196,16 @@ var_num_t par_getnum(void); #define par_getreal() par_getnum() +/** + * @ingroup par + * + * get next parameter as integer + * moves IP to the next position. + * + * @return the integer + */ +var_int_t par_getval(var_int_t def); + /** * @ingroup par * diff --git a/src/common/proc.c b/src/common/proc.c index 68092225..11f7fdd9 100644 --- a/src/common/proc.c +++ b/src/common/proc.c @@ -503,6 +503,23 @@ var_num_t par_getnum() { return f; } +var_int_t par_getval(var_int_t def) { + var_int_t result; + byte code = code_peek(); + if (code == kwTYPE_VAR) { + result = par_getint(); + } else if (code == kwTYPE_INT) { + code_skipnext(); + result = code_getint(); + } else if (code == kwTYPE_NUM) { + code_skipnext(); + result = code_getreal(); + } else { + result = def; + } + return result; +} + /* * no-error if the next byte is separator * returns the separator diff --git a/src/common/sberr.c b/src/common/sberr.c index a391f02c..8f746b0b 100644 --- a/src/common/sberr.c +++ b/src/common/sberr.c @@ -385,7 +385,7 @@ void err_throw_str(const char *err) { int try_sp = err_find_try(throw_sp); int reset_sp; - if (!prog_error && try_sp != -1 && prog_stack[try_sp].x.vtry.catch_ip > prog_ip) { + if (!prog_error && try_sp != -1) { bcip_t catch_ip = prog_stack[try_sp].x.vtry.catch_ip; // position after kwCATCH code_jump(catch_ip + 1); @@ -399,7 +399,7 @@ void err_throw_str(const char *err) { caught = err_throw_catch(err); reset_sp = try_sp; - while (!caught && catch_ip != INVALID_ADDR) { + while (!caught && (catch_ip != INVALID_ADDR || try_sp != -1)) { // find in the current block while (!caught && catch_ip != INVALID_ADDR) { code_jump(catch_ip + 1); From f729aa98a42f383308314d675e9a2c084f486fd9 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Thu, 31 Mar 2016 21:41:04 +1000 Subject: [PATCH 18/29] UI: add simple input layout handling --- ide/android/assets/main.bas | 5 ++- src/common/sberr.c | 3 +- src/platform/android/jni/runtime.cpp | 1 + src/ui/inputs.cpp | 20 +++++++++--- src/ui/inputs.h | 10 +++--- src/ui/screen.cpp | 49 +++++++++++++++++++++++----- src/ui/screen.h | 1 + 7 files changed, 70 insertions(+), 19 deletions(-) diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index 68e9f647..065740d3 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -236,13 +236,15 @@ sub manageFiles() bn_edit.width = xmax bn_edit.type = "text" bn_edit.color = "white" + bn_edit.resizable = TRUE bn_files.x = x1 bn_files.y = bn_edit.y + char_h + 2 bn_files.height = ymax - bn_files.y bn_files.width = xmax - x1 bn_files.color = 2 bn_files.type = "list" - f.focus = idxfiles + bn_files.resizable = TRUE + f.focus = idxEdit f.inputs << bn_edit f.inputs << bn_files f = form(f) @@ -354,6 +356,7 @@ sub manageFiles() createUI() reloadList(0) wnd = window() + wnd.showKeypad() while 1 f.doEvents() diff --git a/src/common/sberr.c b/src/common/sberr.c index 8f746b0b..305a5a01 100644 --- a/src/common/sberr.c +++ b/src/common/sberr.c @@ -423,7 +423,8 @@ void err_throw_str(const char *err) { // cleanup the stack stknode_t node; - for (int sp = throw_sp; sp > reset_sp; sp--) { + int sp; + for (sp = throw_sp; sp > reset_sp; sp--) { code_pop_and_free(&node); } if (node.type != kwTRY) { diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 9aacad8a..dea721d9 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -386,6 +386,7 @@ void Runtime::loadConfig() { _output->setTextColor(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND); _output->setFontSize(fontSize); _initialFontSize = _output->getFontSize(); + chdir("/sdcard"); path.append(_app->activity->internalDataPath); path.append(CONFIG_FILE); diff --git a/src/ui/inputs.cpp b/src/ui/inputs.cpp index 51bd1648..d1dac478 100644 --- a/src/ui/inputs.cpp +++ b/src/ui/inputs.cpp @@ -103,6 +103,7 @@ FormInput::FormInput(int x, int y, int w, int h) : _exit(false), _visible(true), _noFocus(false), + _resizable(false), _bg(DEFAULT_BACKGROUND), _fg(DEFAULT_FOREGROUND), _onclick(0) { @@ -320,12 +321,18 @@ bool FormInput::updateUI(var_p_t form, var_p_t field) { } } - bool visible = map_get_int(field, FORM_INPUT_VISIBLE, -1) != 0; + bool visible = map_get_int(field, FORM_INPUT_VISIBLE, 1) != 0; if (visible != _visible) { updated = true; _visible = visible; } + bool resizable = map_get_bool(field, FORM_INPUT_RESIZABLE); + if (resizable != _resizable) { + updated = true; + _resizable = resizable; + } + return updated; } @@ -415,16 +422,21 @@ FormTab::FormTab(const char *link, int x, int y, int w, int h) : } void FormTab::draw(int x, int y, int w, int h, int chw) { + int x_begin = chw; int x_end = x + MIN(w, _width); maSetColor(_fg); - drawText(_link, x + (BN_W / 2), y, w, chw); + drawText(_link, x + x_begin, y, w, chw); setTextColor(); maLine(x_end, y + 4, x_end, y + _height - 4); - x_end -= (BN_W / 2); + x_end -= x_begin; maSetColor(_pressed ? _fg : _bg); - maLine(x + (BN_W / 2), y + _height - 2, x_end, y + _height - 2); + maLine(x + x_begin, y + _height - 2, x_end, y + _height - 2); +} + +int FormTab::padding(bool vert) const { + return vert ? 0 : g_system->getOutput()->getCharWidth() * 2; } // diff --git a/src/ui/inputs.h b/src/ui/inputs.h index 24306965..9da9223b 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -59,8 +59,9 @@ using namespace strlib; #define FORM_INPUT_TYPE "type" #define FORM_INPUT_BG "backgroundColor" #define FORM_INPUT_FG "color" -#define FORM_INPUT_VISIBLE "visible" #define FORM_INPUT_IS_EXIT "isExit" +#define FORM_INPUT_RESIZABLE "resizable" +#define FORM_INPUT_VISIBLE "visible" #define FORM_INPUT_INDEX "selectedIndex" #define FORM_INPUT_LENGTH "length" #define FORM_INPUT_NO_FOCUS "noFocus" @@ -115,7 +116,6 @@ struct FormInput : public Shape { virtual bool isDrawTop() { return false; } virtual bool hasHover() { return false; } virtual void setFocus(bool focus); - virtual void resize(int w, int h) {} void construct(var_p_t form, var_p_t field, int id); void drawButton(const char *caption, int x, int y, int w, int h, bool pressed); @@ -128,8 +128,9 @@ struct FormInput : public Shape { void hide() { _visible = false; } int getId() { return _id; } var_p_t getField(var_p_t form); - bool isVisible() { return _visible; } bool isNoFocus() { return _noFocus; } + bool isResizable() { return _resizable; } + bool isVisible() { return _visible; } void setColor(int bg, int fg) { _bg = bg; _fg = fg; } void setTextColor(); void selected(); @@ -141,6 +142,7 @@ struct FormInput : public Shape { bool _exit; bool _visible; bool _noFocus; + bool _resizable; int _bg; int _fg; bcip_t _onclick; @@ -197,7 +199,7 @@ struct FormTab : public FormLink { virtual ~FormTab() {} void draw(int x, int y, int w, int h, int chw); - int padding(bool vert) const { return vert ? 0 : BN_W; } + int padding(bool vert) const; }; struct FormEditInput : public FormInput { diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index 17b7fa0d..a7d27a61 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -219,6 +219,44 @@ FormInput *Screen::getMenu(FormInput *prev, int px, int py) { return result; } +void Screen::layoutInputs(int newWidth, int newHeight) { + List_each(FormInput *, it, _inputs) { + FormInput *r1 = (*it); + if (r1->isResizable()) { + bool right = true; + bool bottom = true; + List_each(FormInput *, subIt, _inputs) { + FormInput *r2 = (*subIt); + if (r1 != r2) { + if (r2->_x > r1->_x && + r2->_y >= r1->_y && + r2->_y <= r1->_y + _height) { + // cant resize over right side sibling + right = false; + } + if (r2->_y > r1->_y && + r2->_x >= r1->_x && + r2->_x <= r1->_x + _width) { + // cant resize over lower side sibling + bottom = false; + } + } + } + if (right) { + r1->_width = newWidth - r1->_x; + } + if (bottom) { + r1->_height = newHeight - r1->_y; + } + } + } +} + +// whether the given point overlaps with the screen rectangle +bool Screen::overlaps(int px, int py) { + return (!OUTSIDE_RECT(px, py, _x, _y, _width, _height)); +} + int Screen::print(const char *p, int lineHeight, bool allChars) { // print minimum of one character int numChars = 1; @@ -240,11 +278,6 @@ int Screen::print(const char *p, int lineHeight, bool allChars) { return numChars; } -// whether the given point overlaps with the screen rectangle -bool Screen::overlaps(int px, int py) { - return (!OUTSIDE_RECT(px, py, _x, _y, _width, _height)); -} - // remove the button from the list void Screen::remove(Shape *button) { List_each(Shape *, it, _shapes) { @@ -663,10 +696,7 @@ void GraphicScreen::resize(int newWidth, int newHeight, int oldWidth, if (!fullscreen) { drawBase(false); } - List_each(FormInput *, it, _inputs) { - FormInput *next = (*it); - next->resize(newWidth, newHeight); - } + layoutInputs(newWidth, newHeight); } void GraphicScreen::updateFont(int size) { @@ -1018,6 +1048,7 @@ void TextScreen::resize(int newWidth, int newHeight, int, int, int) { _width = newWidth; _height = newHeight; } + layoutInputs(newWidth, newHeight); } // diff --git a/src/ui/screen.h b/src/ui/screen.h index e09fa4b4..b2d43602 100755 --- a/src/ui/screen.h +++ b/src/ui/screen.h @@ -56,6 +56,7 @@ struct Screen : public Shape { FormInput *getMenu(FormInput *prev, int px, int py); FormInput *getNextField(FormInput *field); void getScroll(int &x, int &y) { x = _scrollX; y = _scrollY; } + void layoutInputs(int newWidth, int newHeight); bool overlaps(int px, int py); void remove(Shape *button); void removeImage(unsigned imageId); From b671a13454f19568ad7db333263269b65e220f5d Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 4 Apr 2016 18:03:41 +1000 Subject: [PATCH 19/29] COMMON: Added LSHIFT and RSHIFT bit shift operators --- ChangeLog | 1 + samples/distro-examples/libs/base64.bas | 75 +++++++++++++++++++++ samples/distro-examples/tests/eval-test.bas | 4 ++ src/common/eval.c | 6 ++ src/common/kw.h | 3 + src/languages/keywords.en.c | 32 ++++----- src/platform/sdl/editor.cpp | 2 +- 7 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 samples/distro-examples/libs/base64.bas diff --git a/ChangeLog b/ChangeLog index 4c9e3d90..5457f4d4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ Removed BALLOC, MALLOC and VADR keywords. Removed duplicate ENVIRON Removed system constants BPP and VIDADR, OSNAME, LINECHART, BARCHART Added TIMESTAMP to return the modified-datetime of a file + Added LSHIFT and RSHIFT bit shift operators SBVER now includes build information TICKS now returns millisecond intervals Refactor eval() for performance diff --git a/samples/distro-examples/libs/base64.bas b/samples/distro-examples/libs/base64.bas new file mode 100644 index 00000000..44862d3a --- /dev/null +++ b/samples/distro-examples/libs/base64.bas @@ -0,0 +1,75 @@ +' +' Base64 encoding and decoding +' see: https://tools.ietf.org/html/rfc4648 +' + +Unit Base64 +Export encode + +const alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +const padding="=" + +func encode(message) + local result="" + local in_len = len(message) + local offset = 1 + local group, group_len + local b_a, b_b, b_c + + while offset <= in_len + group_len = min(3, (in_len - offset + 1)) + select case group_len + case 1 + ' final unit of encoded output will be two characters followed by two "=" padding characters. + b_a = mid(message, offset, 1) + result += mid(alphabet, (asc(b_a) rshift 2) + 1, 1) + result += mid(alphabet, ((asc(b_a) band 0x3) lshift 4) + 1, 1) + result += padding + result += padding + case 2 + ' final unit of encoded output will be three characters followed by one "=" padding character. + b_a = mid(message, offset, 1) + b_b = mid(message, offset + 1, 1) + result += mid(alphabet, (asc(b_a) rshift 2) + 1, 1) + result += mid(alphabet, (((asc(b_a) band 0x3) lshift 4) + (asc(b_b) rshift 4)) + 1, 1) + result += mid(alphabet, ((asc(b_b) band 0xf) lshift 2) + 1, 1) + result += padding + case else + b_a = mid(message, offset, 1) + b_b = mid(message, offset + 1, 1) + b_c = mid(message, offset + 2, 1) + result += mid(alphabet, (asc(b_a) rshift 2) + 1, 1) + result += mid(alphabet, (((asc(b_a) band 0x3) lshift 4) + (asc(b_b) rshift 4)) + 1, 1) + result += mid(alphabet, (((asc(b_b) band 0xf) lshift 2) + (asc(b_c) rshift 6)) + 1, 1) + result += mid(alphabet, (asc(b_c) band 0x3f) + 1, 1) + end select + offset += group_len + wend + encode = result +end + +func decode(message) + local result = "" + decode = result +end + +sub test + if (len(alphabet) != 64) then + throw "Invalid alphaet length" + fi + if (encode("foobar") != "Zm9vYmFy") then + throw "base64 encoding error 1" + endif + if (encode("This is a longer string") != "VGhpcyBpcyBhIGxvbmdlciBzdHJpbmc=") then + throw "base64 encoding error 2" + endif + if (encode("This result will end with two equals.") != "VGhpcyByZXN1bHQgd2lsbCBlbmQgd2l0aCB0d28gZXF1YWxzLg==") then + throw "base64 encoding error 3" + endif +' if (base64_dec("Zm9vYmEy") != "foobar") then +' throw "base64 encoding error" +' endif +end + +test + diff --git a/samples/distro-examples/tests/eval-test.bas b/samples/distro-examples/tests/eval-test.bas index f19ae06a..b0488252 100644 --- a/samples/distro-examples/tests/eval-test.bas +++ b/samples/distro-examples/tests/eval-test.bas @@ -203,3 +203,7 @@ assertEq 0xcccccccc Eqv 0xaaaaaaaa, 0x99999999, PROGLINE assertEq 0xcccc Eqv 0xaaaa, 0x9999, PROGLINE assertEq 0xcc Eqv 0xaa, 0x99, PROGLINE assertEq 0xc Eqv 0xa, 0x9, PROGLINE + +' bit shift operators +assertEq 0xFF LSHIFT 1, 0x1FE, PROGLINE +assertEq 0x1FE RSHIFT 1, 0xFF, PROGLINE diff --git a/src/common/eval.c b/src/common/eval.c index 0388c384..8ed9d04f 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -534,6 +534,12 @@ static inline void oper_log(var_t *r, var_t *left, byte op) { case OPLOG_XOR: ri = li ^ ri; break; + case OPLOG_LSHIFT: + ri = li << ri; + break; + case OPLOG_RSHIFT: + ri = li >> ri; + break; } // cleanup diff --git a/src/common/kw.h b/src/common/kw.h index caed2258..5bd54dbb 100644 --- a/src/common/kw.h +++ b/src/common/kw.h @@ -42,6 +42,9 @@ extern "C" { #define OPLOG_MOD 'M' // MOD (remain) #define OPLOG_MDL 'L' // MDL (modulus) #define OPLOG_LIKE 'W' // LIKE wc +#define OPLOG_LSHIFT 'X' // LSHIFT +#define OPLOG_RSHIFT 'Y' // RSHIFT + /** * @ingroup sys * @enum keyword diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index d1535a67..d1fb7907 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -133,21 +133,23 @@ struct keyword_s keyword_table[] = { * OPERATORS (not the symbols) */ struct opr_keyword_s opr_table[] = { -{ "AND", kwTYPE_LOGOPR, '&' }, -{ "OR", kwTYPE_LOGOPR, '|' }, -{ "BAND", kwTYPE_LOGOPR, OPLOG_BAND }, -{ "BOR", kwTYPE_LOGOPR, OPLOG_BOR }, -{ "XOR", kwTYPE_LOGOPR, '~' }, -{ "NOT", kwTYPE_UNROPR, '!' }, -{ "MOD", kwTYPE_MULOPR, OPLOG_MOD }, -{ "MDL", kwTYPE_MULOPR, OPLOG_MDL }, -{ "EQV", kwTYPE_LOGOPR, OPLOG_EQV }, -{ "IMP", kwTYPE_LOGOPR, OPLOG_IMP }, -{ "NAND", kwTYPE_LOGOPR, OPLOG_NAND }, -{ "NOR", kwTYPE_LOGOPR, OPLOG_NOR }, -{ "XNOR", kwTYPE_LOGOPR, OPLOG_XNOR }, -{ "IN", kwTYPE_CMPOPR, OPLOG_IN }, -{ "LIKE", kwTYPE_CMPOPR, OPLOG_LIKE }, +{ "AND", kwTYPE_LOGOPR, '&' }, +{ "OR", kwTYPE_LOGOPR, '|' }, +{ "BAND", kwTYPE_LOGOPR, OPLOG_BAND }, +{ "BOR", kwTYPE_LOGOPR, OPLOG_BOR }, +{ "XOR", kwTYPE_LOGOPR, '~' }, +{ "NOT", kwTYPE_UNROPR, '!' }, +{ "MOD", kwTYPE_MULOPR, OPLOG_MOD }, +{ "MDL", kwTYPE_MULOPR, OPLOG_MDL }, +{ "EQV", kwTYPE_LOGOPR, OPLOG_EQV }, +{ "IMP", kwTYPE_LOGOPR, OPLOG_IMP }, +{ "NAND", kwTYPE_LOGOPR, OPLOG_NAND }, +{ "NOR", kwTYPE_LOGOPR, OPLOG_NOR }, +{ "XNOR", kwTYPE_LOGOPR, OPLOG_XNOR }, +{ "IN", kwTYPE_CMPOPR, OPLOG_IN }, +{ "LIKE", kwTYPE_CMPOPR, OPLOG_LIKE }, +{ "LSHIFT", kwTYPE_LOGOPR, OPLOG_LSHIFT }, +{ "RSHIFT", kwTYPE_LOGOPR, OPLOG_RSHIFT }, { "", 0, 0 } }; diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 70ca661c..7dc34b0c 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -190,7 +190,7 @@ void System::editSource(String loadPath) { break; case SB_KEY_F(1): case SB_KEY_ALT('h'): - _output->setStatus("Keyword Help. Esc=Close"); + _output->setStatus("Keyword Help. F2=online, Esc=Close"); widget = helpWidget; helpWidget->createKeywordIndex(); helpWidget->show(); From bd68bb0696a4aab58e539e4e475091b048e35019 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 4 Apr 2016 20:18:01 +1000 Subject: [PATCH 20/29] COMMON: Added LSHIFT and RSHIFT bit shift operators --- samples/distro-examples/libs/base64.bas | 36 ++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/samples/distro-examples/libs/base64.bas b/samples/distro-examples/libs/base64.bas index 44862d3a..a3c87b1a 100644 --- a/samples/distro-examples/libs/base64.bas +++ b/samples/distro-examples/libs/base64.bas @@ -9,6 +9,12 @@ Export encode const alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" const padding="=" +dim index(128) +for i = 0 to 63 + index(asc(mid(alphabet, i + 1, 1))) = i +next i +index(asc(padding)) = 0 + func encode(message) local result="" local in_len = len(message) @@ -47,9 +53,25 @@ func encode(message) wend encode = result end - + func decode(message) local result = "" + local in_len = len(message) + local i, b_a, b_b, b_c, b_d + + if (in_len % 4 != 0) then + throw "Invalid base64 message length" + endif + + for i = 1 to in_len step 4 + b_a = index(asc(mid(message, i, 1))) + b_b = index(asc(mid(message, i + 1, 1))) + b_c = index(asc(mid(message, i + 2, 1))) + b_d = index(asc(mid(message, i + 3, 1))) + result += chr(((b_a lshift 2) + (b_b rshift 4))) + result += chr(((b_b band 0xf) lshift 4) + (b_c rshift 2)) + result += chr(((b_c band 0x3) lshift 6) + b_d) + next offset decode = result end @@ -66,9 +88,15 @@ sub test if (encode("This result will end with two equals.") != "VGhpcyByZXN1bHQgd2lsbCBlbmQgd2l0aCB0d28gZXF1YWxzLg==") then throw "base64 encoding error 3" endif -' if (base64_dec("Zm9vYmEy") != "foobar") then -' throw "base64 encoding error" -' endif + if (decode("Zm9vYmFy") != "foobar") then + throw "base64 encoding error 4" + endif + if (decode("VGhpcyBpcyBhIGxvbmdlciBzdHJpbmc=") != "This is a longer string") then + throw "base64 encoding error 5" + endif + if (decode("VGhpcyByZXN1bHQgd2lsbCBlbmQgd2l0aCB0d28gZXF1YWxzLg==") != "This result will end with two equals.") then + throw "base64 encoding error 6" + endif end test From 6aa079b745b8d8b144fe98e1f656ca7a23e7a93f Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Tue, 5 Apr 2016 08:37:49 +1000 Subject: [PATCH 21/29] UI: fix editor layout --- src/ui/image.cpp | 46 +++++++++++++++++++++++++++++++++++++++------- src/ui/inputs.h | 1 + src/ui/screen.cpp | 2 ++ src/ui/textedit.h | 4 ++-- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/ui/image.cpp b/src/ui/image.cpp index e8547c21..8233d9c9 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -122,6 +122,27 @@ ImageBuffer *load_image(var_t *map) { return result; } +ImageBuffer *load_image(const unsigned char* buffer, int32_t size) { + ImageBuffer *result = NULL; + unsigned w, h; + unsigned char *image; + unsigned error = 0; + + error = lodepng_decode32(&image, &w, &h, buffer, size); + if (!error) { + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); + } else { + err_throw(ERR_IMAGE_LOAD, lodepng_error_text(error)); + } + return result; +} + ImageBuffer *load_image(dev_file_t *filep) { ImageBuffer *result = NULL; List_each(ImageBuffer *, it, cache) { @@ -424,14 +445,25 @@ extern "C" void v_create_image(var_p_t var) { strcpy(file.name, arg.v.p.ptr); file.type = ft_stream; image = load_image(&file); - } else if (arg.type == V_ARRAY && !prog_error) { - char **data = new char*[arg.v.a.size]; - for (int i = 0; i < arg.v.a.size; i++) { - var_p_t elem = v_elem(&arg, i); - data[i] = elem->v.p.ptr; + } else if (arg.type == V_ARRAY && arg.v.a.size > 0 && !prog_error) { + var_p_t elem0 = v_elem(&arg, 0); + if (elem0->type == V_STR) { + char **data = new char*[arg.v.a.size]; + for (int i = 0; i < arg.v.a.size; i++) { + var_p_t elem = v_elem(&arg, i); + data[i] = elem->v.p.ptr; + } + image = load_xpm_image(data); + delete [] data; + } else if (elem0->type == V_INT) { + unsigned char *data = new unsigned char[arg.v.a.size]; + for (int i = 0; i < arg.v.a.size; i++) { + var_p_t elem = v_elem(&arg, i); + data[i] = (unsigned char)elem->v.i; + } + image = load_image(data, arg.v.a.size); + delete [] data; } - image = load_xpm_image(data); - delete [] data; } else { image = load_image(&arg); } diff --git a/src/ui/inputs.h b/src/ui/inputs.h index 9da9223b..2988d57b 100644 --- a/src/ui/inputs.h +++ b/src/ui/inputs.h @@ -116,6 +116,7 @@ struct FormInput : public Shape { virtual bool isDrawTop() { return false; } virtual bool hasHover() { return false; } virtual void setFocus(bool focus); + virtual void layout(int w, int h) {} void construct(var_p_t form, var_p_t field, int id); void drawButton(const char *caption, int x, int y, int w, int h, bool pressed); diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index a7d27a61..1d051689 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -248,6 +248,8 @@ void Screen::layoutInputs(int newWidth, int newHeight) { if (bottom) { r1->_height = newHeight - r1->_y; } + } else { + r1->layout(newWidth, newHeight); } } } diff --git a/src/ui/textedit.h b/src/ui/textedit.h index 783b995c..f02316b7 100644 --- a/src/ui/textedit.h +++ b/src/ui/textedit.h @@ -106,7 +106,7 @@ struct TextEditInput : public FormEditInput { void selectAll(); bool isDirty() { return _dirty && _state.undostate.undo_point > 0; } void setDirty(bool dirty) { _dirty = dirty; } - void resize(int w, int h) { _width = w; _height = h; } + void layout(int w, int h) { _width = w; _height = h; } const char *getNodeId(); char *getWordBeforeCursor(); bool replaceNext(const char *text); @@ -198,7 +198,7 @@ struct TextEditHelpWidget : public TextEditInput { bool edit(int key, int screenWidth, int charWidth); void paste(const char *text) {} bool isDrawTop() { return true; } - void resize(int w, int h) { _x = w - _width; _height = h; } + void layout(int w, int h) { _x = w - _width; _height = h; } void reset(HelpMode mode); bool closeOnEnter() const; bool lineEditMode() const { return _mode == kLineEdit; } From 601eefb63923b3b231d8a3bf0678215510fef307 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 10 Apr 2016 18:32:48 +1000 Subject: [PATCH 22/29] UI: Fixed using POINT to retrieve IMAGE data --- ChangeLog | 1 + ide/android/assets/main.bas | 4 ++-- src/common/blib_graph.c | 3 ++- src/ui/ansiwidget.h | 1 - src/ui/graphics.cpp | 3 +++ src/ui/screen.cpp | 16 ---------------- src/ui/screen.h | 3 --- src/ui/system.cpp | 12 +++++++++++- 8 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5457f4d4..44ecf26c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,7 @@ Fixed capslock handling Added file manager to main shell program Fixed issues with TRY/CATCH + Fixed using POINT to retrieve IMAGE data 2016-02-11 Added export to mobile command (SDL) diff --git a/ide/android/assets/main.bas b/ide/android/assets/main.bas index 065740d3..a058fac0 100644 --- a/ide/android/assets/main.bas +++ b/ide/android/assets/main.bas @@ -56,7 +56,7 @@ sub do_okay_button() button.x = xmax / 2 button.y = -1 button.label = "Close" - button.backgroundColor = "blue" + button.backgroundColor = "green" button.color = "white" frm.inputs << button frm = form(frm) @@ -66,7 +66,7 @@ end sub do_about() cls - color 2,0 + color 7,0 print " __ _ ___ _" print "(_ ._ _ _.|||_) /\ (_ |/ " print "__)| | |(_||||_)/--\__)|\_" diff --git a/src/common/blib_graph.c b/src/common/blib_graph.c index ff14caec..a74e93be 100644 --- a/src/common/blib_graph.c +++ b/src/common/blib_graph.c @@ -102,7 +102,8 @@ void cmd_window() { // PSET [STEP] x, y [, color | COLOR color] // void cmd_pset() { - int32_t color = dev_fgcolor, step1 = 0; + long color = dev_fgcolor; + int32_t step1 = 0; ipt_t pt; /* diff --git a/src/ui/ansiwidget.h b/src/ui/ansiwidget.h index 32198328..fe2d2866 100755 --- a/src/ui/ansiwidget.h +++ b/src/ui/ansiwidget.h @@ -51,7 +51,6 @@ struct AnsiWidget { int getColor() { return _back->_fg; } int getFontSize() { return _fontSize; } FormInput *getNextField(FormInput *field) { return _back->getNextField(field); } - int getPixel(int x, int y) { return _back->getPixel(x, y); } int getScreenWidth() { return _back->_width; } void getScroll(int &x, int &y) { _back->getScroll(x, y); } int getHeight() { return _height; } diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index 9ff2fc38..b298af30 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -307,6 +307,9 @@ void Graphics::getImageData(Canvas *canvas, uint8_t *image, int Graphics::getPixel(Canvas *canvas, int posX, int posY) { int result = 0; + if (canvas == HANDLE_SCREEN) { + canvas = _screen; + } if (canvas && posX > -1 && posY > -1 diff --git a/src/ui/screen.cpp b/src/ui/screen.cpp index 1d051689..8d12d0c2 100644 --- a/src/ui/screen.cpp +++ b/src/ui/screen.cpp @@ -509,22 +509,6 @@ void GraphicScreen::drawRectFilled(int x1, int y1, int x2, int y2) { maFillRect(x1, y1, x2 - x1, y2 - y1); } -// returns the color of the pixel at the given xy location -int GraphicScreen::getPixel(int x, int y) { - MARect rc; - rc.left = x; - rc.top = y; - rc.width = 1; - rc.height = 1; - - int data[1]; - int result; - - maGetImageData(_image, &data, &rc, 1); - result = -(data[0] & 0x00FFFFFF); - return result; -} - // extend the image to allow for additional content on the newline void GraphicScreen::imageAppend(MAHandle newImage) { MARect srcRect; diff --git a/src/ui/screen.h b/src/ui/screen.h index b2d43602..760d0bf9 100755 --- a/src/ui/screen.h +++ b/src/ui/screen.h @@ -36,7 +36,6 @@ struct Screen : public Shape { virtual void drawLine(int x1, int y1, int x2, int y2) = 0; virtual void drawRect(int x1, int y1, int x2, int y2) = 0; virtual void drawRectFilled(int x1, int y1, int x2, int y2) = 0; - virtual int getPixel(int x, int y) = 0; virtual void newLine(int lineHeight) = 0; virtual int print(const char *p, int lineHeight, bool allChars=false); virtual bool setGraphicsRendition(const char c, int escValue, int lineHeight) = 0; @@ -111,7 +110,6 @@ struct GraphicScreen : public Screen { void resize(int newWidth, int newHeight, int oldWidth, int oldHeight, int lineHeight); void updateFont(int size); - int getPixel(int x, int y); int getMaxHScroll() { return 0; } MAHandle _image; @@ -319,7 +317,6 @@ struct TextScreen : public Screen { void drawLine(int x1, int y1, int x2, int y2); void drawRect(int x1, int y1, int x2, int y2); void drawRectFilled(int x1, int y1, int x2, int y2); - int getPixel(int x, int y) { return 0; } void inset(int x, int y, int w, int h, Screen *over); void newLine(int lineHeight); int print(const char *p, int lineHeight, bool allChars=false); diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 03825b3c..2f2a3cac 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -1072,7 +1072,17 @@ int osd_getpen(int mode) { } long osd_getpixel(int x, int y) { - return g_system->getOutput()->getPixel(x, y); + g_system->getOutput()->redraw(); + + MARect rc; + int data[1]; + rc.left = x; + rc.top = y; + rc.width = 1; + rc.height = 1; + maGetImageData(HANDLE_SCREEN, &data, &rc, 1); + int result = -(data[0] & 0x00FFFFFF); + return result; } int osd_getx(void) { From 67ecb69400e725e5e963ca6443b24acbe1ba054f Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Tue, 12 Apr 2016 21:06:05 +1000 Subject: [PATCH 23/29] UI: update IMAGE handling --- ChangeLog | 1 + samples/distro-examples/libs/base64.bas | 22 +++ samples/distro-examples/tests/chain.bas | 48 +++++ src/common/blib.c | 10 +- src/ui/image.cpp | 240 ++++++++++++++---------- src/ui/image.h | 1 + src/ui/system.cpp | 2 + 7 files changed, 229 insertions(+), 95 deletions(-) diff --git a/ChangeLog b/ChangeLog index 44ecf26c..55b5365c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,7 @@ Added file manager to main shell program Fixed issues with TRY/CATCH Fixed using POINT to retrieve IMAGE data + The IMAGE argument can now be PNG data stored in an INT array 2016-02-11 Added export to mobile command (SDL) diff --git a/samples/distro-examples/libs/base64.bas b/samples/distro-examples/libs/base64.bas index a3c87b1a..c2c68bf7 100644 --- a/samples/distro-examples/libs/base64.bas +++ b/samples/distro-examples/libs/base64.bas @@ -75,6 +75,28 @@ func decode(message) decode = result end +func decodeBin(message) + dim result + local in_len = len(message) + local i, b_a, b_b, b_c, b_d + + if (in_len % 4 != 0) then + throw "Invalid base64 message length" + endif + + for i = 1 to in_len step 4 + b_a = index(asc(mid(message, i, 1))) + b_b = index(asc(mid(message, i + 1, 1))) + b_c = index(asc(mid(message, i + 2, 1))) + b_d = index(asc(mid(message, i + 3, 1))) + result << (((b_a lshift 2) + (b_b rshift 4))) + result << (((b_b band 0xf) lshift 4) + (b_c rshift 2)) + result << (((b_c band 0x3) lshift 6) + b_d) + next offset + decode = result +end + + sub test if (len(alphabet) != 64) then throw "Invalid alphaet length" diff --git a/samples/distro-examples/tests/chain.bas b/samples/distro-examples/tests/chain.bas index 4a9f078b..f176236b 100644 --- a/samples/distro-examples/tests/chain.bas +++ b/samples/distro-examples/tests/chain.bas @@ -7,4 +7,52 @@ ar_code << " print i" ar_code << "next i" chain ar_code +Const FILENAME = "chain.tmp" + +Sub open_1 + Open FILENAME For Output As #1 +End Sub + +' ERROR: Missing ')' (does not read last character): +open_1 +? #1, "Print Pow(2, 1)"; +Close #1 +Chain FILENAME + +' OK: prints 4 (last character is ":"): +open_1 +? #1, "Print Pow(2, 2):"; +Close #1 +Chain FILENAME + +' OK: prints 8 (last character is \n): +open_1 +? #1, "Print Pow(2, 3)" +Close #1 +Chain FILENAME + +' BUG?: prints 16 (reads ONLY the first line, +' should read file as Array - not as String...): +open_1 +? #1, "Print Pow(2, 4)" +? #1, "Print Pow(2, 5)" +Close #1 +Chain FILENAME + +' OK: prints 64 128 (but as a String - not as an Array...): +? +open_1 +? #1, "Print Pow(2, 6):"; +? #1, "Print Pow(2, 7):"; +Close #1 +Chain FILENAME + +' OK: causes an ERROR: Variable is NOT an Array (use DIM) +open_1 +? #1, "Print Power(2, 1)" ' (Power instead of Pow) +Close #1 +Chain FILENAME + +' BUG?: does not report an ERROR (entering endless/long loop - ONLY IF previous "Chain FILENAME" is uncommented): +Chain "Print Power(2, 1)" ' using a "string" instead of filename. diff --git a/src/common/blib.c b/src/common/blib.c index 4fc8ac12..a1973b75 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -2767,7 +2767,15 @@ void cmd_call_vfunc() { if (v_func == NULL || v_func->type != V_FUNC) { rt_raise(ERR_NO_FUNC); } else { - v_func->v.fn.cb(v_func->v.fn.self); + if (code_peek() != kwTYPE_LEVEL_BEGIN) { + err_missing_lp(); + } else { + code_skipnext(); + v_func->v.fn.cb(v_func->v.fn.self); + if (!prog_error && code_peek() != kwTYPE_LEVEL_END) { + err_missing_rp(); + } + } } } diff --git a/src/ui/image.cpp b/src/ui/image.cpp index 8233d9c9..ab5c0181 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -35,12 +35,17 @@ extern "C" { #define IMG_OPACITY "opacity" #define IMG_ID "ID" #define IMG_BID "BID" -#define IMG_HANDLE "HANDLE" extern System *g_system; unsigned nextId = 0; strlib::List cache; +void create_func(var_p_t form, const char *name, method cb); + +void reset_image_cache() { + cache.removeAll(); +} + ImageBuffer::ImageBuffer() : _bid(0), _filename(NULL), @@ -106,18 +111,99 @@ void ImageDisplay::draw(int x, int y, int w, int h, int cw) { maDrawRGB(&dstPoint, _buffer->_image, &srcRect, _opacity, _buffer->_width); } +dev_file_t *eval_filep() { + dev_file_t *result = NULL; + code_skipnext(); + if (code_getnext() == '#') { + int handle = par_getint(); + if (!prog_error) { + result = dev_getfileptr(handle); + } + } + return result; +} + +uint8_t *get_image_data(int x, int y, int w, int h) { + MARect rc; + rc.left = x; + rc.top = y; + rc.width = w; + rc.height = h; + int size = w * 4 * h * 4; + uint8_t *result = (uint8_t *)malloc(size); + if (result != NULL) { + g_system->getOutput()->redraw(); + maGetImageData(HANDLE_SCREEN, result, &rc, w); + } + return result; +} + +ImageBuffer *load_image(var_int_t x) { + var_int_t y, w, h; + int count = par_massget("iii", &y, &w, &h); + int width = g_system->getOutput()->getWidth(); + int height = g_system->getOutput()->getHeight(); + ImageBuffer *result = NULL; + + if (prog_error || count == 0 || count == 2) { + err_throw(ERR_PARAM); + } else { + if (count == 1) { + w = width; + h = height; + } else { + w = MIN(w, width); + h = MIN(h, height); + } + uint8_t* image = get_image_data(x, y, w, h); + if (image == NULL) { + err_throw(ERR_IMAGE_LOAD, "Failed to load screen image"); + } else { + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); + } + } + return result; +} + // share image buffer from another image variable -ImageBuffer *load_image(var_t *map) { +ImageBuffer *load_image(var_t *var) { ImageBuffer *result = NULL; - int bid = map->type == V_MAP ? map_get_int(map, IMG_BID, -1) : -1; - if (bid != -1) { - List_each(ImageBuffer *, it, cache) { - ImageBuffer *next = (*it); - if (next->_bid == (unsigned)bid) { - result = next; - break; + if (var->type == V_MAP) { + int bid = map_get_int(var, IMG_BID, -1); + if (bid != -1) { + List_each(ImageBuffer *, it, cache) { + ImageBuffer *next = (*it); + if (next->_bid == (unsigned)bid) { + result = next; + break; + } + } + } + } else if (var->type == V_ARRAY && var->v.a.maxdim == 2) { + int w = ABS(var->v.a.lbound[0] - var->v.a.ubound[0]) + 1; + int h = ABS(var->v.a.lbound[1] - var->v.a.ubound[1]) + 1; + unsigned char *image = (unsigned char *)malloc(w * h); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int pos = y * w + x; + var_t *elem = (var_t *) (var->v.a.ptr + (sizeof(var_t) * pos)); + // TODO combine RGBA from next four bytes of image[pos] + //image[pos] = v_getint(elem); } } + result = new ImageBuffer(); + result->_bid = ++nextId; + result->_width = w; + result->_height = h; + result->_filename = NULL; + result->_image = image; + cache.add(result); } return result; } @@ -210,21 +296,6 @@ ImageBuffer *load_xpm_image(char **data) { return result; } -uint8_t *get_image_data(int x, int y, int w, int h) { - MARect rc; - rc.left = x; - rc.top = y; - rc.width = w; - rc.height = h; - int size = w * 4 * h * 4; - uint8_t *result = (uint8_t *)malloc(size); - if (result != NULL) { - g_system->getOutput()->flushNow(); - maGetImageData(HANDLE_SCREEN, result, &rc, w); - } - return result; -} - void cmd_image_show(var_s *self) { ImageDisplay image; image._bid = map_get_int(self, IMG_BID, -1); @@ -281,44 +352,50 @@ void cmd_image_hide(var_s *self) { } void cmd_image_save(var_s *self) { - var_int_t x, y, w, h; - int count = par_massget("iiii", &x, &y, &w, &h); - int width = g_system->getOutput()->getWidth(); - int height = g_system->getOutput()->getHeight(); + unsigned id = map_get_int(self, IMG_BID, -1); + ImageBuffer *image = NULL; + List_each(ImageBuffer *, it, cache) { + ImageBuffer *next = (*it); + if (next->_bid == id) { + image = next; + break; + } + } - if (count == 0 && !prog_error) { - // save entire screen - x = 0; - y = 0; - w = width; - h = height; - } else if (count == 2) { - // save width + height at 0, 0 - w = MIN(x, width); - h = MIN(y, height); - x = 0; - y = 0; - } else if (count == 4) { - w = MIN(w, width); - h = MIN(h, height); - } else { - err_throw(ERR_PARAM); + var_t *array = NULL; + dev_file_t *filep = NULL; + byte code = code_peek(); + switch (code) { + case kwTYPE_SEP: + filep = eval_filep(); + break; + default: + array = par_getvar_ptr(); + break; } bool saved = false; - int handle = map_get_int((var_p_t)self, IMG_HANDLE, -1); - if (!prog_error && handle != -1) { - dev_file_t *filep = dev_getfileptr(handle); - if (filep != NULL) { - uint8_t* image = get_image_data(x, y, w, h); - if (image != NULL) { - if (!lodepng_encode32_file(filep->name, image, w, h)) { - saved = true; + if (!prog_error && image != NULL) { + int w = image->_width; + int h = image->_height; + if (filep != NULL && filep->open_flags == DEV_FILE_OUTPUT) { + if (!lodepng_encode32_file(filep->name, image->_image, w, h)) { + saved = true; + } + } else if (array != NULL) { + v_tomatrix(array, w, h); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int pos = y * w + x; + var_t *elem = (var_t *) (array->v.a.ptr + (sizeof(var_t) * pos)); + // TODO combine RGBA from next four bytes of image[pos] + //v_setint(elem, image->_image[pos]); } - free(image); } + saved = true; } } + if (!saved) { err_throw(ERR_IMAGE_SAVE); } @@ -333,27 +410,12 @@ void create_image(var_p_t var, ImageBuffer *image) { map_add_var(var, IMG_ZINDEX, 1); map_add_var(var, IMG_OPACITY, 0); map_add_var(var, IMG_ID, ++nextId); - - if (image != NULL) { - map_add_var(var, IMG_WIDTH, image->_width); - map_add_var(var, IMG_HEIGHT, image->_height); - map_add_var(var, IMG_BID, image->_bid); - - var_p_t v_show = map_add_var(var, "show", 0); - v_show->type = V_FUNC; - v_show->v.fn.self = var; - v_show->v.fn.cb = cmd_image_show; - - var_p_t v_hide = map_add_var(var, "hide", 0); - v_hide->type = V_FUNC; - v_hide->v.fn.self = var; - v_hide->v.fn.cb = cmd_image_hide; - } else { - var_p_t v_save = map_add_var(var, "save", 0); - v_save->type = V_FUNC; - v_save->v.fn.self = var; - v_save->v.fn.cb = cmd_image_save; - } + map_add_var(var, IMG_WIDTH, image->_width); + map_add_var(var, IMG_HEIGHT, image->_height); + map_add_var(var, IMG_BID, image->_bid); + create_func(var, "show", cmd_image_show); + create_func(var, "hide", cmd_image_hide); + create_func(var, "save", cmd_image_save); } // loads an image for the form image input type @@ -417,19 +479,15 @@ void screen_dump() { extern "C" void v_create_image(var_p_t var) { var_t arg; - int handle = -1; ImageBuffer *image = NULL; dev_file_t *filep = NULL; byte code = code_peek(); switch (code) { case kwTYPE_SEP: - code_skipnext(); - if (code_getnext() == '#') { - handle = par_getint(); - if (!prog_error) { - filep = dev_getfileptr(handle); - } + filep = eval_filep(); + if (filep != NULL) { + image = load_image(filep); } break; @@ -455,6 +513,9 @@ extern "C" void v_create_image(var_p_t var) { } image = load_xpm_image(data); delete [] data; + } else if (arg.v.a.maxdim == 2) { + // load from 2d array + image = load_image(&arg); } else if (elem0->type == V_INT) { unsigned char *data = new unsigned char[arg.v.a.size]; for (int i = 0; i < arg.v.a.size; i++) { @@ -464,6 +525,8 @@ extern "C" void v_create_image(var_p_t var) { image = load_image(data, arg.v.a.size); delete [] data; } + } else if (arg.type == V_INT && !prog_error) { + image = load_image(arg.v.i); } else { image = load_image(&arg); } @@ -471,20 +534,9 @@ extern "C" void v_create_image(var_p_t var) { break; }; - if (image == NULL && filep != NULL) { - if (filep->open_flags == DEV_FILE_OUTPUT) { - create_image(var, NULL); - map_add_var(var, IMG_HANDLE, handle); - } else { - handle = -1; - image = load_image(filep); - } - } - if (image != NULL) { create_image(var, image); - } else if (handle == -1) { + } else { err_throw(ERR_BAD_FILE_HANDLE); } } - diff --git a/src/ui/image.h b/src/ui/image.h index bd35216d..16f5adfb 100644 --- a/src/ui/image.h +++ b/src/ui/image.h @@ -42,6 +42,7 @@ struct ImageDisplay : public Shape { }; ImageDisplay *create_display_image(var_p_t var, const char *name); +void reset_image_cache(); void screen_dump(); extern "C" int xpm_decode32(uint8_t **image, unsigned *width, unsigned *height, const char *const *xpm); diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 2f2a3cac..3d29f360 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -19,6 +19,7 @@ #include "common/keymap.h" #include "ui/system.h" #include "ui/inputs.h" +#include "ui/image.h" #define MENU_CONSOLE 0 #define MENU_SOURCE 1 @@ -107,6 +108,7 @@ void System::checkModifiedTime() { bool System::execute(const char *bas) { _output->reset(); + reset_image_cache(); // reset program controlled options opt_antialias = true; From 724bc30965aebb6d5227548035c0a304677615db Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Wed, 13 Apr 2016 22:06:16 +1000 Subject: [PATCH 24/29] UI: update IMAGE handling --- src/ui/form.cpp | 7 ------- src/ui/graphics.cpp | 14 ++++++++------ src/ui/image.cpp | 29 +++++++++++++++++++++-------- src/ui/system.cpp | 7 ++++++- src/ui/system.h | 3 +++ src/ui/window.cpp | 1 - 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/ui/form.cpp b/src/ui/form.cpp index 7bd5923e..b5527f6a 100644 --- a/src/ui/form.cpp +++ b/src/ui/form.cpp @@ -236,13 +236,6 @@ FormInput *create_input(var_p_t v_field) { return widget; } -void create_func(var_p_t form, const char *name, method cb) { - var_p_t v_func = map_add_var(form, name, 0); - v_func->type = V_FUNC; - v_func->v.fn.self = form; - v_func->v.fn.cb = cb; -} - // creates a new form using the given map extern "C" void v_create_form(var_p_t var) { bool hasInputs = false; diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index b298af30..e78c5b58 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -283,18 +283,20 @@ void Graphics::drawText(int left, int top, const char *str, int len) { void Graphics::getImageData(Canvas *canvas, uint8_t *image, const MARect *srcRect, int bytesPerLine) { size_t scale = 1; - int w = bytesPerLine; + int x_end = srcRect->left + srcRect->width; + int y_end = srcRect->top + srcRect->height; if (canvas == HANDLE_SCREEN) { canvas = _screen; } - for (int dy = 0, y = srcRect->top; y < srcRect->height; y += scale, dy++) { - if (y >= canvas->y() && y < canvas->h()) { + for (int dy = 0, y = srcRect->top; y < y_end; y += scale, dy++) { + if (y >= canvas->y() && y < canvas->h()) { pixel_t *line = canvas->getLine(y); - for (int dx = 0, x = srcRect->left; x < srcRect->width; x += scale, dx++) { + int yoffs = (dy * bytesPerLine * 4); + for (int dx = 0, x = srcRect->left; x < x_end; x += scale, dx++) { if (x >= canvas->x() && x < canvas->w()) { - uint8_t r,g,b; + uint8_t r, g, b; GET_RGB2(line[x], r, g, b); - int offs = (4 * dy * w) + (4 * dx); + int offs = yoffs + (dx * 4); image[offs + 0] = r; image[offs + 1] = g; image[offs + 2] = b; diff --git a/src/ui/image.cpp b/src/ui/image.cpp index ab5c0181..85ba8992 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -16,7 +16,9 @@ #include "common/var.h" #include "common/var_map.h" #include "lib/maapi.h" +#include "ui/image.h" #include "ui/system.h" +#include "ui/graphics.h" #if !defined(LODEPNG_NO_COMPILE_CPP) #define LODEPNG_NO_COMPILE_CPP @@ -40,8 +42,6 @@ extern System *g_system; unsigned nextId = 0; strlib::List cache; -void create_func(var_p_t form, const char *name, method cb); - void reset_image_cache() { cache.removeAll(); } @@ -129,7 +129,7 @@ uint8_t *get_image_data(int x, int y, int w, int h) { rc.top = y; rc.width = w; rc.height = h; - int size = w * 4 * h * 4; + int size = w * h * 4; uint8_t *result = (uint8_t *)malloc(size); if (result != NULL) { g_system->getOutput()->redraw(); @@ -188,13 +188,21 @@ ImageBuffer *load_image(var_t *var) { } else if (var->type == V_ARRAY && var->v.a.maxdim == 2) { int w = ABS(var->v.a.lbound[0] - var->v.a.ubound[0]) + 1; int h = ABS(var->v.a.lbound[1] - var->v.a.ubound[1]) + 1; - unsigned char *image = (unsigned char *)malloc(w * h); + int size = w * h * 4; + unsigned char *image = (unsigned char *)malloc(size); for (int y = 0; y < h; y++) { + int yoffs = (4 * y * w); for (int x = 0; x < w; x++) { int pos = y * w + x; var_t *elem = (var_t *) (var->v.a.ptr + (sizeof(var_t) * pos)); - // TODO combine RGBA from next four bytes of image[pos] - //image[pos] = v_getint(elem); + pixel_t px = v_getint(elem); + uint8_t r, g, b; + GET_RGB2(px, r, g, b); + int offs = yoffs + (4 * x); + image[offs + 0] = r; + image[offs + 1] = g; + image[offs + 2] = b; + image[offs + 3] = 255; } } result = new ImageBuffer(); @@ -385,11 +393,16 @@ void cmd_image_save(var_s *self) { } else if (array != NULL) { v_tomatrix(array, w, h); for (int y = 0; y < h; y++) { + int yoffs = (4 * y * w); for (int x = 0; x < w; x++) { + int offs = yoffs + (4 * x); + uint8_t r = image->_image[offs + 0]; + uint8_t g = image->_image[offs + 1]; + uint8_t b = image->_image[offs + 2]; + pixel_t px = SET_RGB(r, g, b); int pos = y * w + x; var_t *elem = (var_t *) (array->v.a.ptr + (sizeof(var_t) * pos)); - // TODO combine RGBA from next four bytes of image[pos] - //v_setint(elem, image->_image[pos]); + v_setint(elem, px); } } saved = true; diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 3d29f360..d17f29a5 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -19,7 +19,6 @@ #include "common/keymap.h" #include "ui/system.h" #include "ui/inputs.h" -#include "ui/image.h" #define MENU_CONSOLE 0 #define MENU_SOURCE 1 @@ -1173,3 +1172,9 @@ int maGetMilliSecondCount(void) { return dev_get_millisecond_count(); } +void create_func(var_p_t form, const char *name, method cb) { + var_p_t v_func = map_add_var(form, name, 0); + v_func->type = V_FUNC; + v_func->v.fn.self = form; + v_func->v.fn.cb = cb; +} diff --git a/src/ui/system.h b/src/ui/system.h index 13203e2a..8fb4d2a1 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -18,6 +18,9 @@ #include "platform/fltk/system.h" #else +void create_func(var_p_t form, const char *name, method cb); +void reset_image_cache(); + struct Cache : public strlib::Properties { Cache(int size) : Properties(size * 2), _index(0) {} void add(const char *key, const char *value); diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 3611372c..f885d13a 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -16,7 +16,6 @@ #include "ui/system.h" extern System *g_system; -void create_func(var_p_t form, const char *name, method cb); #define WINDOW_SCREEN1 "graphicsScreen1" #define WINDOW_SCREEN2 "graphicsScreen2" From 8544435a83b3e031ef61293ed3b6213373d3ffe6 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Wed, 13 Apr 2016 22:28:33 +1000 Subject: [PATCH 25/29] UI: update IMAGE handling --- src/ui/image.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/image.cpp b/src/ui/image.cpp index 85ba8992..5b7cb00a 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -195,7 +195,7 @@ ImageBuffer *load_image(var_t *var) { for (int x = 0; x < w; x++) { int pos = y * w + x; var_t *elem = (var_t *) (var->v.a.ptr + (sizeof(var_t) * pos)); - pixel_t px = v_getint(elem); + pixel_t px = -v_getint(elem); uint8_t r, g, b; GET_RGB2(px, r, g, b); int offs = yoffs + (4 * x); @@ -402,7 +402,7 @@ void cmd_image_save(var_s *self) { pixel_t px = SET_RGB(r, g, b); int pos = y * w + x; var_t *elem = (var_t *) (array->v.a.ptr + (sizeof(var_t) * pos)); - v_setint(elem, px); + v_setint(elem, -px); } } saved = true; From 7cd274c49f09b4ac8b308ff234bee52d9c87cbeb Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Wed, 13 Apr 2016 22:32:52 +1000 Subject: [PATCH 26/29] UI: update lodepng --- src/lib/lodepng.c | 1252 ++++++++++++++++++++++++--------------------- src/lib/lodepng.h | 145 ++++-- 2 files changed, 779 insertions(+), 618 deletions(-) diff --git a/src/lib/lodepng.c b/src/lib/lodepng.c index 2be3fbeb..59e3af94 100644 --- a/src/lib/lodepng.c +++ b/src/lib/lodepng.c @@ -1,7 +1,7 @@ /* -LodePNG version 20140823 +LodePNG version 20160409 -Copyright (c) 2005-2014 Lode Vandevenne +Copyright (c) 2005-2016 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -30,20 +30,17 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for #include "lodepng.h" +#include #include #include -#ifdef LODEPNG_COMPILE_CPP -#include -#endif /*LODEPNG_COMPILE_CPP*/ - -#define VERSION_STRING "20140823" - #if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ #endif /*_MSC_VER */ +const char* LODEPNG_VERSION_STRING = "20160409"; + /* This source file is built up in the following large parts. The code sections with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. @@ -119,6 +116,13 @@ Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); if(error) return error;\ } +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code)\ +{\ + errorvar = code;\ + return;\ +} + /* About uivector, ucvector and string: -All of them wrap dynamic arrays or text strings in a similar way. @@ -174,7 +178,7 @@ static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) { size_t oldsize = p->size, i; if(!uivector_resize(p, size)) return 0; - for(i = oldsize; i < size; i++) p->data[i] = value; + for(i = oldsize; i < size; ++i) p->data[i] = value; return 1; } @@ -192,15 +196,6 @@ static unsigned uivector_push_back(uivector* p, unsigned c) p->data[p->size - 1] = c; return 1; } - -/*copy q to p, returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_copy(uivector* p, const uivector* q) -{ - size_t i; - if(!uivector_resize(p, q->size)) return 0; - for(i = 0; i < q->size; i++) p->data[i] = q->data[i]; - return 1; -} #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ @@ -253,17 +248,6 @@ static void ucvector_init(ucvector* p) p->data = NULL; p->size = p->allocsize = 0; } - -#ifdef LODEPNG_COMPILE_DECODER -/*resize and give all new elements the value*/ -static unsigned ucvector_resizev(ucvector* p, size_t size, unsigned char value) -{ - size_t oldsize = p->size, i; - if(!ucvector_resize(p, size)) return 0; - for(i = oldsize; i < size; i++) p->data[i] = value; - return 1; -} -#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ZLIB @@ -319,10 +303,10 @@ static void string_cleanup(char** out) static void string_set(char** out, const char* in) { - size_t insize = strlen(in), i = 0; + size_t insize = strlen(in), i; if(string_resize(out, insize)) { - for(i = 0; i < insize; i++) + for(i = 0; i != insize; ++i) { (*out)[i] = in[i]; } @@ -363,31 +347,53 @@ static void lodepng_add32bitInt(ucvector* buffer, unsigned value) #ifdef LODEPNG_COMPILE_DISK -unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) { FILE* file; long size; + file = fopen(filename, "rb"); + if(!file) return -1; - /*provide some proper output values if error will happen*/ - *out = 0; - *outsize = 0; + if(fseek(file, 0, SEEK_END) != 0) + { + fclose(file); + return -1; + } + + size = ftell(file); + /* It may give LONG_MAX as directory size, this is invalid for us. */ + if(size == LONG_MAX) size = -1; + + fclose(file); + return size; +} +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) +{ + FILE* file; + size_t readsize; file = fopen(filename, "rb"); if(!file) return 78; - /*get filesize:*/ - fseek(file , 0 , SEEK_END); - size = ftell(file); - rewind(file); + readsize = fread(out, 1, size, file); + fclose(file); + + if (readsize != size) return 78; + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +{ + long size = lodepng_filesize(filename); + if (size < 0) return 78; + *outsize = (size_t)size; - /*read contents of the file into the vector*/ - *outsize = 0; *out = (unsigned char*)lodepng_malloc((size_t)size); - if(size && (*out)) (*outsize) = fread(*out, 1, (size_t)size, file); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ - fclose(file); - if(!(*out) && size) return 83; /*the above malloc failed*/ - return 0; + return lodepng_buffer_file(*out, (size_t)size, filename); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ @@ -418,19 +424,19 @@ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ - (*bitpointer)++;\ + ++(*bitpointer);\ } static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; - for(i = 0; i < nbits; i++) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); } static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; - for(i = 0; i < nbits; i++) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); } #endif /*LODEPNG_COMPILE_ENCODER*/ @@ -441,17 +447,17 @@ static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, uns static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); - (*bitpointer)++; + ++(*bitpointer); return result; } static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0, i; - for(i = 0; i < nbits; i++) + for(i = 0; i != nbits; ++i) { result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; - (*bitpointer)++; + ++(*bitpointer); } return result; } @@ -514,7 +520,7 @@ typedef struct HuffmanTree static void HuffmanTree_draw(HuffmanTree* tree) { std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; - for(size_t i = 0; i < tree->tree1d.size; i++) + for(size_t i = 0; i != tree->tree1d.size; ++i) { if(tree->lengths.data[i]) std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; @@ -551,22 +557,23 @@ static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1. - A good huffmann tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen */ - for(n = 0; n < tree->numcodes * 2; n++) + for(n = 0; n < tree->numcodes * 2; ++n) { tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ } - for(n = 0; n < tree->numcodes; n++) /*the codes*/ + for(n = 0; n < tree->numcodes; ++n) /*the codes*/ { - for(i = 0; i < tree->lengths[n]; i++) /*the bits for this code*/ + for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ { unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); - if(treepos > tree->numcodes - 2) return 55; /*oversubscribed, see comment in lodepng_error_text*/ + /*oversubscribed, see comment in lodepng_error_text*/ + if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ { if(i + 1 == tree->lengths[n]) /*last bit*/ @@ -578,7 +585,7 @@ static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) { /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)...*/ - nodefilled++; + ++nodefilled; /*addresses encoded with numcodes added to it*/ tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; treepos = nodefilled; @@ -588,7 +595,7 @@ static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) } } - for(n = 0; n < tree->numcodes * 2; n++) + for(n = 0; n < tree->numcodes * 2; ++n) { if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ } @@ -605,7 +612,8 @@ static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { uivector blcount; uivector nextcode; - unsigned bits, n, error = 0; + unsigned error = 0; + unsigned bits, n; uivector_init(&blcount); uivector_init(&nextcode); @@ -620,14 +628,14 @@ static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) if(!error) { /*step 1: count number of instances of each code length*/ - for(bits = 0; bits < tree->numcodes; bits++) blcount.data[tree->lengths[bits]]++; + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; /*step 2: generate the nextcode values*/ - for(bits = 1; bits <= tree->maxbitlen; bits++) + for(bits = 1; bits <= tree->maxbitlen; ++bits) { nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; } /*step 3: generate all the codes*/ - for(n = 0; n < tree->numcodes; n++) + for(n = 0; n != tree->numcodes; ++n) { if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; } @@ -651,7 +659,7 @@ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* b unsigned i; tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ - for(i = 0; i < numcodes; i++) tree->lengths[i] = bitlen[i]; + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->maxbitlen = maxbitlen; return HuffmanTree_makeFromLengths2(tree); @@ -659,101 +667,136 @@ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* b #ifdef LODEPNG_COMPILE_ENCODER -/* -A coin, this is the terminology used for the package-merge algorithm and the -coin collector's problem. This is used to generate the huffman tree. -A coin can be multiple coins (when they're merged) -*/ -typedef struct Coin -{ - uivector symbols; - float weight; /*the sum of all weights in this coin*/ -} Coin; +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ -static void coin_init(Coin* c) +/*chain node for boundary package merge*/ +typedef struct BPMNode { - uivector_init(&c->symbols); -} + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; -/*argument c is void* so that this dtor can be given as function pointer to the vector resize function*/ -static void coin_cleanup(void* c) +/*lists of chains*/ +typedef struct BPMLists { - uivector_cleanup(&((Coin*)c)->symbols); -} + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; -static void coin_copy(Coin* c1, const Coin* c2) +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { - c1->weight = c2->weight; - uivector_copy(&c1->symbols, &c2->symbols); -} + unsigned i; + BPMNode* result; -static void add_coins(Coin* c1, const Coin* c2) -{ - size_t i; - for(i = 0; i < c2->symbols.size; i++) uivector_push_back(&c1->symbols, c2->symbols.data[i]); - c1->weight += c2->weight; -} + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) + { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) + { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) + { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } -static void init_coins(Coin* coins, size_t num) -{ - size_t i; - for(i = 0; i < num; i++) coin_init(&coins[i]); + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; } -static void cleanup_coins(Coin* coins, size_t num) +static int bpmnode_compare(const void* a, const void* b) { - size_t i; - for(i = 0; i < num; i++) coin_cleanup(&coins[i]); + int wa = ((const BPMNode*)a)->weight; + int wb = ((const BPMNode*)b)->weight; + if(wa < wb) return -1; + if(wa > wb) return 1; + /*make the qsort a stable sort*/ + return ((const BPMNode*)a)->index < ((const BPMNode*)b)->index ? 1 : -1; } -static int coin_compare(const void* a, const void* b) { - float wa = ((const Coin*)a)->weight; - float wb = ((const Coin*)b)->weight; - return wa > wb ? 1 : wa < wb ? -1 : 0; -} - -static unsigned append_symbol_coins(Coin* coins, const unsigned* frequencies, unsigned numcodes, size_t sum) +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { - unsigned i; - unsigned j = 0; /*index of present symbols*/ - for(i = 0; i < numcodes; i++) + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) { - if(frequencies[i] != 0) /*only include symbols that are present*/ + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } + else + { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) + { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) { - coins[j].weight = frequencies[i] / (float)sum; - uivector_push_back(&coins[j].symbols, i); - j++; + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); } } - return 0; } unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen) { - unsigned i, j; - size_t sum = 0, numpresent = 0; unsigned error = 0; - Coin* coins; /*the coins of the currently calculated row*/ - Coin* prev_row; /*the previous row of coins*/ - size_t numcoins; - size_t coinmem; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ - for(i = 0; i < numcodes; i++) + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) { if(frequencies[i] > 0) { - numpresent++; - sum += frequencies[i]; + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; } } - for(i = 0; i < numcodes; i++) lengths[i] = 0; + for(i = 0; i != numcodes; ++i) lengths[i] = 0; /*ensure at least two present symbols. There should be at least one symbol - according to RFC 1951 section 3.2.7. To decoders incorrectly require two. To + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To make these work as well ensure there are at least two symbols. The Package-Merge code below also doesn't work correctly if there's only one symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ @@ -763,87 +806,55 @@ unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequen } else if(numpresent == 1) { - for(i = 0; i < numcodes; i++) - { - if(frequencies[i]) - { - lengths[i] = 1; - lengths[i == 0 ? 1 : 0] = 1; - break; - } - } + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; } else { - /*Package-Merge algorithm represented by coin collector's problem - For every symbol, maxbitlen coins will be created*/ + BPMLists lists; + BPMNode* node; - coinmem = numpresent * 2; /*max amount of coins needed with the current algo*/ - coins = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem); - prev_row = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem); - if(!coins || !prev_row) - { - lodepng_free(coins); - lodepng_free(prev_row); - return 83; /*alloc fail*/ - } - init_coins(coins, coinmem); - init_coins(prev_row, coinmem); + qsort(leaves, numpresent, sizeof(BPMNode), bpmnode_compare); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ - /*first row, lowest denominator*/ - error = append_symbol_coins(coins, frequencies, numcodes, sum); - numcoins = numpresent; - qsort(coins, numcoins, sizeof(Coin), coin_compare); if(!error) { - unsigned numprev = 0; - for(j = 1; j <= maxbitlen && !error; j++) /*each of the remaining rows*/ - { - unsigned tempnum; - Coin* tempcoins; - /*swap prev_row and coins, and their amounts*/ - tempcoins = prev_row; prev_row = coins; coins = tempcoins; - tempnum = numprev; numprev = numcoins; numcoins = tempnum; - - cleanup_coins(coins, numcoins); - init_coins(coins, numcoins); + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; - numcoins = 0; + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); - /*fill in the merged coins of the previous row*/ - for(i = 0; i + 1 < numprev; i += 2) - { - /*merge prev_row[i] and prev_row[i + 1] into new coin*/ - Coin* coin = &coins[numcoins++]; - coin_copy(coin, &prev_row[i]); - add_coins(coin, &prev_row[i + 1]); - } - /*fill in all the original symbols again*/ - if(j < maxbitlen) - { - error = append_symbol_coins(coins + numcoins, frequencies, numcodes, sum); - numcoins += numpresent; - } - qsort(coins, numcoins, sizeof(Coin), coin_compare); + for(i = 0; i != lists.listsize; ++i) + { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; } - } - if(!error) - { - /*calculate the lenghts of each symbol, as the amount of times a coin of each symbol is used*/ - for(i = 0; i < numpresent - 1; i++) + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { - Coin* coin = &coins[i]; - for(j = 0; j < coin->symbols.size; j++) lengths[coin->symbols.data[j]]++; + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; } } - cleanup_coins(coins, coinmem); - lodepng_free(coins); - cleanup_coins(prev_row, coinmem); - lodepng_free(prev_row); + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); } + lodepng_free(leaves); return error; } @@ -852,7 +863,7 @@ static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigne size_t mincodes, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; - while(!frequencies[numcodes - 1] && numcodes > mincodes) numcodes--; /*trim zeroes*/ + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ tree->maxbitlen = maxbitlen; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); @@ -884,10 +895,10 @@ static unsigned generateFixedLitLenTree(HuffmanTree* tree) if(!bitlen) return 83; /*alloc fail*/ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ - for(i = 0; i <= 143; i++) bitlen[i] = 8; - for(i = 144; i <= 255; i++) bitlen[i] = 9; - for(i = 256; i <= 279; i++) bitlen[i] = 7; - for(i = 280; i <= 287; i++) bitlen[i] = 8; + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); @@ -903,7 +914,7 @@ static unsigned generateFixedDistanceTree(HuffmanTree* tree) if(!bitlen) return 83; /*alloc fail*/ /*there are 32 distance codes, but 30-31 are unused*/ - for(i = 0; i < NUM_DISTANCE_SYMBOLS; i++) bitlen[i] = 5; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); lodepng_free(bitlen); @@ -928,7 +939,7 @@ static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, the expression below because this is the biggest bottleneck while decoding */ ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; - (*bp)++; + ++(*bp); if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ @@ -967,7 +978,7 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, unsigned* bitlen_cl = 0; HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ - if((*bp) >> 3 >= inlength - 2) return 49; /*error: the bit pointer is or will go past the memory*/ + if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ HLIT = readBitsFromStream(bp, in, 5) + 257; @@ -976,6 +987,8 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ HCLEN = readBitsFromStream(bp, in, 4) + 4; + if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ + HuffmanTree_init(&tree_cl); while(!error) @@ -985,7 +998,7 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < NUM_CODE_LENGTH_CODES; i++) + for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) { if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ @@ -998,8 +1011,8 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < NUM_DEFLATE_CODE_SYMBOLS; i++) bitlen_ll[i] = 0; - for(i = 0; i < NUM_DISTANCE_SYMBOLS; i++) bitlen_d[i] = 0; + for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; @@ -1010,61 +1023,59 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, { if(i < HLIT) bitlen_ll[i] = code; else bitlen_d[i - HLIT] = code; - i++; + ++i; } else if(code == 16) /*repeat previous*/ { unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ unsigned value; /*set value to the previous code*/ - if(*bp >= inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 2); if(i < HLIT + 1) value = bitlen_ll[i - 1]; else value = bitlen_d[i - HLIT - 1]; /*repeat this value in the next lengths*/ - for(n = 0; n < replength; n++) + for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = value; else bitlen_d[i - HLIT] = value; - i++; + ++i; } } else if(code == 17) /*repeat "0" 3-10 times*/ { unsigned replength = 3; /*read in the bits that indicate repeat length*/ - if(*bp >= inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - + if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 3); /*repeat this value in the next lengths*/ - for(n = 0; n < replength; n++) + for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; - i++; + ++i; } } else if(code == 18) /*repeat "0" 11-138 times*/ { unsigned replength = 11; /*read in the bits that indicate repeat length*/ - if(*bp >= inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - + if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 7); /*repeat this value in the next lengths*/ - for(n = 0; n < replength; n++) + for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; - i++; + ++i; } } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ @@ -1123,7 +1134,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); out->data[*pos] = (unsigned char)code_ll; - (*pos)++; + ++(*pos); } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { @@ -1136,7 +1147,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size /*part 2: get extra bits and add the value of that to length*/ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; - if(*bp >= inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ length += readBitsFromStream(bp, in, numextrabits_l); /*part 3: get distance code*/ @@ -1156,8 +1167,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size /*part 4: get extra bits from distance*/ numextrabits_d = DISTANCEEXTRA[code_d]; - if(*bp >= inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ - + if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ distance += readBitsFromStream(bp, in, numextrabits_d); /*part 5: fill in all the out[n] values based on the length and dist*/ @@ -1166,12 +1176,14 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size backward = start - distance; if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); - for(forward = 0; forward < length; forward++) - { - out->data[(*pos)] = out->data[backward]; - (*pos)++; - backward++; - if(backward >= start) backward = start - distance; + if (distance < length) { + for(forward = 0; forward < length; ++forward) + { + out->data[(*pos)++] = out->data[backward++]; + } + } else { + memcpy(out->data + *pos, out->data + backward, length); + *pos += length; } } else if(code_ll == 256) @@ -1182,7 +1194,7 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ - error = (*bp) > inlength * 8 ? 10 : 11; + error = ((*bp) > inlength * 8) ? 10 : 11; break; } } @@ -1195,14 +1207,15 @@ static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) { - /*go to first boundary of byte*/ size_t p; unsigned LEN, NLEN, n, error = 0; - while(((*bp) & 0x7) != 0) (*bp)++; + + /*go to first boundary of byte*/ + while(((*bp) & 0x7) != 0) ++(*bp); p = (*bp) / 8; /*byte position*/ /*read LEN (2 bytes) and NLEN (2 bytes)*/ - if(p >= inlength - 4) return 52; /*error, bit pointer will jump past memory*/ + if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ LEN = in[p] + 256u * in[p + 1]; p += 2; NLEN = in[p] + 256u * in[p + 1]; p += 2; @@ -1213,7 +1226,7 @@ static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, siz /*read the literal data: LEN bytes are now stored in the out buffer*/ if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ - for(n = 0; n < LEN; n++) out->data[(*pos)++] = in[p++]; + for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; (*bp) = p * 8; @@ -1297,21 +1310,17 @@ static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, un given array must be sorted (if no value is smaller, it returns the size of the given array)*/ static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { - /*linear search implementation*/ - /*for(size_t i = 1; i < array_size; i++) if(array[i] > value) return i - 1; - return array_size - 1;*/ - - /*binary search implementation (not that much faster) (precondition: array_size > 0)*/ - size_t left = 1; + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; size_t right = array_size - 1; - while(left <= right) - { - size_t mid = (left + right) / 2; - if(array[mid] <= value) left = mid + 1; /*the value to find is more to the right*/ - else if(array[mid - 1] > value) right = mid - 1; /*the value to find is more to the left*/ - else return mid - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if (array[mid] >= value) right = mid - 1; + else left = mid + 1; } - return array_size - 1; + if(left >= array_size || array[left] > value) left--; + return left; } static void addLengthDistance(uivector* values, size_t length, size_t distance) @@ -1369,12 +1378,12 @@ static unsigned hash_init(Hash* hash, unsigned windowsize) } /*initialize hash table*/ - for(i = 0; i < HASH_NUM_VALUES; i++) hash->head[i] = -1; - for(i = 0; i < windowsize; i++) hash->val[i] = -1; - for(i = 0; i < windowsize; i++) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ - for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; i++) hash->headz[i] = -1; - for(i = 0; i < windowsize; i++) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ return 0; } @@ -1395,7 +1404,7 @@ static void hash_cleanup(Hash* hash) static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { unsigned result = 0; - if (pos + 2 < size) + if(pos + 2 < size) { /*A simple shift and xor hash is used. Since the data of PNGs is dominated by zeroes due to the filters, a better hash does not have a significant @@ -1408,7 +1417,7 @@ static unsigned getHash(const unsigned char* data, size_t size, size_t pos) size_t amount, i; if(pos >= size) return 0; amount = size - pos; - for(i = 0; i < amount; i++) result ^= (unsigned)(data[pos + i] << (i * 8u)); + for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); } return result & HASH_BIT_MASK; } @@ -1419,7 +1428,7 @@ static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; if(end > data + size) end = data + size; data = start; - while (data != end && *data == 0) data++; + while(data != end && *data == 0) ++data; /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ return (unsigned)(data - start); } @@ -1468,12 +1477,12 @@ static unsigned encodeLZ77(uivector* out, Hash* hash, const unsigned char *lastptr, *foreptr, *backptr; unsigned hashpos; - if(windowsize <= 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; - for(pos = inpos; pos < insize; pos++) + for(pos = inpos; pos < insize; ++pos) { size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ unsigned chainlength = 0; @@ -1482,8 +1491,8 @@ static unsigned encodeLZ77(uivector* out, Hash* hash, if(usezeros && hashval == 0) { - if (numzeros == 0) numzeros = countZeros(in, insize, pos); - else if (pos + numzeros > insize || in[pos + numzeros - 1] != 0) numzeros--; + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { @@ -1542,11 +1551,14 @@ static unsigned encodeLZ77(uivector* out, Hash* hash, } if(hashpos == hash->chain[hashpos]) break; - - if(numzeros >= 3 && length > numzeros) { + + if(numzeros >= 3 && length > numzeros) + { hashpos = hash->chainz[hashpos]; if(hash->zeros[hashpos] != numzeros) break; - } else { + } + else + { hashpos = hash->chain[hashpos]; /*outdated hash value, happens if particular value was not encountered in whole last window*/ if(hash->val[hashpos] != (int)hashval) break; @@ -1577,7 +1589,7 @@ static unsigned encodeLZ77(uivector* out, Hash* hash, offset = lazyoffset; hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ hash->headz[numzeros] = -1; /*idem*/ - pos--; + --pos; } } } @@ -1597,15 +1609,15 @@ static unsigned encodeLZ77(uivector* out, Hash* hash, else { addLengthDistance(out, length, offset); - for(i = 1; i < length; i++) + for(i = 1; i < length; ++i) { - pos++; + ++pos; wpos = pos & (windowsize - 1); hashval = getHash(in, insize, pos); if(usezeros && hashval == 0) { - if (numzeros == 0) numzeros = countZeros(in, insize, pos); - else if (pos + numzeros > insize || in[pos + numzeros - 1] != 0) numzeros--; + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { @@ -1628,7 +1640,7 @@ static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, s size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; unsigned datapos = 0; - for(i = 0; i < numdeflateblocks; i++) + for(i = 0; i != numdeflateblocks; ++i) { unsigned BFINAL, BTYPE, LEN, NLEN; unsigned char firstbyte; @@ -1643,13 +1655,13 @@ static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, s if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; NLEN = 65535 - LEN; - ucvector_push_back(out, (unsigned char)(LEN % 256)); - ucvector_push_back(out, (unsigned char)(LEN / 256)); - ucvector_push_back(out, (unsigned char)(NLEN % 256)); - ucvector_push_back(out, (unsigned char)(NLEN / 256)); + ucvector_push_back(out, (unsigned char)(LEN & 255)); + ucvector_push_back(out, (unsigned char)(LEN >> 8)); + ucvector_push_back(out, (unsigned char)(NLEN & 255)); + ucvector_push_back(out, (unsigned char)(NLEN >> 8)); /*Decompressed data*/ - for(j = 0; j < 65535 && datapos < datasize; j++) + for(j = 0; j < 65535 && datapos < datasize; ++j) { ucvector_push_back(out, data[datapos++]); } @@ -1667,7 +1679,7 @@ static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encode const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { size_t i = 0; - for(i = 0; i < lz77_encoded->size; i++) + for(i = 0; i != lz77_encoded->size; ++i) { unsigned val = lz77_encoded->data[i]; addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); @@ -1760,21 +1772,21 @@ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, else { if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); - for(i = datapos; i < dataend; i++) lz77_encoded.data[i] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ } if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); /*Count the frequencies of lit, len and dist codes*/ - for(i = 0; i < lz77_encoded.size; i++) + for(i = 0; i != lz77_encoded.size; ++i) { unsigned symbol = lz77_encoded.data[i]; - frequencies_ll.data[symbol]++; + ++frequencies_ll.data[symbol]; if(symbol > 256) { unsigned dist = lz77_encoded.data[i + 2]; - frequencies_d.data[dist]++; + ++frequencies_d.data[dist]; i += 3; } } @@ -1790,19 +1802,19 @@ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; /*store the code lengths of both generated trees in bitlen_lld*/ - for(i = 0; i < numcodes_ll; i++) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); - for(i = 0; i < numcodes_d; i++) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); + for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeroes), 18 (11-138 zeroes)*/ - for(i = 0; i < (unsigned)bitlen_lld.size; i++) + for(i = 0; i != (unsigned)bitlen_lld.size; ++i) { unsigned j = 0; /*amount of repititions*/ - while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) j++; + while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ { - j++; /*include the first zero*/ + ++j; /*include the first zero*/ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { uivector_push_back(&bitlen_lld_e, 17); @@ -1821,7 +1833,7 @@ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, size_t k; unsigned num = j / 6, rest = j % 6; uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); - for(k = 0; k < num; k++) + for(k = 0; k < num; ++k) { uivector_push_back(&bitlen_lld_e, 16); uivector_push_back(&bitlen_lld_e, 6 - 3); @@ -1843,12 +1855,12 @@ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, /*generate tree_cl, the huffmantree of huffmantrees*/ if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < bitlen_lld_e.size; i++) + for(i = 0; i != bitlen_lld_e.size; ++i) { - frequencies_cl.data[bitlen_lld_e.data[i]]++; + ++frequencies_cl.data[bitlen_lld_e.data[i]]; /*after a repeat code come the bits that specify the number of repetitions, those don't need to be in the frequencies_cl calculation*/ - if(bitlen_lld_e.data[i] >= 16) i++; + if(bitlen_lld_e.data[i] >= 16) ++i; } error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, @@ -1856,7 +1868,7 @@ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, if(error) break; if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < tree_cl.numcodes; i++) + for(i = 0; i != tree_cl.numcodes; ++i) { /*lenghts of code length tree is in the order as specified by deflate*/ bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); @@ -1892,16 +1904,16 @@ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, HDIST = (unsigned)(numcodes_d - 1); HCLEN = (unsigned)bitlen_cl.size - 4; /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ - while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) HCLEN--; + while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; addBitsToStream(bp, out, HLIT, 5); addBitsToStream(bp, out, HDIST, 5); addBitsToStream(bp, out, HCLEN, 4); /*write the code lenghts of the code length alphabet*/ - for(i = 0; i < HCLEN + 4; i++) addBitsToStream(bp, out, bitlen_cl.data[i], 3); + for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); /*write the lenghts of the lit/len AND the dist alphabet*/ - for(i = 0; i < bitlen_lld_e.size; i++) + for(i = 0; i != bitlen_lld_e.size; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); @@ -1970,7 +1982,7 @@ static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, } else /*no LZ77, but still will be Huffman compressed*/ { - for(i = datapos; i < dataend; i++) + for(i = datapos; i < dataend; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); } @@ -1998,8 +2010,10 @@ static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t else if(settings->btype == 1) blocksize = insize; else /*if(settings->btype == 2)*/ { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ blocksize = insize / 8 + 8; - if(blocksize < 65535) blocksize = 65535; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; } numdeflateblocks = (insize + blocksize - 1) / blocksize; @@ -2008,7 +2022,7 @@ static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t error = hash_init(&hash, settings->windowsize); if(error) return error; - for(i = 0; i < numdeflateblocks && !error; i++) + for(i = 0; i != numdeflateblocks && !error; ++i) { unsigned final = (i == numdeflateblocks - 1); size_t start = i * blocksize; @@ -2071,7 +2085,7 @@ static unsigned update_adler32(unsigned adler, const unsigned char* data, unsign { s1 += (*data++); s2 += s1; - amount--; + --amount; } s1 %= 65521; s2 %= 65521; @@ -2165,7 +2179,6 @@ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsig unsigned char* deflatedata = 0; size_t deflatesize = 0; - unsigned ADLER32; /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ unsigned FLEVEL = 0; @@ -2177,15 +2190,15 @@ unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsig /*ucvector-controlled version of the output buffer, for dynamic array*/ ucvector_init_buffer(&outv, *out, *outsize); - ucvector_push_back(&outv, (unsigned char)(CMFFLG / 256)); - ucvector_push_back(&outv, (unsigned char)(CMFFLG % 256)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); error = deflate(&deflatedata, &deflatesize, in, insize, settings); if(!error) { - ADLER32 = adler32(in, (unsigned)insize); - for(i = 0; i < deflatesize; i++) ucvector_push_back(&outv, deflatedata[i]); + unsigned ADLER32 = adler32(in, (unsigned)insize); + for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); lodepng_free(deflatedata); lodepng_add32bitInt(&outv, ADLER32); } @@ -2218,7 +2231,7 @@ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsign static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { - if (!settings->custom_zlib) return 87; /*no custom zlib function provided */ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2226,7 +2239,7 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsi static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { - if (!settings->custom_zlib) return 87; /*no custom zlib function provided */ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_ENCODER*/ @@ -2287,6 +2300,8 @@ const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, /* / CRC32 / */ /* ////////////////////////////////////////////////////////////////////////// */ + +#ifndef LODEPNG_NO_COMPILE_CRC /* CRC polynomial: 0xedb88320 */ static unsigned lodepng_crc32_table[256] = { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, @@ -2324,17 +2339,19 @@ static unsigned lodepng_crc32_table[256] = { }; /*Return the CRC of the bytes buf[0..len-1].*/ -unsigned lodepng_crc32(const unsigned char* buf, size_t len) +unsigned lodepng_crc32(const unsigned char* data, size_t length) { - unsigned c = 0xffffffffL; - size_t n; - - for(n = 0; n < len; n++) + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) { - c = lodepng_crc32_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); } - return c ^ 0xffffffffL; + return r ^ 0xffffffffu; } +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ /* ////////////////////////////////////////////////////////////////////////// */ /* / Reading and writing single bits and bytes from/to stream for LodePNG / */ @@ -2343,7 +2360,7 @@ unsigned lodepng_crc32(const unsigned char* buf, size_t len) static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); - (*bitpointer)++; + ++(*bitpointer); return result; } @@ -2351,7 +2368,7 @@ static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned ch { unsigned result = 0; size_t i; - for(i = nbits - 1; i < nbits; i--) + for(i = nbits - 1; i < nbits; --i) { result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i; } @@ -2367,7 +2384,7 @@ static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); } - (*bitpointer)++; + ++(*bitpointer); } #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2376,7 +2393,7 @@ static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, /*the current bit in bitstream may be 0 or 1 for this to work*/ if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); - (*bitpointer)++; + ++(*bitpointer); } /* ////////////////////////////////////////////////////////////////////////// */ @@ -2391,7 +2408,7 @@ unsigned lodepng_chunk_length(const unsigned char* chunk) void lodepng_chunk_type(char type[5], const unsigned char* chunk) { unsigned i; - for(i = 0; i < 4; i++) type[i] = (char)chunk[4 + i]; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; type[4] = 0; /*null termination char*/ } @@ -2469,7 +2486,7 @@ unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsi (*outlength) = new_length; chunk_start = &(*out)[new_length - total_chunk_length]; - for(i = 0; i < total_chunk_length; i++) chunk_start[i] = chunk[i]; + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; return 0; } @@ -2497,7 +2514,7 @@ unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned l chunk[7] = (unsigned char)type[3]; /*3: the data*/ - for(i = 0; i < length; i++) chunk[8 + i] = data[i]; + for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; /*4: CRC (of the chunkname characters and the data)*/ lodepng_chunk_generate_crc(chunk); @@ -2569,7 +2586,7 @@ unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* { dest->palette = (unsigned char*)lodepng_malloc(1024); if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ - for(i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i]; + for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; } return 0; } @@ -2586,10 +2603,15 @@ static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColo if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } - if(a->palettesize != b->palettesize) return 0; - for(i = 0; i < a->palettesize * 4; i++) - { - if(a->palette[i] != b->palette[i]) return 0; + /*if one of the palette sizes is 0, then we consider it to be the same as the + other: it means that e.g. the palette was not given by the user and should be + considered the same as the palette inside the PNG.*/ + if(1/*a->palettesize != 0 && b->palettesize != 0*/) { + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; + } } return 1; } @@ -2618,7 +2640,7 @@ unsigned lodepng_palette_add(LodePNGColorMode* info, info->palette[4 * info->palettesize + 1] = g; info->palette[4 * info->palettesize + 2] = b; info->palette[4 * info->palettesize + 3] = a; - info->palettesize++; + ++info->palettesize; return 0; } @@ -2651,7 +2673,7 @@ unsigned lodepng_is_palette_type(const LodePNGColorMode* info) unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { size_t i; - for(i = 0; i < info->palettesize; i++) + for(i = 0; i != info->palettesize; ++i) { if(info->palette[i * 4 + 3] < 255) return 1; } @@ -2667,12 +2689,18 @@ unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { - return (w * h * lodepng_get_bpp(color) + 7) / 8; + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { - return (w * h * lodepng_get_bpp_lct(colortype, bitdepth) + 7) / 8; + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } @@ -2681,7 +2709,10 @@ size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colorty /*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) { - return h * ((w * lodepng_get_bpp(color) + 7) / 8); + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; + return h * line; } #endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -2691,14 +2722,14 @@ static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGCol static void LodePNGUnknownChunks_init(LodePNGInfo* info) { unsigned i; - for(i = 0; i < 3; i++) info->unknown_chunks_data[i] = 0; - for(i = 0; i < 3; i++) info->unknown_chunks_size[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; } static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { unsigned i; - for(i = 0; i < 3; i++) lodepng_free(info->unknown_chunks_data[i]); + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); } static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) @@ -2707,13 +2738,13 @@ static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* LodePNGUnknownChunks_cleanup(dest); - for(i = 0; i < 3; i++) + for(i = 0; i != 3; ++i) { size_t j; dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ - for(j = 0; j < src->unknown_chunks_size[i]; j++) + for(j = 0; j < src->unknown_chunks_size[i]; ++j) { dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; } @@ -2734,7 +2765,7 @@ static void LodePNGText_init(LodePNGInfo* info) static void LodePNGText_cleanup(LodePNGInfo* info) { size_t i; - for(i = 0; i < info->text_num; i++) + for(i = 0; i != info->text_num; ++i) { string_cleanup(&info->text_keys[i]); string_cleanup(&info->text_strings[i]); @@ -2749,7 +2780,7 @@ static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) dest->text_keys = 0; dest->text_strings = 0; dest->text_num = 0; - for(i = 0; i < source->text_num; i++) + for(i = 0; i != source->text_num; ++i) { CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); } @@ -2772,7 +2803,7 @@ unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) return 83; /*alloc fail*/ } - info->text_num++; + ++info->text_num; info->text_keys = new_keys; info->text_strings = new_strings; @@ -2799,7 +2830,7 @@ static void LodePNGIText_init(LodePNGInfo* info) static void LodePNGIText_cleanup(LodePNGInfo* info) { size_t i; - for(i = 0; i < info->itext_num; i++) + for(i = 0; i != info->itext_num; ++i) { string_cleanup(&info->itext_keys[i]); string_cleanup(&info->itext_langtags[i]); @@ -2820,7 +2851,7 @@ static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) dest->itext_transkeys = 0; dest->itext_strings = 0; dest->itext_num = 0; - for(i = 0; i < source->itext_num; i++) + for(i = 0; i != source->itext_num; ++i) { CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], source->itext_transkeys[i], source->itext_strings[i])); @@ -2849,7 +2880,7 @@ unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langt return 83; /*alloc fail*/ } - info->itext_num++; + ++info->itext_num; info->itext_keys = new_keys; info->itext_langtags = new_langtags; info->itext_transkeys = new_transkeys; @@ -2957,14 +2988,14 @@ struct ColorTree static void color_tree_init(ColorTree* tree) { int i; - for(i = 0; i < 16; i++) tree->children[i] = 0; + for(i = 0; i != 16; ++i) tree->children[i] = 0; tree->index = -1; } static void color_tree_cleanup(ColorTree* tree) { int i; - for(i = 0; i < 16; i++) + for(i = 0; i != 16; ++i) { if(tree->children[i]) { @@ -2978,7 +3009,7 @@ static void color_tree_cleanup(ColorTree* tree) static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { int bit = 0; - for(bit = 0; bit < 8; bit++) + for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) return -1; @@ -3000,7 +3031,7 @@ static void color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { int bit; - for(bit = 0; bit < 8; bit++) + for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) @@ -3249,7 +3280,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; @@ -3257,7 +3288,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, } else if(mode->bitdepth == 16) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; @@ -3267,7 +3298,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = 0; - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; @@ -3279,7 +3310,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 3 + 0]; buffer[1] = in[i * 3 + 1]; @@ -3290,7 +3321,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, } else { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; @@ -3306,7 +3337,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, { unsigned index; size_t j = 0; - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { if(mode->bitdepth == 8) index = in[i]; else index = readBitsFromReversedStream(&j, in, mode->bitdepth); @@ -3331,7 +3362,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; if(has_alpha) buffer[3] = in[i * 2 + 1]; @@ -3339,7 +3370,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, } else { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; if(has_alpha) buffer[3] = in[i * 4 + 2]; @@ -3350,7 +3381,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 4 + 0]; buffer[1] = in[i * 4 + 1]; @@ -3360,7 +3391,7 @@ static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, } else { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; @@ -3384,30 +3415,31 @@ static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned s } else if(mode->colortype == LCT_RGB) { - *r = 256 * in[i * 6 + 0] + in[i * 6 + 1]; - *g = 256 * in[i * 6 + 2] + in[i * 6 + 3]; - *b = 256 * in[i * 6 + 4] + in[i * 6 + 5]; - if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_GREY_ALPHA) { - *r = *g = *b = 256 * in[i * 4 + 0] + in[i * 4 + 1]; - *a = 256 * in[i * 4 + 2] + in[i * 4 + 3]; + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; } else if(mode->colortype == LCT_RGBA) { - *r = 256 * in[i * 8 + 0] + in[i * 8 + 1]; - *g = 256 * in[i * 8 + 2] + in[i * 8 + 3]; - *b = 256 * in[i * 8 + 4] + in[i * 8 + 5]; - *a = 256 * in[i * 8 + 6] + in[i * 8 + 7]; + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; } } unsigned lodepng_convert(unsigned char* out, const unsigned char* in, - LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) { size_t i; @@ -3417,25 +3449,35 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, if(lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - for(i = 0; i < numbytes; i++) out[i] = in[i]; + for(i = 0; i != numbytes; ++i) out[i] = in[i]; return 0; } if(mode_out->colortype == LCT_PALETTE) { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; size_t palsize = 1u << mode_out->bitdepth; - if(mode_out->palettesize < palsize) palsize = mode_out->palettesize; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) + { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + } + if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); - for(i = 0; i < palsize; i++) + for(i = 0; i != palsize; ++i) { - unsigned char* p = &mode_out->palette[i * 4]; + const unsigned char* p = &palette[i * 4]; color_tree_add(&tree, p[0], p[1], p[2], p[3], i); } } if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { - for(i = 0; i < numpixels; i++) + for(i = 0; i != numpixels; ++i) { unsigned short r = 0, g = 0, b = 0, a = 0; getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); @@ -3453,10 +3495,10 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, else { unsigned char r = 0, g = 0, b = 0, a = 0; - for(i = 0; i < numpixels; i++) + for(i = 0; i != numpixels; ++i) { getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); - rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + CERROR_TRY_RETURN(rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a)); } } @@ -3465,7 +3507,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, color_tree_cleanup(&tree); } - return 0; /*no error (this function currently never has one, but maybe OOM detection added later.)*/ + return 0; /*no error*/ } #ifdef LODEPNG_COMPILE_ENCODER @@ -3494,7 +3536,7 @@ void lodepng_color_profile_init(LodePNGColorProfile* profile) }*/ /*Returns how many bits needed to represent given value (max 8 bit)*/ -unsigned getValueRequiredBits(unsigned char value) +static unsigned getValueRequiredBits(unsigned char value) { if(value == 0 || value == 255) return 1; /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ @@ -3504,9 +3546,9 @@ unsigned getValueRequiredBits(unsigned char value) /*profile must already have been inited with mode. It's ok to set some parameters of profile to done already.*/ -unsigned get_color_profile(LodePNGColorProfile* profile, - const unsigned char* in, unsigned w, unsigned h, - const LodePNGColorMode* mode) +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode) { unsigned error = 0; size_t i; @@ -3528,10 +3570,11 @@ unsigned get_color_profile(LodePNGColorProfile* profile, if(mode->bitdepth == 16) { unsigned short r, g, b, a; - for(i = 0; i < numpixels; i++) + for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - if(r % 257u != 0 || g % 257u != 0 || b % 257u != 0 || a % 257u != 0) /*first and second byte differ*/ + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { sixteen = 1; break; @@ -3545,10 +3588,10 @@ unsigned get_color_profile(LodePNGColorProfile* profile, profile->bits = 16; bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ - for(i = 0; i < numpixels; i++) + for(i = 0; i != numpixels; ++i) { getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - + if(!colored_done && (r != g || r != b)) { profile->colored = 1; @@ -3578,15 +3621,28 @@ unsigned get_color_profile(LodePNGColorProfile* profile, alpha_done = 1; } } - if(alpha_done && numcolors_done && colored_done && bits_done) break; } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + alpha_done = 1; + } + } + } } else /* < 16-bit */ { - for(i = 0; i < numpixels; i++) + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { - unsigned char r = 0, g = 0, b = 0, a = 0; getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); if(!bits_done && profile->bits < 8) @@ -3643,7 +3699,7 @@ unsigned get_color_profile(LodePNGColorProfile* profile, p[n * 4 + 2] = b; p[n * 4 + 3] = a; } - profile->numcolors++; + ++profile->numcolors; numcolors_done = profile->numcolors >= maxnumcolors; } } @@ -3651,10 +3707,24 @@ unsigned get_color_profile(LodePNGColorProfile* profile, if(alpha_done && numcolors_done && colored_done && bits_done) break; } + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + alpha_done = 1; + } + } + } + /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ - profile->key_r *= 257; - profile->key_g *= 257; - profile->key_b *= 257; + profile->key_r += (profile->key_r << 8); + profile->key_g += (profile->key_g << 8); + profile->key_b += (profile->key_b << 8); } color_tree_cleanup(&tree); @@ -3675,11 +3745,15 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, unsigned i, n, palettebits, grey_ok, palette_ok; lodepng_color_profile_init(&prof); - error = get_color_profile(&prof, image, w, h, mode_in); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); if(error) return error; mode_out->key_defined = 0; - if(prof.key && w * h <= 16) prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + if(prof.key && w * h <= 16) + { + prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } grey_ok = !prof.colored && !prof.alpha; /*grey without alpha, with potentially low bits*/ n = prof.numcolors; palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); @@ -3691,7 +3765,7 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, { unsigned char* p = prof.palette; lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ - for(i = 0; i < prof.numcolors; i++) + for(i = 0; i != prof.numcolors; ++i) { error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); if(error) break; @@ -3774,7 +3848,7 @@ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t fil unsigned i; /*calculate width and height in pixels of each pass*/ - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; @@ -3783,7 +3857,7 @@ static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t fil } filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ filter_passstart[i + 1] = filter_passstart[i] @@ -3810,7 +3884,7 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, { CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ } - if(insize < 29) + if(insize < 33) { CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ } @@ -3824,7 +3898,11 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, { CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ } - if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') + if(lodepng_chunk_length(in + 8) != 13) + { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ } @@ -3838,6 +3916,11 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, info->filter_method = in[27]; info->interlace_method = in[28]; + if(*w == 0 || *h == 0) + { + CERROR_RETURN_ERROR(state->error, 93); + } + if(!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); @@ -3875,53 +3958,53 @@ static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scan switch(filterType) { case 0: - for(i = 0; i < length; i++) recon[i] = scanline[i]; + for(i = 0; i != length; ++i) recon[i] = scanline[i]; break; case 1: - for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if(precon) { - for(i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; } else { - for(i = 0; i < length; i++) recon[i] = scanline[i]; + for(i = 0; i != length; ++i) recon[i] = scanline[i]; } break; case 3: if(precon) { - for(i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; - for(i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); } else { - for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); } break; case 4: if(precon) { - for(i = 0; i < bytewidth; i++) + for(i = 0; i != bytewidth; ++i) { recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ } - for(i = bytewidth; i < length; i++) + for(i = bytewidth; i < length; ++i) { recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } } else { - for(i = 0; i < bytewidth; i++) + for(i = 0; i != bytewidth; ++i) { recon[i] = scanline[i]; } - for(i = bytewidth; i < length; i++) + for(i = bytewidth; i < length; ++i) { /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ recon[i] = (scanline[i] + recon[i - bytewidth]); @@ -3950,7 +4033,7 @@ static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w size_t bytewidth = (bpp + 7) / 8; size_t linebytes = (w * bpp + 7) / 8; - for(y = 0; y < h; y++) + for(y = 0; y < h; ++y) { size_t outindex = linebytes * y; size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ @@ -3985,16 +4068,16 @@ static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsig if(bpp >= 8) { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; - for(b = 0; b < bytewidth; b++) + for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } @@ -4003,18 +4086,18 @@ static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsig } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; - for(b = 0; b < bpp; b++) + for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ @@ -4040,10 +4123,10 @@ static void removePaddingBits(unsigned char* out, const unsigned char* in, unsigned y; size_t diff = ilinebits - olinebits; size_t ibp = 0, obp = 0; /*input and output bit pointers*/ - for(y = 0; y < h; y++) + for(y = 0; y < h; ++y) { size_t x; - for(x = 0; x < olinebits; x++) + for(x = 0; x < olinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); @@ -4075,7 +4158,7 @@ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); } - /*we can immediatly filter into the out buffer, no other steps needed*/ + /*we can immediately filter into the out buffer, no other steps needed*/ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); } else /*interlace_method is 1 (Adam7)*/ @@ -4085,7 +4168,7 @@ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, @@ -4118,7 +4201,7 @@ static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* dat } if(color->palettesize > 256) return 38; /*error: palette too big*/ - for(i = 0; i < color->palettesize; i++) + for(i = 0; i != color->palettesize; ++i) { color->palette[4 * i + 0] = data[pos++]; /*R*/ color->palette[4 * i + 1] = data[pos++]; /*G*/ @@ -4137,7 +4220,7 @@ static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* dat /*error: more alpha values given than there are palette entries*/ if(chunkLength > color->palettesize) return 38; - for(i = 0; i < chunkLength; i++) color->palette[4 * i + 3] = data[i]; + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } else if(color->colortype == LCT_GREY) { @@ -4209,7 +4292,7 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz unsigned length, string2_begin; length = 0; - while(length < chunkLength && data[length] != 0) length++; + while(length < chunkLength && data[length] != 0) ++length; /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ @@ -4218,7 +4301,7 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; - for(i = 0; i < length; i++) key[i] = (char)data[i]; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; string2_begin = length + 1; /*skip keyword null terminator*/ @@ -4227,7 +4310,7 @@ static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, siz if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ str[length] = 0; - for(i = 0; i < length; i++) str[i] = (char)data[string2_begin + i]; + for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; error = lodepng_add_text(info, key, str); @@ -4255,7 +4338,7 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting while(!error) /*not really a while loop, only used to break on error*/ { - for(length = 0; length < chunkLength && data[length] != 0; length++) ; + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ @@ -4263,7 +4346,7 @@ static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSetting if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; - for(i = 0; i < length; i++) key[i] = (char)data[i]; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ @@ -4308,7 +4391,7 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ /*read the key*/ - for(length = 0; length < chunkLength && data[length] != 0; length++) ; + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ @@ -4316,7 +4399,7 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; - for(i = 0; i < length; i++) key[i] = (char)data[i]; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; /*read the compression method*/ compressed = data[length + 1]; @@ -4328,24 +4411,24 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting /*read the langtag*/ begin = length + 3; length = 0; - for(i = begin; i < chunkLength && data[i] != 0; i++) length++; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; langtag = (char*)lodepng_malloc(length + 1); if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ langtag[length] = 0; - for(i = 0; i < length; i++) langtag[i] = (char)data[begin + i]; + for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; /*read the transkey*/ begin += length + 1; length = 0; - for(i = begin; i < chunkLength && data[i] != 0; i++) length++; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; transkey = (char*)lodepng_malloc(length + 1); if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ transkey[length] = 0; - for(i = 0; i < length; i++) transkey[i] = (char)data[begin + i]; + for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; /*read the actual text*/ begin += length + 1; @@ -4367,7 +4450,7 @@ static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSetting if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); decoded.data[length] = 0; - for(i = 0; i < length; i++) decoded.data[i] = data[begin + i]; + for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; } error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); @@ -4422,6 +4505,8 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, ucvector idat; /*the data from idat chunks*/ ucvector scanlines; size_t predict; + size_t numpixels; + size_t outsize; /*for unknown chunk order*/ unsigned unknown = 0; @@ -4435,6 +4520,14 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; + numpixels = *w * *h; + + /*multiplication overflow*/ + if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); + /*multiplication overflow possible further below. Allows up to 2^31-1 pixel + bytes with 16-bit RGBA, the rest is room for filter bytes.*/ + if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + ucvector_init(&idat); chunk = &in[33]; /*first byte of the first chunk after the header*/ @@ -4465,7 +4558,7 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, { size_t oldsize = idat.size; if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); - for(i = 0; i < chunkLength; i++) idat.data[oldsize + i] = data[i]; + for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ @@ -4561,24 +4654,44 @@ static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, ucvector_init(&scanlines); /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. - The prediction is currently not correct for interlaced PNG images.*/ - predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) + { + /*The extra *h is added because this are the filter bytes every scanline starts with*/ + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + } + else + { + /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ + const LodePNGColorMode* color = &state->info_png.color; + predict = 0; + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); + } if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ if(!state->error) { state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, idat.size, &state->decoder.zlibsettings); + if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ } ucvector_cleanup(&idat); if(!state->error) { - ucvector outv; - ucvector_init(&outv); - if(!ucvector_resizev(&outv, - lodepng_get_raw_size(*w, *h, &state->info_png.color), 0)) state->error = 83; /*alloc fail*/ - if(!state->error) state->error = postProcessScanlines(outv.data, scanlines.data, *w, *h, &state->info_png); - *out = outv.data; + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) + { + for(i = 0; i < outsize; i++) (*out)[i] = 0; + state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); } ucvector_cleanup(&scanlines); } @@ -4655,7 +4768,7 @@ unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const u unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth) { - unsigned char* buffer; + unsigned char* buffer = 0; size_t buffersize; unsigned error; error = lodepng_load_file(&buffer, &buffersize, filename); @@ -4775,7 +4888,7 @@ static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) size_t i; ucvector PLTE; ucvector_init(&PLTE); - for(i = 0; i < info->palettesize * 4; i++) + for(i = 0; i != info->palettesize * 4; ++i) { /*add all channels except alpha channel*/ if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); @@ -4796,32 +4909,32 @@ static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { size_t amount = info->palettesize; /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ - for(i = info->palettesize; i > 0; i--) + for(i = info->palettesize; i != 0; --i) { - if(info->palette[4 * (i - 1) + 3] == 255) amount--; + if(info->palette[4 * (i - 1) + 3] == 255) --amount; else break; } /*add only alpha channel*/ - for(i = 0; i < amount; i++) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); + for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); } else if(info->colortype == LCT_GREY) { if(info->key_defined) { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r % 256)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); } } else if(info->colortype == LCT_RGB) { if(info->key_defined) { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r % 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g % 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b % 256)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); } } @@ -4861,10 +4974,10 @@ static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* te size_t i; ucvector text; ucvector_init(&text); - for(i = 0; keyword[i] != 0; i++) ucvector_push_back(&text, (unsigned char)keyword[i]); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&text, 0); /*0 termination char*/ - for(i = 0; textstring[i] != 0; i++) ucvector_push_back(&text, (unsigned char)textstring[i]); + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); error = addChunk(out, "tEXt", text.data, text.size); ucvector_cleanup(&text); @@ -4880,7 +4993,7 @@ static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* te ucvector_init(&data); ucvector_init(&compressed); - for(i = 0; keyword[i] != 0; i++) ucvector_push_back(&data, (unsigned char)keyword[i]); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*0 termination char*/ ucvector_push_back(&data, 0); /*compression method: 0*/ @@ -4889,7 +5002,7 @@ static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* te (unsigned char*)textstring, textsize, zlibsettings); if(!error) { - for(i = 0; i < compressed.size; i++) ucvector_push_back(&data, compressed.data[i]); + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); error = addChunk(out, "zTXt", data.data, data.size); } @@ -4907,14 +5020,14 @@ static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* ke ucvector_init(&data); - for(i = 0; keyword[i] != 0; i++) ucvector_push_back(&data, (unsigned char)keyword[i]); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*null termination char*/ ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ ucvector_push_back(&data, 0); /*compression method*/ - for(i = 0; langtag[i] != 0; i++) ucvector_push_back(&data, (unsigned char)langtag[i]); + for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); ucvector_push_back(&data, 0); /*null termination char*/ - for(i = 0; transkey[i] != 0; i++) ucvector_push_back(&data, (unsigned char)transkey[i]); + for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); ucvector_push_back(&data, 0); /*null termination char*/ if(compressed) @@ -4925,13 +5038,13 @@ static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* ke (unsigned char*)textstring, textsize, zlibsettings); if(!error) { - for(i = 0; i < compressed_data.size; i++) ucvector_push_back(&data, compressed_data.data[i]); + for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); } ucvector_cleanup(&compressed_data); } else /*not compressed*/ { - for(i = 0; textstring[i] != 0; i++) ucvector_push_back(&data, (unsigned char)textstring[i]); + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); } if(!error) error = addChunk(out, "iTXt", data.data, data.size); @@ -4946,21 +5059,21 @@ static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) ucvector_init(&bKGD); if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_r % 256)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_r % 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_g / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_g % 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_b / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_b % 256)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); } else if(info->color.colortype == LCT_PALETTE) { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r % 256)); /*palette index*/ + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ } error = addChunk(out, "bKGD", bKGD.data, bKGD.size); @@ -4974,8 +5087,8 @@ static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) unsigned error = 0; unsigned char* data = (unsigned char*)lodepng_malloc(7); if(!data) return 83; /*alloc fail*/ - data[0] = (unsigned char)(time->year / 256); - data[1] = (unsigned char)(time->year % 256); + data[0] = (unsigned char)(time->year >> 8); + data[1] = (unsigned char)(time->year & 255); data[2] = (unsigned char)time->month; data[3] = (unsigned char)time->day; data[4] = (unsigned char)time->hour; @@ -5011,57 +5124,49 @@ static void filterScanline(unsigned char* out, const unsigned char* scanline, co switch(filterType) { case 0: /*None*/ - for(i = 0; i < length; i++) out[i] = scanline[i]; + for(i = 0; i != length; ++i) out[i] = scanline[i]; break; case 1: /*Sub*/ - if(prevline) - { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth]; - } - else - { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth]; - } + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; break; case 2: /*Up*/ if(prevline) { - for(i = 0; i < length; i++) out[i] = scanline[i] - prevline[i]; + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; } else { - for(i = 0; i < length; i++) out[i] = scanline[i]; + for(i = 0; i != length; ++i) out[i] = scanline[i]; } break; case 3: /*Average*/ if(prevline) { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i] - prevline[i] / 2; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) / 2); + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); } else { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth] / 2; + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); } break; case 4: /*Paeth*/ if(prevline) { /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ - for(i = 0; i < bytewidth; i++) out[i] = (scanline[i] - prevline[i]); - for(i = bytewidth; i < length; i++) + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) { out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); } } else { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ - for(i = bytewidth; i < length; i++) out[i] = (scanline[i] - scanline[i - bytewidth]); + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); } break; default: return; /*unexisting filter type given*/ @@ -5073,7 +5178,7 @@ static float flog2(float f) { float result = 0; while(f > 32) { result += 4; f /= 16; } - while(f > 2) { result++; f /= 2; } + while(f > 2) { ++result; f /= 2; } return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); } @@ -5116,7 +5221,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, if(strategy == LFS_ZERO) { - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; @@ -5129,39 +5234,39 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, { /*adaptive filtering*/ size_t sum[5]; - ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned char type, bestType = 0; - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - ucvector_init(&attempt[type]); - if(!ucvector_resize(&attempt[type], linebytes)) return 83; /*alloc fail*/ + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ } if(!error) { - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { /*try the 5 filter types*/ - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type); + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); /*calculate the sum of the result*/ sum[type] = 0; if(type == 0) { - for(x = 0; x < linebytes; x++) sum[type] += (unsigned char)(attempt[type].data[x]); + for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); } else { - for(x = 0; x < linebytes; x++) + for(x = 0; x != linebytes; ++x) { /*For differences, each byte should be treated as signed, values above 127 are negative (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. This means filtertype 0 is almost never chosen, but that is justified.*/ - unsigned char s = attempt[type].data[x]; + unsigned char s = attempt[type][x]; sum[type] += s < 128 ? s : (255U - s); } } @@ -5178,37 +5283,37 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x]; + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } - for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]); + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_ENTROPY) { float sum[5]; - ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ float smallest = 0; unsigned type, bestType = 0; unsigned count[256]; - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - ucvector_init(&attempt[type]); - if(!ucvector_resize(&attempt[type], linebytes)) return 83; /*alloc fail*/ + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ } - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { /*try the 5 filter types*/ - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type); - for(x = 0; x < 256; x++) count[x] = 0; - for(x = 0; x < linebytes; x++) count[attempt[type].data[x]]++; - count[type]++; /*the filter type itself is part of the scanline*/ + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + for(x = 0; x != 256; ++x) count[x] = 0; + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ sum[type] = 0; - for(x = 0; x < 256; x++) + for(x = 0; x != 256; ++x) { float p = count[x] / (float)(linebytes + 1); sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; @@ -5225,14 +5330,14 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x]; + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } - for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]); + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_PREDEFINED) { - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; @@ -5248,7 +5353,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, deflate the scanline after every filter attempt to see which one deflates best. This is very slow and gives only slightly smaller, sometimes even larger, result*/ size_t size[5]; - ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; @@ -5262,22 +5367,22 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, images only, so disable it*/ zlibsettings.custom_zlib = 0; zlibsettings.custom_deflate = 0; - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - ucvector_init(&attempt[type]); - ucvector_resize(&attempt[type], linebytes); /*todo: give error if resize failed*/ + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ } - for(y = 0; y < h; y++) /*try the 5 filter types*/ + for(y = 0; y != h; ++y) /*try the 5 filter types*/ { - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - unsigned testsize = attempt[type].size; + unsigned testsize = linebytes; /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ - filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type); + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); size[type] = 0; dummy = 0; - zlib_compress(&dummy, &size[type], attempt[type].data, testsize, &zlibsettings); + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); lodepng_free(dummy); /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || size[type] < smallest) @@ -5288,9 +5393,9 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, } prevline = &in[y * linebytes]; out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x]; + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } - for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]); + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else return 88; /* unknown filter strategy */ @@ -5305,17 +5410,17 @@ static void addPaddingBits(unsigned char* out, const unsigned char* in, unsigned y; size_t diff = olinebits - ilinebits; size_t obp = 0, ibp = 0; /*bit pointers*/ - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { size_t x; - for(x = 0; x < ilinebits; x++) + for(x = 0; x < ilinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } /*obp += diff; --> no, fill in some value in the padding bits too, to avoid "Use of uninitialised value of size ###" warning from valgrind*/ - for(x = 0; x < diff; x++) setBitOfReversedStream(&obp, out, 0); + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); } } @@ -5340,16 +5445,16 @@ static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigne if(bpp >= 8) { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; - for(b = 0; b < bytewidth; b++) + for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } @@ -5358,18 +5463,18 @@ static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigne } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); - for(b = 0; b < bpp; b++) + for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); @@ -5415,7 +5520,7 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const } else { - /*we can immediatly filter into the out buffer, no other steps needed*/ + /*we can immediately filter into the out buffer, no other steps needed*/ error = filter(*out, in, w, h, &info_png->color, settings); } } @@ -5440,7 +5545,7 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned i; Adam7_interlace(adam7, in, w, h, bpp); - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { if(bpp < 8) { @@ -5479,7 +5584,7 @@ static unsigned getPaletteTranslucency(const unsigned char* palette, size_t pale size_t i; unsigned key = 0; unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ - for(i = 0; i < palettesize; i++) + for(i = 0; i != palettesize; ++i) { if(!key && palette[4 * i + 3] == 0) { @@ -5624,7 +5729,7 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, /*tIME*/ if(info.time_defined) addChunk_tIME(&outv, &info.time); /*tEXt and/or zTXt*/ - for(i = 0; i < info.text_num; i++) + for(i = 0; i != info.text_num; ++i) { if(strlen(info.text_keys[i]) > 79) { @@ -5649,7 +5754,7 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, if(state->encoder.add_id) { unsigned alread_added_id_text = 0; - for(i = 0; i < info.text_num; i++) + for(i = 0; i != info.text_num; ++i) { if(!strcmp(info.text_keys[i], "LodePNG")) { @@ -5659,11 +5764,11 @@ unsigned lodepng_encode(unsigned char** out, size_t* outsize, } if(alread_added_id_text == 0) { - addChunk_tEXt(&outv, "LodePNG", VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ } } /*iTXt*/ - for(i = 0; i < info.itext_num; i++) + for(i = 0; i != info.itext_num; ++i) { if(strlen(info.itext_keys[i]) > 79) { @@ -5818,8 +5923,7 @@ const char* lodepng_error_text(unsigned code) case 43: return "bKGD chunk has wrong size for palette image"; case 44: return "bKGD chunk has wrong size for greyscale image"; case 45: return "bKGD chunk has wrong size for RGB image"; - /*the input data is empty, maybe a PNG file doesn't exist or is in the wrong path*/ - case 48: return "empty input or file doesn't exist"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; case 49: return "jumped past memory while generating dynamic huffman tree"; case 50: return "jumped past memory while generating dynamic huffman tree"; case 51: return "jumped past memory while inflating huffman block"; @@ -5866,6 +5970,10 @@ const char* lodepng_error_text(unsigned code) case 89: return "text chunk keyword too short or long: must have size 1-79"; /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "too many pixels, not supported"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; } return "unknown error code"; } @@ -5882,27 +5990,20 @@ namespace lodepng { #ifdef LODEPNG_COMPILE_DISK -void load_file(std::vector& buffer, const std::string& filename) +unsigned load_file(std::vector& buffer, const std::string& filename) { - std::ifstream file(filename.c_str(), std::ios::in|std::ios::binary|std::ios::ate); - - /*get filesize*/ - std::streamsize size = 0; - if(file.seekg(0, std::ios::end).good()) size = file.tellg(); - if(file.seekg(0, std::ios::beg).good()) size -= file.tellg(); - - /*read contents of the file into the vector*/ - buffer.resize(size_t(size)); - if(size > 0) file.read((char*)(&buffer[0]), size); + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ -void save_file(const std::vector& buffer, const std::string& filename) +unsigned save_file(const std::vector& buffer, const std::string& filename) { - std::ofstream file(filename.c_str(), std::ios::out|std::ios::binary); - file.write(buffer.empty() ? 0 : (char*)&buffer[0], std::streamsize(buffer.size())); + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); } -#endif //LODEPNG_COMPILE_DISK +#endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER @@ -5925,7 +6026,7 @@ unsigned decompress(std::vector& out, const std::vector& out, const unsigned char* in, size_t insize, @@ -5947,8 +6048,8 @@ unsigned compress(std::vector& out, const std::vector& out, unsigned& w, unsigned& h, const LodePNGColorType colortype, unsigned bitdepth) { std::vector buffer; - load_file(buffer, filename); + unsigned error = load_file(buffer, filename); + if(error) return error; return decode(out, w, h, buffer, colortype, bitdepth); } -#endif //LODEPNG_COMPILE_DECODER -#endif //LODEPNG_COMPILE_DISK +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ENCODER unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, @@ -6086,7 +6188,7 @@ unsigned encode(const std::string& filename, { std::vector buffer; unsigned error = encode(buffer, in, w, h, colortype, bitdepth); - if(!error) save_file(buffer, filename); + if(!error) error = save_file(buffer, filename); return error; } @@ -6097,8 +6199,8 @@ unsigned encode(const std::string& filename, if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_ENCODER -#endif //LODEPNG_COMPILE_PNG -} //namespace lodepng +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ diff --git a/src/lib/lodepng.h b/src/lib/lodepng.h index de4e68df..33f10519 100644 --- a/src/lib/lodepng.h +++ b/src/lib/lodepng.h @@ -1,7 +1,7 @@ /* -LodePNG version 20140823 +LodePNG version 20160409 -Copyright (c) 2005-2014 Lode Vandevenne +Copyright (c) 2005-2016 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -28,12 +28,16 @@ freely, subject to the following restrictions: #include /*for size_t*/ +extern const char* LODEPNG_VERSION_STRING; + /* The following #defines are used to create code sections. They can be disabled to disable code sections, which can give faster compile time and smaller binary. The "NO_COMPILE" defines are designed to be used to pass as defines to the compiler command to disable them without modifying this header, e.g. -DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. */ /*deflate & zlib. If disabled, you must specify alternative zlib functions in the custom_zlib field of the compress and decompress settings*/ @@ -74,11 +78,14 @@ source files with custom allocators.*/ #ifdef __cplusplus #ifndef LODEPNG_NO_COMPILE_CPP #define LODEPNG_COMPILE_CPP -#include -#include #endif #endif +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + #ifdef LODEPNG_COMPILE_PNG /*The PNG color types (also used for raw).*/ typedef enum LodePNGColorType @@ -208,8 +215,8 @@ Same as the other decode functions, but instead takes a filename as input. unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_DECODER +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER /*Same as lodepng_encode_memory, but encodes to an std::vector. colortype @@ -232,9 +239,9 @@ unsigned encode(const std::string& filename, unsigned encode(const std::string& filename, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_ENCODER -} //namespace lodepng +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -501,7 +508,7 @@ For 16-bit per channel colors, uses big endian format like PNG does. Return value is LodePNG error code */ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, - LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h); #ifdef LODEPNG_COMPILE_DECODER @@ -533,7 +540,7 @@ typedef enum LodePNGFilterStrategy { /*every filter at zero*/ LFS_ZERO, - /*Use filter that gives minumum sum, as described in the official PNG filter heuristic.*/ + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ LFS_MINSUM, /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending on the image, this is better or worse than minsum.*/ @@ -565,9 +572,9 @@ typedef struct LodePNGColorProfile void lodepng_color_profile_init(LodePNGColorProfile* profile); /*Get a LodePNGColorProfile of the image.*/ -unsigned get_color_profile(LodePNGColorProfile* profile, - const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in); +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); /*The function LodePNG uses internally to decide the PNG color with auto_convert. Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, @@ -624,7 +631,7 @@ typedef struct LodePNGState LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ unsigned error; #ifdef LODEPNG_COMPILE_CPP - //For the lodepng::State subclass. + /* For the lodepng::State subclass. */ virtual ~LodePNGState(){} #endif } LodePNGState; @@ -674,7 +681,11 @@ Third byte: must be uppercase Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy */ -/*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/ +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ unsigned lodepng_chunk_length(const unsigned char* chunk); /*puts the 4-byte type in null terminated string*/ @@ -802,7 +813,7 @@ unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const #endif /*LODEPNG_COMPILE_DISK*/ #ifdef LODEPNG_COMPILE_CPP -//The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ namespace lodepng { #ifdef LODEPNG_COMPILE_PNG @@ -816,7 +827,7 @@ class State : public LodePNGState }; #ifdef LODEPNG_COMPILE_DECODER -//Same as other lodepng::decode, but using a State for more settings and information. +/* Same as other lodepng::decode, but using a State for more settings and information. */ unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize); @@ -826,7 +837,7 @@ unsigned decode(std::vector& out, unsigned& w, unsigned& h, #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER -//Same as other lodepng::encode, but using a State for more settings and information. +/* Same as other lodepng::encode, but using a State for more settings and information. */ unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, State& state); @@ -837,47 +848,47 @@ unsigned encode(std::vector& out, #ifdef LODEPNG_COMPILE_DISK /* -Load a file from disk into an std::vector. If the vector is empty, then either -the file doesn't exist or is an empty file. +Load a file from disk into an std::vector. +return value: error code (0 means ok) */ -void load_file(std::vector& buffer, const std::string& filename); +unsigned load_file(std::vector& buffer, const std::string& filename); /* Save the binary data in an std::vector to a file on disk. The file is overwritten without warning. */ -void save_file(const std::vector& buffer, const std::string& filename); -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_PNG +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER -//Zlib-decompress an unsigned char buffer +/* Zlib-decompress an unsigned char buffer */ unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); -//Zlib-decompress an std::vector +/* Zlib-decompress an std::vector */ unsigned decompress(std::vector& out, const std::vector& in, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); -#endif //LODEPNG_COMPILE_DECODER +#endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER -//Zlib-compress an unsigned char buffer +/* Zlib-compress an unsigned char buffer */ unsigned compress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); -//Zlib-compress an std::vector +/* Zlib-compress an std::vector */ unsigned compress(std::vector& out, const std::vector& in, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); -#endif //LODEPNG_COMPILE_ENCODER -#endif //LODEPNG_COMPILE_ZLIB -} //namespace lodepng +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ /* TODO: [.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often -[.] check compatibility with vareous compilers - done but needs to be redone for every newer version +[.] check compatibility with various compilers - done but needs to be redone for every newer version [X] converting color to 16-bit per channel types [ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) [ ] make sure encoder generates no chunks with size > (2^31)-1 @@ -885,8 +896,9 @@ unsigned compress(std::vector& out, const std::vector Date: Thu, 14 Apr 2016 21:23:54 +1000 Subject: [PATCH 27/29] COMMON: fixed issues with CHAIN --- ChangeLog | 10 +++- samples/distro-examples/tests/chain.bas | 15 ++++- .../distro-examples/tests/output/chain.out | 48 +++++++++++++++- src/common/blib.c | 12 ++-- src/common/brun.c | 57 +++++++++++-------- 5 files changed, 104 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index 55b5365c..7c934d42 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,18 +14,22 @@ Fixed editor highlighting Runtime errors now show source screen with red error highlighter Form refresh command now takes an boolean arg, true=push ui state to vars + The IMAGE argument can now be PNG data stored in an INT array + The IMAGE argument can now be x,y,w,h screen corordinates + The IMAGE argument can now be a 2-D array of POINTS + Updated IMAGE sub-command to save 2-D array of POINT Added window.setFont command to set font size, bold and italic. example: w = window():w.setFont(10, "pt", false, true) + TRUE is now always returned as 1 + Added file manager to main shell program Fixed problem with escaped chars using FORMAT Fixed problem with XNOR command result - TRUE is now always returned as 1 Fixed problem with IMP and EQV command result Fixed problem with INKEY command Fixed capslock handling - Added file manager to main shell program Fixed issues with TRY/CATCH Fixed using POINT to retrieve IMAGE data - The IMAGE argument can now be PNG data stored in an INT array + Fixed issues with CHAIN 2016-02-11 Added export to mobile command (SDL) diff --git a/samples/distro-examples/tests/chain.bas b/samples/distro-examples/tests/chain.bas index f176236b..b8c2697e 100644 --- a/samples/distro-examples/tests/chain.bas +++ b/samples/distro-examples/tests/chain.bas @@ -1,5 +1,11 @@ -code = "print \"hello\"" -chain code +' +' Tests for CHAIN +' + +chain "print \"error1\"" +chain "throw \"error2\"" +chain "throw \"error3\"" +chain "throw \"error4\"" dim ar_code ar_code << "for i=0 to 10" @@ -7,6 +13,8 @@ ar_code << " print i" ar_code << "next i" chain ar_code +chain "throw \"error5\"" + Const FILENAME = "chain.tmp" Sub open_1 @@ -34,8 +42,10 @@ Chain FILENAME ' BUG?: prints 16 (reads ONLY the first line, ' should read file as Array - not as String...): open_1 +? #1, "Print \"POW 2,4\"" ? #1, "Print Pow(2, 4)" ? #1, "Print Pow(2, 5)" +? #1, "Print \"done\"" Close #1 Chain FILENAME @@ -56,3 +66,4 @@ Chain FILENAME ' BUG?: does not report an ERROR (entering endless/long loop - ONLY IF previous "Chain FILENAME" is uncommented): Chain "Print Power(2, 1)" ' using a "string" instead of filename. +kill FILENAME diff --git a/samples/distro-examples/tests/output/chain.out b/samples/distro-examples/tests/output/chain.out index ac153734..de9ad44f 100644 --- a/samples/distro-examples/tests/output/chain.out +++ b/samples/distro-examples/tests/output/chain.out @@ -1,4 +1,22 @@ -hello +error1 + + + * RTE-ERROR AT CH_MAIN:1 * +Description: +error2 + + + + * RTE-ERROR AT CH_MAIN:1 * +Description: +error3 + + + + * RTE-ERROR AT CH_MAIN:1 * +Description: +error4 + 0 1 2 @@ -10,3 +28,31 @@ hello 8 9 10 + + + * RTE-ERROR AT CH_MAIN:1 * +Description: +error5 + +2 +4 +8 +POW 2,4 +16 +32 +done + +64 +128 + + + * RTE-ERROR AT CH_MAIN:1 * +Description: +Expr/RT: Variable is NOT an array (Use DIM) + + + + * RTE-ERROR AT CH_MAIN:1 * +Description: +Expr/RT: Variable is NOT an array (Use DIM) + diff --git a/src/common/blib.c b/src/common/blib.c index a1973b75..2fe5cb82 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -2767,14 +2767,12 @@ void cmd_call_vfunc() { if (v_func == NULL || v_func->type != V_FUNC) { rt_raise(ERR_NO_FUNC); } else { - if (code_peek() != kwTYPE_LEVEL_BEGIN) { - err_missing_lp(); - } else { + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + code_skipnext(); + } + v_func->v.fn.cb(v_func->v.fn.self); + if (code_peek() == kwTYPE_LEVEL_END) { code_skipnext(); - v_func->v.fn.cb(v_func->v.fn.self); - if (!prog_error && code_peek() != kwTYPE_LEVEL_END) { - err_missing_rp(); - } } } } diff --git a/src/common/brun.c b/src/common/brun.c index 98ec031c..e1232835 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -366,8 +366,7 @@ void brun_break() { */ void cmd_chain(void) { var_t var; - const char *code = NULL; - char *code_alloc = NULL; + char *code = NULL; v_init(&var); eval(&var); @@ -380,20 +379,18 @@ void cmd_chain(void) { if (var.type == V_STR) { if (access(var.v.p.ptr, R_OK) == 0) { // argument is a file name - FILE *f = fopen(var.v.p.ptr, "r"); - if (!fseek(f, 0, SEEK_END)) { - int len = ftell(f); - fseek(f, 0, SEEK_SET); - if (len) { - code_alloc = malloc(len + 1); - fgets(code_alloc, len, f); - code = code_alloc; - } + int h = open(var.v.p.ptr, O_BINARY | O_RDONLY, 0644); + if (h != -1) { + int len = lseek(h, 0, SEEK_END); + lseek(h, 0, SEEK_SET); + code = (char *)malloc(len + 1); + len = read(h, code, len); + code[len] = '\0'; + close(h); } - fclose(f); } if (!code) { - code = var.v.p.ptr; + code = strdup(var.v.p.ptr); } } else if (var.type == V_ARRAY) { int el; @@ -403,19 +400,16 @@ void cmd_chain(void) { if (el_p->type == V_STR) { int str_len = strlen(el_p->v.p.ptr) + 2; if (len) { - code_alloc = realloc(code_alloc, len + str_len); - strcat(code_alloc, el_p->v.p.ptr); + code = realloc(code, len + str_len); + strcat(code, el_p->v.p.ptr); } else { - code_alloc = malloc(str_len); - strcpy(code_alloc, el_p->v.p.ptr); + code = malloc(str_len); + strcpy(code, el_p->v.p.ptr); } - strcat(code_alloc, "\n"); + strcat(code, "\n"); len += str_len + 1; } } - if (len) { - code = code_alloc; - } } if (code == NULL) { @@ -424,6 +418,8 @@ void cmd_chain(void) { return; } + v_free(&var); + int tid_base = create_task("CH_BASE"); int tid_prev = activate_task(tid_base); @@ -431,10 +427,9 @@ void cmd_chain(void) { sys_before_comp(); int success = comp_compile_buffer(code); - v_free(&var); - if (code_alloc) { - free(code_alloc); - } + free(code); + code = NULL; + if (success == 0) { close_task(tid_base); activate_task(tid_prev); @@ -442,6 +437,11 @@ void cmd_chain(void) { return; } + char last_file[OS_PATHNAME_SIZE + 1]; + char bas_dir[OS_PATHNAME_SIZE + 1]; + strcpy(last_file, gsb_last_file); + strcpy(bas_dir, gsb_bas_dir); + int tid_main = brun_create_task("CH_MAIN", ctask->bytecode, 0); exec_sync_variables(0); @@ -452,6 +452,13 @@ void cmd_chain(void) { close_task(tid_base); // cleanup task container activate_task(tid_prev); // resume calling task + // reset globals + gsb_last_line = 0; + gsb_last_error = 0; + strcpy(gsb_last_file, last_file); + strcpy(gsb_bas_dir, bas_dir); + strcpy(gsb_last_errmsg, ""); + if (success == 0) { prog_error = 1; } From bb1053993fb1731f00cba4e91443781932834dbf Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 16 Apr 2016 14:23:47 +1000 Subject: [PATCH 28/29] COMMON: Fixed TLOAD to work correctly with TRY/CATCH --- ChangeLog | 1 + samples/distro-examples/tests/trycatch.bas | 59 ++++++++++++++++++++++ src/common/blib_db.c | 23 +++------ src/common/sberr.c | 48 +++++++++++++----- src/common/sberr.h | 7 ++- 5 files changed, 107 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7c934d42..7718c2b3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -30,6 +30,7 @@ Fixed issues with TRY/CATCH Fixed using POINT to retrieve IMAGE data Fixed issues with CHAIN + Fixed TLOAD to work correctly with TRY/CATCH 2016-02-11 Added export to mobile command (SDL) diff --git a/samples/distro-examples/tests/trycatch.bas b/samples/distro-examples/tests/trycatch.bas index cb34155e..f2aca133 100644 --- a/samples/distro-examples/tests/trycatch.bas +++ b/samples/distro-examples/tests/trycatch.bas @@ -131,3 +131,62 @@ end try if (!caughtError) then throw "Error not caught!!!" endif + +' some more tests from Shain + +Found = 0 + +Try + Tload "__xyz__abc__0123", lines + ? "Error" +Catch err + Found++ +End Try + +Try + Tsave ":foo-name:/~~~", lines + ? "Error" +Catch err + Found++ +End Try + +' catch file error outside sub or function +Try + Open "xyz__xyz__012" For Input As #1 + ? "Error" +Catch err + Found++ +End Try + +' catch file error within function +Func open_safe(filename) + Local fn = Freefile + Try + Open filename For Input As #fn + open_safe = fn + ? "Error!" + Catch err + Found++ + open_safe = 0 + End Try +End Func + +' catch file error within nested function: +Func call_safe(filename) + Local fn + fn = open_safe(filename) + call_safe = fn +End Func + +' catch file error within function: +fn = open_safe("xyz__xyz__012") + +' catch file error within nested functions +fn = call_safe("xyz__xyz__012") + +if (Found != 5) then + throw "Failed: " + Found +endif + + + diff --git a/src/common/blib_db.c b/src/common/blib_db.c index 632bb053..b02dfb4a 100644 --- a/src/common/blib_db.c +++ b/src/common/blib_db.c @@ -551,19 +551,9 @@ void cmd_mkdir() { */ #define LDLN_INC 256 #define GROW_SIZE 1024 -#define BUFMAX 256 - -#define CHK_ERR_CLEANUP(s) \ - if (prog_error) { \ - v_free(&file_name); \ - rt_raise(s); \ - return; \ - } -#define CHK_ERR(s) \ - if (prog_error) { \ - rt_raise(s); \ - return; \ - } +#define BUFMAX 256 +#define CHK_ERR_CLEANUP(s) if (err_handle_error(s, &file_name)) return; +#define CHK_ERR(s) if (err_handle_error(s, NULL)) return; void cmd_floadln() { var_t file_name, *array_p = NULL, *var_p = NULL; @@ -574,6 +564,7 @@ void cmd_floadln() { int eof, eol, bufLen, bufIndex; dword unreadBytes; + err_reset(); if (code_peek() == kwTYPE_SEP) { // "filename" is an already open file number flags = 0; @@ -655,7 +646,7 @@ void cmd_floadln() { unreadBytes -= bufLen; dev_fread(handle, (byte *)buf, bufLen); - if (prog_error) { + if (err_has_error()) { eof = 1; break; } @@ -675,7 +666,8 @@ void cmd_floadln() { } } // read line - if (prog_error) { // clear & exit + if (err_has_error()) { + // clear & exit v_free(array_p); v_init(array_p); break; @@ -724,6 +716,7 @@ void cmd_fsaveln() { int flags = DEV_FILE_OUTPUT; int handle, i; + err_reset(); if (code_peek() == kwTYPE_SEP) { // "filename" is an already open file number flags = 0; diff --git a/src/common/sberr.c b/src/common/sberr.c index 305a5a01..e93cf4bd 100644 --- a/src/common/sberr.c +++ b/src/common/sberr.c @@ -14,6 +14,8 @@ #include #include +int error_caught; + /** * common message handler */ @@ -145,18 +147,6 @@ void err_parm_num(int found, int expected) { rt_raise(ERR_PARAM_NUM, found, expected); } -void err_file(dword code) { - char buf[1024], *p; - - strcpy(buf, strerror(code)); - p = buf; - while (*p) { - *p = to_upper(*p); - p++; - } - err_throw(FSERR_FMT, code, buf); -} - void err_stackoverflow(void) { rt_raise(ERR_STACK_OVERFLOW); } @@ -435,6 +425,7 @@ void err_throw_str(const char *err) { prog_error = 0x80; err_common_msg(WORD_RTE, prog_file, prog_line, err); } + error_caught = caught; } // throw internal error @@ -468,3 +459,36 @@ void cmd_throw() { } } +void err_file(dword code) { + if (!gsb_last_error) { + char *err = malloc(SB_TEXTLINE_SIZE + 1); + sprintf(err, FSERR_FMT, code, strerror(code)); + strupper(err); + err_throw_str(err); + free(err); + } +} + +void err_reset() { + error_caught = 0; +} + +int err_has_error() { + return error_caught || prog_error; +} + +int err_handle_error(const char *err, var_p_t var) { + int result; + if (error_caught == 1) { + result = 1; + } else if (prog_error) { + rt_raise(err); + result = 1; + } else { + result = 0; + } + if (result && var) { + v_free(var); + } + return result; +} diff --git a/src/common/sberr.h b/src/common/sberr.h index 54004a59..ceddee07 100644 --- a/src/common/sberr.h +++ b/src/common/sberr.h @@ -71,14 +71,13 @@ void err_ref_var(); void err_ref_circ_var(); void err_array(); void err_form_input(); - -#define err_type_mismatch() err_typemismatch() - void inf_done(void); void inf_break(int pline); - void err_throw(const char *fmt, ...); void cmd_throw(); +void err_reset(); +int err_handle_error(const char *err, var_p_t var); +int err_has_error(); #if defined(__cplusplus) } From cbdf3aa3977be3ff158fe6f5406ab52a8ab3f0ec Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 17 Apr 2016 09:26:25 +1000 Subject: [PATCH 29/29] COMMON: Fixed XPOS and YPOS to return 0 based values --- ChangeLog | 3 ++- src/common/blib_func.c | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7718c2b3..eae673fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,12 +25,13 @@ Fixed problem with escaped chars using FORMAT Fixed problem with XNOR command result Fixed problem with IMP and EQV command result - Fixed problem with INKEY command + Fixed issues with INKEY command to allow ALT/SHIFT/CTRL states to be returned Fixed capslock handling Fixed issues with TRY/CATCH Fixed using POINT to retrieve IMAGE data Fixed issues with CHAIN Fixed TLOAD to work correctly with TRY/CATCH + Fixed XPOS and YPOS to return 0 based values 2016-02-11 Added export to mobile command (SDL) diff --git a/src/common/blib_func.c b/src/common/blib_func.c index 0380cd9e..1551c3af 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -519,7 +519,6 @@ var_num_t cmd_math1(long funcCode, var_t *arg) { r = atanh(x); #endif break; - case kwSEC: r = 1.0 / cos(x); break; @@ -532,7 +531,6 @@ var_num_t cmd_math1(long funcCode, var_t *arg) { case kwASECH: r = log((1.0 + sqrt(1.0 - x * x)) / x); break; - case kwCSC: r = 1.0 / sin(x); break; @@ -597,18 +595,18 @@ var_num_t cmd_math1(long funcCode, var_t *arg) { r = x; break; case kwXPOS: - if (os_graphics) + if (os_graphics) { r = dev_getx() / dev_textwidth("0"); - else + } else { r = dev_getx(); - r++; + } break; case kwYPOS: - if (os_graphics) + if (os_graphics) { r = dev_gety() / dev_textheight("0"); - else + } else { r = dev_gety(); - r++; + } break; case kwRND: r = ((var_num_t) rand()) / (RAND_MAX + 1.0);