├── project.bat ├── version.bat ├── lib ├── acs │ ├── README.txt │ ├── zcommon.acs │ └── zcommon_acc │ │ └── README.txt ├── zasm.h.bcs ├── zcommon.h.bcs └── zcommon.bcs ├── src ├── version.c ├── gbuf.h ├── codegen │ ├── linear.h │ ├── asm.c │ ├── phase.h │ └── pcode.h ├── cache │ ├── field.h │ ├── cache.h │ ├── field.c │ └── archive.c ├── parse │ ├── token │ │ ├── queue.c │ │ ├── output.c │ │ ├── info.c │ │ └── expr.c │ ├── asm.c │ └── phase.c ├── gbuf.c └── common.h ├── doc ├── details.md ├── readme.txt └── grammar_acs95.txt ├── test ├── jm_header │ ├── auto_fist.h.bcs │ ├── who_exited.h.bcs │ ├── solo_ranker.h.bcs │ ├── team_ranker.h.bcs │ ├── svmz.h.bcs │ ├── timer.h.bcs │ ├── telepatch.h.bcs │ ├── map_message.h.bcs │ ├── client.h.bcs │ ├── config.h.bcs │ ├── rsd.h.bcs │ ├── auto_fist.bcs │ ├── utility.h.bcs │ ├── who_exited.bcs │ ├── jm.h.bcs │ ├── hs.h.bcs │ ├── luk.h.bcs │ ├── timer.bcs │ ├── telepatch.bcs │ ├── client.bcs │ ├── utility.bcs │ ├── hs.bcs │ ├── map_message.bcs │ ├── jm.bcs │ ├── rsd.bcs │ ├── luk.bcs │ └── solo_ranker.bcs ├── parse_fixed.bcs ├── stack.bcs ├── list.bcs ├── bigint.bcs ├── functions.bcs └── sorting.bcs ├── scripts ├── makefiles │ ├── build_x86.mk │ ├── build_x64.mk │ ├── pomake │ │ ├── build_x86.mk │ │ ├── build_x64.mk │ │ └── common.mk │ └── common.mk ├── config.default.php ├── version.php └── project.php ├── LICENSE ├── README.md └── Makefile /project.bat: -------------------------------------------------------------------------------- 1 | @php ./scripts/project.php %* -------------------------------------------------------------------------------- /version.bat: -------------------------------------------------------------------------------- 1 | @php .\scripts\version.php %* -------------------------------------------------------------------------------- /lib/acs/README.txt: -------------------------------------------------------------------------------- 1 | You can place your ACS libraries and include files in this directory. -------------------------------------------------------------------------------- /src/version.c: -------------------------------------------------------------------------------- 1 | // NOTE: This file is automatically generated. 2 | const char* c_version = "0.8.0"; 3 | -------------------------------------------------------------------------------- /lib/zasm.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef LIB_ZASM_H_BCS 2 | #define LIB_ZASM_H_BCS 3 | 4 | #import "zasm.bcs" 5 | 6 | #endif -------------------------------------------------------------------------------- /doc/details.md: -------------------------------------------------------------------------------- 1 | The content of the details file was moved to the [wiki](https://github.com/wormt/bcc/wiki). 2 | -------------------------------------------------------------------------------- /test/jm_header/auto_fist.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_AUTOFIST_H 2 | #define TEST_JMHEADER_AUTOFIST_H 3 | 4 | #linklibrary "autofist" 5 | 6 | #endif -------------------------------------------------------------------------------- /lib/acs/zcommon.acs: -------------------------------------------------------------------------------- 1 | // BCC zcommon ACS files: 2 | #include "zcommon/master.acs" 3 | 4 | // ACC zcommon ACS files: 5 | // Comment out the BCC #include, then uncomment the following #include. 6 | // #include "zcommon_acc/zcommon.acs" 7 | -------------------------------------------------------------------------------- /lib/zcommon.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef LIB_ZCOMMON_H_BCS 2 | #define LIB_ZCOMMON_H_BCS 3 | 4 | #if defined( __ZANDRONUM__ ) 5 | #import "zcommon/zandronum.bcs" 6 | #elif defined( __GZDOOM__ ) 7 | #import "zcommon/gzdoom.bcs" 8 | #else 9 | #import "zcommon/master.bcs" 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /lib/zcommon.bcs: -------------------------------------------------------------------------------- 1 | // Because there is a "zcommon.acs" file, users might try to also include a 2 | // "zcommon.bcs" file, but we do not use such a file. So create the file and 3 | // let the user know of a real file. 4 | #error the "zcommon.bcs" file should not be included, \ 5 | try including "zcommon.h" instead 6 | -------------------------------------------------------------------------------- /lib/acs/zcommon_acc/README.txt: -------------------------------------------------------------------------------- 1 | This folder is meant to hold the zcommon ACS files provided by ACC. BCC 2 | provides its own zcommon ACS files, but if you don't want to use them, you can 3 | use the zcommon ACS files provided by ACC. Place the zcommon.acs, zdefs.acs, 4 | zspecial.acs, and zwvars.acs files from the ACC compiler in this folder, then 5 | edit the zcommon.acs file in the parent folder. 6 | -------------------------------------------------------------------------------- /test/jm_header/who_exited.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_WHOEXITED_H 2 | #define TEST_JMHEADER_WHOEXITED_H 3 | 4 | #linklibrary "whoexit" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.WhoExited { 8 | // ========================================================================== 9 | 10 | extern void Show(); 11 | extern void ShowAndExit(); 12 | 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /test/jm_header/solo_ranker.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_SOLORANKER_H 2 | #define TEST_JMHEADER_SOLORANKER_H 3 | 4 | #linklibrary "solorank" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.SoloRanker { 8 | // ========================================================================== 9 | 10 | extern void Finish(); 11 | extern str GetHsAuthorName(); 12 | 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /test/jm_header/team_ranker.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_TEAMRANKER_H 2 | #define TEST_JMHEADER_TEAMRANKER_H 3 | 4 | #linklibrary "teamrank" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.TeamRanker { 8 | // ========================================================================== 9 | 10 | extern void ClearPointsTable(); 11 | extern void PrepareForSvmz(); 12 | 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /test/jm_header/svmz.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_SVMZ_H 2 | #define TEST_JMHEADER_SVMZ_H 3 | 4 | #linklibrary "svmz" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Svmz { 8 | // ========================================================================== 9 | 10 | extern bool Is(); 11 | extern bool Init(); 12 | extern void MakeEscapee(); 13 | extern bool ValidateExit(); 14 | 15 | } 16 | 17 | #endif -------------------------------------------------------------------------------- /test/jm_header/timer.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_TIMER_H 2 | #define TEST_JMHEADER_TIMER_H 3 | 4 | #linklibrary "timer" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Timer { 8 | // ========================================================================== 9 | 10 | extern void Run(); 11 | extern void SetPar( str par ); 12 | extern void ShowTime( int time ); 13 | extern int GetTics(); 14 | 15 | } 16 | 17 | #endif -------------------------------------------------------------------------------- /test/jm_header/telepatch.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_TELEPATCH_H 2 | #define TEST_JMHEADER_TELEPATCH_H 3 | 4 | #linklibrary "telpatch" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Telepatch { 8 | // ========================================================================== 9 | 10 | extern void OnDeath( int player ); 11 | extern void OnRespawn( int player ); 12 | extern void OnDisconnect( int player ); 13 | 14 | } 15 | 16 | #endif -------------------------------------------------------------------------------- /doc/readme.txt: -------------------------------------------------------------------------------- 1 | Project page: 2 | https://github.com/wormt/bcc 3 | 4 | Overview of features: 5 | https://github.com/wormt/bcc/blob/bcs/doc/details.md 6 | 7 | Version: 0.8.0 8 | ============================================================================= 9 | 10 | Known issues in this release: 11 | - File path of cached library file now shown in diagnostic messages. 12 | - memcpy() breaks when both the source and destination operands are array 13 | references. 14 | - memcpy() of global and world arrays may cause the game to crash. 15 | -------------------------------------------------------------------------------- /scripts/makefiles/build_x86.mk: -------------------------------------------------------------------------------- 1 | # bcc Makefile. 2 | 3 | EXE=bcc.exe 4 | BUILD_DIR=build/x86 5 | VERSION_FILE=$(BUILD_DIR)/version.c 6 | 7 | CC = pocc 8 | INCLUDE = \ 9 | -I"$(PELLESC_DIR)\Include\Win" \ 10 | -I"$(PELLESC_DIR)\Include" \ 11 | -Isrc -Isrc/parse 12 | PELLESC_DIR = C:\Program Files\PellesC 13 | 14 | CCFLAGS = -Tx86-coff -Ot -Ze -std:C99 15 | OPTIONS=$(CCFLAGS) $(INCLUDE) 16 | LIB = \ 17 | -LIBPATH:"$(PELLESC_DIR)\Lib\Win" \ 18 | -LIBPATH:"$(PELLESC_DIR)\Lib" 19 | LINKFLAGS = -subsystem:console -machine:x86 -debug -debugtype:coff 20 | 21 | include common.mk 22 | -------------------------------------------------------------------------------- /scripts/makefiles/build_x64.mk: -------------------------------------------------------------------------------- 1 | # bcc Makefile. 2 | 3 | EXE=bcc.exe 4 | BUILD_DIR=build/x64 5 | VERSION_FILE=$(BUILD_DIR)/version.c 6 | 7 | CC = pocc 8 | INCLUDE = \ 9 | -I"$(PELLESC_DIR)\Include\Win" \ 10 | -I"$(PELLESC_DIR)\Include" \ 11 | -Isrc -Isrc/parse 12 | PELLESC_DIR = C:\Program Files\PellesC 13 | 14 | CCFLAGS = -Tx64-coff -Ot -Ze -std:C99 15 | OPTIONS=$(CCFLAGS) $(INCLUDE) 16 | LIB = \ 17 | -LIBPATH:"$(PELLESC_DIR)\Lib\Win64" \ 18 | -LIBPATH:"$(PELLESC_DIR)\Lib" 19 | LINKFLAGS = -subsystem:console -machine:x64 -debug -debugtype:coff 20 | 21 | include common.mk 22 | -------------------------------------------------------------------------------- /test/jm_header/map_message.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_MAPMESSAGE_H 2 | #define TEST_JMHEADER_MAPMESSAGE_H 3 | 4 | #linklibrary "mapmsg" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.MapMsg { 8 | // ========================================================================== 9 | 10 | enum Skill { 11 | SKILL_VARIED, 12 | SKILL_VERYEASY, 13 | SKILL_EASY, 14 | SKILL_MODERATE, 15 | SKILL_HARD, 16 | SKILL_VERYHARD, 17 | SKILL_TOTAL 18 | }; 19 | 20 | extern void Set( str name, str author, enum Skill skill, str par ); 21 | 22 | } 23 | 24 | #endif -------------------------------------------------------------------------------- /scripts/makefiles/pomake/build_x86.mk: -------------------------------------------------------------------------------- 1 | # bcc Makefile, for pomake. 2 | 3 | EXE=bcc.exe 4 | BUILD_DIR=build/x86 5 | VERSION_FILE=$(BUILD_DIR)/version.c 6 | 7 | CC = pocc 8 | INCLUDE = \ 9 | -I"$(PELLESC_DIR)\Include\Win" \ 10 | -I"$(PELLESC_DIR)\Include" \ 11 | -Isrc -Isrc/parse 12 | PELLESC_DIR = C:\Program Files\PellesC 13 | 14 | CCFLAGS = -Tx86-coff -Ot -Ze -std:C99 15 | OPTIONS=$(CCFLAGS) $(INCLUDE) 16 | LIB = \ 17 | -LIBPATH:"$(PELLESC_DIR)\Lib\Win" \ 18 | -LIBPATH:"$(PELLESC_DIR)\Lib" 19 | LINKFLAGS = -subsystem:console -machine:x86 -debug -debugtype:coff 20 | 21 | !include common.mk 22 | -------------------------------------------------------------------------------- /scripts/makefiles/pomake/build_x64.mk: -------------------------------------------------------------------------------- 1 | # bcc Makefile, for pomake. 2 | 3 | EXE=bcc.exe 4 | BUILD_DIR=build/x64 5 | VERSION_FILE=$(BUILD_DIR)/version.c 6 | 7 | CC = pocc 8 | INCLUDE = \ 9 | -I"$(PELLESC_DIR)\Include\Win" \ 10 | -I"$(PELLESC_DIR)\Include" \ 11 | -Isrc -Isrc/parse 12 | PELLESC_DIR = C:\Program Files\PellesC 13 | 14 | CCFLAGS = -Tx64-coff -Ot -Ze -std:C99 15 | OPTIONS=$(CCFLAGS) $(INCLUDE) 16 | LIB = \ 17 | -LIBPATH:"$(PELLESC_DIR)\Lib\Win64" \ 18 | -LIBPATH:"$(PELLESC_DIR)\Lib" 19 | LINKFLAGS = -subsystem:console -machine:x64 -debug -debugtype:coff 20 | 21 | !include common.mk 22 | -------------------------------------------------------------------------------- /scripts/config.default.php: -------------------------------------------------------------------------------- 1 | make = MAKE_GNU; 9 | 10 | // Option: strip_exe 11 | // Description: run the `strip` command on the generated executable. This 12 | // command will remove debug information from the executable, and therefore 13 | // reduce the size of the executable. 14 | // Available values: 15 | // - true: execute the strip command. 16 | // - false: do not execute the strip command. 17 | $config->strip_exe = false; 18 | -------------------------------------------------------------------------------- /test/jm_header/client.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_CLIENT_H 2 | #define TEST_JMHEADER_CLIENT_H 3 | 4 | #linklibrary "client" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Client { 8 | // ========================================================================== 9 | 10 | extern int GetNumber(); 11 | // Executes client-side script for player with given player number. 12 | extern void Execute( int player, int number ); 13 | // Same as ClientExecute(), but allows including script arguments. Currently, 14 | // only one script argument is supported. 15 | extern void ExecuteArg( int player, int number, int arg1 ); 16 | 17 | } 18 | 19 | #endif -------------------------------------------------------------------------------- /test/jm_header/config.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_CONFIG_H 2 | #define TEST_JMHEADER_CONFIG_H 3 | 4 | // ========================================================================== 5 | strict namespace Jm.Cfg { 6 | // ========================================================================== 7 | 8 | enum { TICS_IN_SECOND = 35 }; 9 | enum { MAX_PLAYERS = 32 }; 10 | enum { VALUE_NONE = -1 }; 11 | // Looking at the screen, in Doom Builder 2, left is West and right is East. 12 | enum { 13 | ANGLE_EAST = 0, 14 | ANGLE_NORTHEAST = 32, 15 | ANGLE_NORTH = 64, 16 | ANGLE_NORTHWEST = 96, 17 | ANGLE_WEST = 128, 18 | ANGLE_SOUTHWEST = 160, 19 | ANGLE_SOUTH = 192, 20 | ANGLE_SOUTHEAST = 224, 21 | }; 22 | 23 | } 24 | 25 | #endif -------------------------------------------------------------------------------- /src/gbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_GBUF 2 | #define SRC_GBUF 3 | 4 | /* 5 | 6 | Growing buffer. 7 | 8 | */ 9 | 10 | #include 11 | 12 | enum { GBUF_SEGMENT_SIZE = 65536 }; 13 | 14 | // Segment. 15 | struct gbuf_seg { 16 | struct gbuf_seg* next; 17 | char data[ GBUF_SEGMENT_SIZE ]; 18 | int used; 19 | int pos; 20 | }; 21 | 22 | // Growing buffer. 23 | struct gbuf { 24 | struct gbuf_seg* head_segment; 25 | struct gbuf_seg* segment; 26 | }; 27 | 28 | void gbuf_init( struct gbuf* buffer ); 29 | void gbuf_write( struct gbuf* buffer, const void* data, int length ); 30 | char* gbuf_alloc_block( struct gbuf* buffer ); 31 | void gbuf_reset( struct gbuf* buffer ); 32 | bool gbuf_save( struct gbuf* buffer, const char* file_path ); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /test/jm_header/rsd.h.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #linklibrary "rsd" 4 | 5 | // Resolution Sensitive Display is a client-side message display system that 6 | // breaks down a long string message into smaller segments that fit nicely on 7 | // a player's screens and shows them one at a time. 8 | // ========================================================================== 9 | strict namespace Jm.Rsd { 10 | // ========================================================================== 11 | 12 | enum { RSD_START_SCRIPT = 945 }; 13 | 14 | extern void RsdSetPadding( int padding ); 15 | // Set to 0 to remove maximum restriction. 16 | extern void RsdSetMaxLineSize( int size ); 17 | // Adds a number onto the arguments queue. 18 | extern void RsdAddNumArg( int num ); 19 | extern void RsdAddStrArg( str value ); 20 | 21 | } 22 | 23 | #endif -------------------------------------------------------------------------------- /test/jm_header/auto_fist.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "autofist" 4 | 5 | #include "zcommon.h" 6 | #include "auto_fist.h" 7 | #include "client.h" 8 | 9 | // ========================================================================== 10 | strict namespace Jm.AutoFist { 11 | // ========================================================================== 12 | 13 | script 995 enter { 14 | Acs_ExecuteAlways( 996, 0 ); 15 | } 16 | 17 | script 996 respawn { 18 | // Small delay to make sure the clearInventory() script has finished 19 | // executing. I don't like this solution but it will do for now. 20 | Delay( 2 ); 21 | Client.Execute( PlayerNumber(), 997 ); 22 | } 23 | 24 | script 997 { 25 | if ( GetCvar( "jm_auto_fist" ) ) { 26 | ConsoleCommand( "use fist" ); 27 | } 28 | } 29 | 30 | } 31 | 32 | #endif -------------------------------------------------------------------------------- /test/jm_header/utility.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_UTILITY_H 2 | #define TEST_JMHEADER_UTILITY_H 3 | 4 | #linklibrary "utility" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Utility { 8 | // ========================================================================== 9 | 10 | extern void Init(); 11 | extern bool IsOnline(); 12 | // Convert a par time string into seconds. 13 | extern int ParToSeconds( str par ); 14 | extern str ZeroPad( int number ); 15 | extern str OrdinalSuffix( int value ); 16 | extern void ClearMessage( int id ); 17 | extern void ClearMessageBold( int id ); 18 | // Function to calculate the centiseconds from any remaining tics after 19 | // subtracting full seconds from the total tics. 20 | extern int CalCseconds( int tics ); 21 | extern str GetMonthName( int month ); 22 | 23 | } 24 | 25 | #endif -------------------------------------------------------------------------------- /test/jm_header/who_exited.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "whoexit" 4 | 5 | #include "zcommon.h" 6 | #include "utility.h" 7 | #include "who_exited.h" 8 | 9 | // ========================================================================== 10 | strict namespace Jm.WhoExited { 11 | // ========================================================================== 12 | 13 | void Show() { 14 | HudMessageBold( 15 | s: "\cc", 16 | n: PlayerNumber() + 1, 17 | s: "\cg exited the level"; 18 | HUDMSG_FADEINOUT | HUDMSG_LOG, 19 | // Avoid displaying the message on screen by setting a long fade-in time. 20 | 0, 0, 0.0, 0.0, 1.0, 1000.0, 0.0 ); 21 | } 22 | 23 | void ShowAndExit() { 24 | // Only show message in multi-player because it's already shown in 25 | // single-player. 26 | if ( Utility.IsOnline() ) { 27 | Show(); 28 | } 29 | Exit_Normal( 0 ); 30 | } 31 | 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /test/jm_header/jm.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_JM_H 2 | #define TEST_JMHEADER_JM_H 3 | 4 | #linklibrary "jm" 5 | 6 | // ========================================================================== 7 | strict namespace Jm { 8 | // ========================================================================== 9 | 10 | enum Flag { 11 | F_NONE = 0b0, 12 | F_MANUALTIMER = 0b1, 13 | }; 14 | 15 | extern enum MapType { 16 | MAP_SOLO, 17 | MAP_TEAM, 18 | } gMapType; 19 | 20 | extern void InitSolo( str par ); 21 | extern void InitTeam( str par, int points ); 22 | extern void EnableFlag( int flag ); 23 | extern void DisableFlags( int flag ); 24 | extern void Run(); 25 | extern void RunSvmz( str mapRestartCommand ); 26 | extern void StartTimer(); 27 | extern void SetMapMessage( str name, str author, 28 | enum Jm.MapMsg.Skill skill ); 29 | extern void AddPoint(); 30 | extern void FinishLine(); 31 | extern void DisallowHs( int player ); 32 | extern void Exit(); 33 | extern bool IsValidExit(); 34 | 35 | } 36 | 37 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Daniel Baimiachkine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/jm_header/hs.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_HS_H 2 | #define TEST_JMHEADER_HS_H 3 | 4 | #linklibrary "hs" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Hs { 8 | // ========================================================================== 9 | 10 | enum { 11 | STATUS_NONE, 12 | STATUS_LOADED, 13 | STATUS_WORKING, 14 | STATUS_ERROR 15 | }; 16 | 17 | enum { LINE_NOTICE = 27233 }; 18 | 19 | extern int gLoadStatus; 20 | // Stores a ranker's high score load script number so other files can wait 21 | // for the ranker to complete loading the high score. 22 | extern str gLoadScript; 23 | // Script of the ranker that shows the high score details table. 24 | extern str gDetailsScript; 25 | 26 | extern bool IsAutoShowDetailsTable(); 27 | extern bool IsCheater( int player ); 28 | extern bool IsCheatsEnabled(); 29 | extern bool IsEnabled(); 30 | extern void Disable(); 31 | extern int GetFinishTime(); 32 | extern void SetFinishTime( int tics ); 33 | extern bool IsSet(); 34 | extern bool IsNew( int time ); 35 | extern void Disallow( int player ); 36 | // Shows high score details table. 37 | extern void ShowDetails( int showTime ); 38 | 39 | } 40 | 41 | #endif -------------------------------------------------------------------------------- /src/codegen/linear.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CODEGEN_LINEAR_H 2 | #define SRC_CODEGEN_LINEAR_H 3 | 4 | struct c_node { 5 | struct c_node* next; 6 | enum { 7 | C_NODE_POINT, 8 | C_NODE_JUMP, 9 | C_NODE_CASEJUMP, 10 | C_NODE_SORTEDCASEJUMP, 11 | C_NODE_PCODE, 12 | C_NODE_TOTAL 13 | } type; 14 | }; 15 | 16 | struct c_point { 17 | struct c_node node; 18 | int obj_pos; 19 | }; 20 | 21 | struct c_jump { 22 | struct c_node node; 23 | struct c_point* point; 24 | struct c_jump* next; 25 | int opcode; 26 | int obj_pos; 27 | }; 28 | 29 | struct c_casejump { 30 | struct c_node node; 31 | struct c_casejump* next; 32 | struct c_point* point; 33 | int value; 34 | int obj_pos; 35 | }; 36 | 37 | struct c_sortedcasejump { 38 | struct c_node node; 39 | struct c_casejump* head; 40 | struct c_casejump* tail; 41 | int count; 42 | int obj_pos; 43 | }; 44 | 45 | struct c_pcode { 46 | struct c_node node; 47 | int code; 48 | struct c_pcode_arg* args; 49 | int obj_pos; 50 | bool optimize; 51 | bool patch; 52 | }; 53 | 54 | struct c_pcode_arg { 55 | struct c_pcode_arg* next; 56 | struct c_point* point; 57 | int value; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bcc is a BCS, ACS, and ACS95 compiler. 2 | 3 | ## Supported Scripting Languages 4 | 5 | bcc can compile source code written in the following languages. 6 | 7 | ### BCS 8 | 9 | ``` 10 | strict namespace SampleCode { 11 | script "Main" open { 12 | static str basket[] = { "apples", "oranges", "pears" }; 13 | foreach ( auto fruit; basket ) { 14 | Print( s: "I love ", s: fruit, s: ( fruit == "oranges" ) ? 15 | " very much" : "" ); 16 | } 17 | } 18 | } 19 | ``` 20 | 21 | BCS is an extension of ACS. BCS is mostly compatible with ACS and provides many interesting and useful features, including the following: 22 | 23 | * Structures 24 | * Enumerations 25 | * Namespaces 26 | * Preprocessor 27 | * Strong types 28 | * Block scoping 29 | * Optional function parameters 30 | * `&&` and `||` operators are short-circuited 31 | * `foreach` loop 32 | * `?:` operator 33 | 34 | See the [wiki](https://github.com/wormt/bcc/wiki) for an overview of the features. 35 | 36 | ### ACS/ACS95 37 | bcc can also compile ACS and ACS95 code. ACS95 is the ACS scripting language that was used for scripting Hexen. The name, ACS95, is invented by bcc to distinguish between the two languages. 38 | -------------------------------------------------------------------------------- /test/jm_header/luk.h.bcs: -------------------------------------------------------------------------------- 1 | #ifndef TEST_JMHEADER_LUK_H 2 | #define TEST_JMHEADER_LUK_H 3 | 4 | #linklibrary "luk" 5 | 6 | // ========================================================================== 7 | strict namespace Jm.Luk { 8 | // ========================================================================== 9 | 10 | // RETRIEVE query types. 11 | enum { 12 | RETQUERY_INT, 13 | RETQUERY_STR, 14 | RETQUERY_DATE 15 | }; 16 | // Query execution results we will tell the user. 17 | enum { 18 | RESULT_OK, 19 | RESULT_FAILED, 20 | RESULT_TIMEOUT, 21 | RESULT_UNKNOWN, 22 | }; 23 | // Maximum size of a string that we will recieve. 24 | enum { MAX_STR_VALUE_SIZE = 100 }; 25 | // To help with readability when calling the retrieval scripts, we will create 26 | // a descriptive constant that contains the script number. 27 | enum { RETSCRIPT = 970 }; 28 | enum { LRETSCRIPT = 975 }; 29 | 30 | extern int gString[ MAX_STR_VALUE_SIZE ]; 31 | extern int gData; 32 | extern int gYear; 33 | extern int gMonth; 34 | extern int gDay; 35 | extern int gQueryResult; 36 | 37 | extern bool IsEnabled(); 38 | extern int StoreInt( str key, int value ); 39 | extern int StoreStr( str key, str value ); 40 | extern int StoreName( str key, int playerNum ); 41 | extern int StoreDate( str key ); 42 | 43 | } 44 | 45 | #endif -------------------------------------------------------------------------------- /src/cache/field.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CACHE_FIELD_H 2 | #define SRC_CACHE_FIELD_H 3 | 4 | #include 5 | 6 | // Writer 7 | // ========================================================================== 8 | 9 | struct field_writer { 10 | struct gbuf* output; 11 | }; 12 | 13 | void f_init_writer( struct field_writer* writer, struct gbuf* buffer ); 14 | void f_wf( struct field_writer* writer, char field ); 15 | void f_wv( struct field_writer* writer, char field, void* value, 16 | size_t value_length ); 17 | void f_ws( struct field_writer* writer, char field, const char* value ); 18 | 19 | // Reader 20 | // ========================================================================== 21 | 22 | struct field_reader { 23 | jmp_buf* bail; 24 | const char* data; 25 | enum { 26 | FIELDRERR_NONE, 27 | FIELDRERR_UNEXPECTEDFIELD, 28 | } err; 29 | char field; 30 | char expected_field; 31 | }; 32 | 33 | void f_init_reader( struct field_reader* reader, jmp_buf* bail, 34 | const char* data ); 35 | void f_rf( struct field_reader* reader, char expected_field ); 36 | void f_rv( struct field_reader* reader, char field, void* value, 37 | size_t value_length ); 38 | const char* f_rs( struct field_reader* reader, char field ); 39 | char f_peek( struct field_reader* reader ); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/parse/token/queue.c: -------------------------------------------------------------------------------- 1 | #include "phase.h" 2 | 3 | void p_init_token_queue( struct token_queue* queue, bool stream ) { 4 | queue->head = NULL; 5 | queue->tail = NULL; 6 | queue->prev_entry = NULL; 7 | queue->size = 0; 8 | queue->stream = stream; 9 | } 10 | 11 | struct queue_entry* p_push_entry( struct parse* parse, 12 | struct token_queue* queue ) { 13 | // Allocate an entry. 14 | struct queue_entry* entry; 15 | if ( parse->tkque_free_entry ) { 16 | entry = parse->tkque_free_entry; 17 | parse->tkque_free_entry = entry->next; 18 | } 19 | else { 20 | entry = mem_alloc( sizeof( *entry ) ); 21 | } 22 | // Initialize necessary fields of the entry. 23 | entry->next = NULL; 24 | entry->token_allocated = false; 25 | // Append the entry. 26 | if ( queue->head ) { 27 | queue->tail->next = entry; 28 | } 29 | else { 30 | queue->head = entry; 31 | } 32 | queue->tail = entry; 33 | ++queue->size; 34 | return entry; 35 | } 36 | 37 | struct token* p_shift_entry( struct parse* parse, 38 | struct token_queue* queue ) { 39 | // Free previous entry. 40 | if ( queue->prev_entry ) { 41 | struct queue_entry* entry = queue->prev_entry; 42 | entry->next = parse->tkque_free_entry; 43 | parse->tkque_free_entry = entry; 44 | if ( entry->token_allocated ) { 45 | p_free_token( parse, entry->token ); 46 | } 47 | } 48 | struct queue_entry* entry = queue->head; 49 | queue->head = entry->next; 50 | queue->prev_entry = entry; 51 | --queue->size; 52 | return entry->token; 53 | } 54 | -------------------------------------------------------------------------------- /test/jm_header/timer.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "timer" 4 | 5 | #include "zcommon.h" 6 | #include "config.h" 7 | #include "timer.h" 8 | 9 | // ========================================================================== 10 | strict namespace Jm.Timer { 11 | // ========================================================================== 12 | 13 | using Upmost; 14 | using Jm: Cfg; 15 | 16 | private str gPar = ""; 17 | // Number of tics that passed before the timer is started. This is used to 18 | // adjust the finish times when the timer is started manually. 19 | private int gSkipped = 0; 20 | 21 | void Run() { 22 | gSkipped = Timer(); 23 | Acs_NamedExecute( "Timer.Start", 0 ); 24 | } 25 | 26 | void SetPar( str par ) { 27 | gPar = par; 28 | } 29 | 30 | script "Timer.Start" { 31 | auto time = 0; 32 | while ( true ) { 33 | ShowTime( time ); 34 | ++time; 35 | Delay( Cfg.TICS_IN_SECOND ); 36 | } 37 | } 38 | 39 | void ShowTime( int time ) { 40 | SetFont( "BIGFONT" ); 41 | HudMessage( 42 | d: time / 60, s: " : ", 43 | d: time % 60 / 10, s: " ", 44 | d: time % 10; 45 | HUDMSG_PLAIN, 985, CR_BLUE, 0.95, 0.95, 0.0 ); 46 | } 47 | 48 | // Show par. 49 | script "Timer.ClientShow" open clientside { 50 | Delay( 2 ); 51 | ConsoleCommand( "pukename \"Timer.Show\"" ); 52 | } 53 | 54 | script "Timer.Show" net { 55 | if ( gPar != "" ) { 56 | SetFont( "BIGFONT" ); 57 | HudMessage( s: gPar; HUDMSG_PLAIN, 986, CR_RED, 0.95, 0.90, 0.0 ); 58 | } 59 | } 60 | 61 | int GetTics() { 62 | return Timer() - gSkipped; 63 | } 64 | 65 | } 66 | 67 | #endif -------------------------------------------------------------------------------- /test/jm_header/telepatch.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "telpatch" 4 | 5 | #include "zcommon.h" 6 | #include "config.h" 7 | #include "telepatch.h" 8 | 9 | // ========================================================================== 10 | strict namespace Jm.Telepatch { 11 | // ========================================================================== 12 | 13 | using Cfg; 14 | 15 | // Holds TIDs of temporary teleport destinations. 16 | private int gDest[ MAX_PLAYERS ]; 17 | 18 | void OnDeath( int player ) { 19 | // Change the activator of the script to the killer of the dead player to 20 | // determine if the player got telefragged. 21 | SetActivatorToTarget( 0 ); 22 | if ( PlayerNumber() >= 0 && PlayerNumber() != player ) { 23 | // Create a TID for a temporary teleport destination. 24 | enum { TID_OFFSET = 28100 }; 25 | gDest[ player ] = TID_OFFSET + player; 26 | // Spawn the teleport destination at killer's coordinates. 27 | Spawn( "TeleportDest2", GetActorX( 0 ), 28 | GetActorY( 0 ), GetActorZ( 0 ), gDest[ player ], 29 | ( int ) GetActorAngle( 0 ) >> 8 ); 30 | } 31 | } 32 | 33 | void OnRespawn( int player ) { 34 | if ( gDest[ player ] != 0 ) { 35 | Teleport( gDest[ player ], 0, 0 ); 36 | OnDisconnect( player ); 37 | } 38 | } 39 | 40 | // Remove any temporary destinations that might have been left behind if a 41 | // player disconnected while being dead. 42 | void OnDisconnect( int player ) { 43 | if ( gDest[ player ] != 0 ) { 44 | Thing_Remove( gDest[ player ] ); 45 | gDest[ player ] = 0; 46 | } 47 | } 48 | 49 | } 50 | 51 | #endif -------------------------------------------------------------------------------- /test/jm_header/client.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "client" 4 | 5 | #include "zcommon.h" 6 | #include "client.h" 7 | 8 | // ========================================================================== 9 | strict namespace Jm.Client { 10 | // ========================================================================== 11 | 12 | // Player number retrival. 13 | // Thanks to Euranna for the solution and helping me understand it. 14 | // Source: http://www.skulltag.com/forum/viewtopic.php?p=310483#p310483 15 | 16 | // -2 indicates the library has not been initialized. 17 | private int gNumber = -2; 18 | 19 | script 940 open clientside { 20 | // Force player to execute a script upon connecting to the server. Offline, 21 | // this script takes more than one tic to execute, so any ENTER scripts 22 | // should be aware of this behavior. 23 | ConsoleCommand( "puke 941" ); 24 | } 25 | 26 | script 941 net clientside { 27 | // Player should be the activator of this script. We can now save their 28 | // player number for later comparisons. 29 | gNumber = PlayerNumber(); 30 | } 31 | 32 | int GetNumber() { 33 | return gNumber; 34 | } 35 | 36 | void Execute( int player, int number ) { 37 | ExecuteArg( player, number, 0 ); 38 | } 39 | 40 | void ExecuteArg( int player, int number, int arg1 ) { 41 | Acs_Execute( 943, 0, player, number, arg1 ); 42 | } 43 | 44 | // NOTE: It's possible to combine the player and scriptNum variables into 45 | // a single variable so we can allow for an extra script argument. 46 | script 943 ( int player, int number, int arg1 ) clientside { 47 | if ( player == gNumber ) { 48 | Acs_Execute( number, 0, arg1 ); 49 | } 50 | } 51 | 52 | } 53 | 54 | #endif -------------------------------------------------------------------------------- /src/cache/cache.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CACHE_CACHE_H 2 | #define SRC_CACHE_CACHE_H 3 | 4 | #include "task.h" 5 | #include "gbuf.h" 6 | #include "field.h" 7 | 8 | struct cache_entry { 9 | struct cache_entry* next; 10 | struct cache_dependency* dependency; 11 | struct cache_dependency* dependency_tail; 12 | struct library* lib; 13 | struct str path; 14 | time_t compile_time; 15 | int id; 16 | bool modified; 17 | }; 18 | 19 | struct cache_dependency { 20 | struct cache_dependency* next; 21 | struct str path; 22 | time_t mtime; 23 | }; 24 | 25 | struct cache_entry_list { 26 | struct cache_entry* head; 27 | struct cache_entry* tail; 28 | }; 29 | 30 | struct cache { 31 | struct task* task; 32 | struct str dir_path; 33 | struct str header_id; 34 | struct cache_entry_list entries; 35 | struct cache_entry_list removed_entries; 36 | struct cache_dependency* free_dependencies; 37 | struct gbuf* buffer; 38 | int lifetime; 39 | }; 40 | 41 | void cache_init( struct cache* cache, struct task* task ); 42 | void cache_load( struct cache* cache ); 43 | void cache_add( struct cache* cache, struct library* lib ); 44 | struct library* cache_get( struct cache* cache, struct file_entry* file ); 45 | void cache_close( struct cache* cache ); 46 | struct cache_entry* cache_alloc_entry( void ); 47 | void cache_append_entry( struct cache_entry_list* entries, 48 | struct cache_entry* entry ); 49 | struct cache_dependency* cache_alloc_dependency( struct cache* cache, 50 | const char* path ); 51 | void cache_append_dependency( struct cache_entry* entry, 52 | struct cache_dependency* dep ); 53 | void cache_clear( struct cache* cache ); 54 | void cache_save_archive( struct cache* cache, struct field_writer* writer ); 55 | void cache_restore_archive( struct cache* cache, 56 | struct field_reader* reader ); 57 | void cache_save_lib( struct task* task, struct field_writer* writer, 58 | struct library* lib ); 59 | struct library* cache_restore_lib( struct cache* cache, 60 | struct field_reader* reader ); 61 | void cache_print( struct cache* cache ); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/cache/field.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gbuf.h" 4 | #include "field.h" 5 | 6 | // Writer 7 | // ========================================================================== 8 | 9 | void f_init_writer( struct field_writer* writer, struct gbuf* buffer ) { 10 | writer->output = buffer; 11 | } 12 | 13 | void f_wf( struct field_writer* writer, char field ) { 14 | gbuf_write( writer->output, &field, sizeof( field ) ); 15 | } 16 | 17 | void f_wv( struct field_writer* writer, char field, void* value, 18 | size_t value_length ) { 19 | f_wf( writer, field ); 20 | gbuf_write( writer->output, value, value_length ); 21 | } 22 | 23 | void f_ws( struct field_writer* writer, char field, const char* value ) { 24 | f_wf( writer, field ); 25 | gbuf_write( writer->output, value, strlen( value ) + 1 ); 26 | } 27 | 28 | // Reader 29 | // ========================================================================== 30 | 31 | void f_init_reader( struct field_reader* reader, jmp_buf* bail, 32 | const char* data ) { 33 | reader->bail = bail; 34 | reader->data = data; 35 | reader->err = FIELDRERR_NONE; 36 | reader->field = 0; 37 | reader->expected_field = 0; 38 | } 39 | 40 | void f_rf( struct field_reader* reader, char expected_field ) { 41 | char field; 42 | memcpy( &field, reader->data, sizeof( field ) ); 43 | reader->data += sizeof( field ); 44 | if ( field != expected_field ) { 45 | reader->err = FIELDRERR_UNEXPECTEDFIELD; 46 | reader->field = field; 47 | reader->expected_field = expected_field; 48 | longjmp( *reader->bail, 1 ); 49 | } 50 | } 51 | 52 | void f_rv( struct field_reader* reader, char field, void* value, 53 | size_t value_length ) { 54 | f_rf( reader, field ); 55 | memcpy( value, reader->data, value_length ); 56 | reader->data += value_length; 57 | } 58 | 59 | 60 | const char* f_rs( struct field_reader* reader, char field ) { 61 | f_rf( reader, field ); 62 | const char* value = reader->data; 63 | while ( *reader->data ) { 64 | ++reader->data; 65 | } 66 | ++reader->data; 67 | return value; 68 | } 69 | 70 | char f_peek( struct field_reader* reader ) { 71 | char field; 72 | memcpy( &field, reader->data, sizeof( field ) ); 73 | return field; 74 | } 75 | -------------------------------------------------------------------------------- /test/parse_fixed.bcs: -------------------------------------------------------------------------------- 1 | 2 | // ========================================================================== 3 | strict namespace Math { 4 | // ========================================================================== 5 | 6 | struct { 7 | enum { 8 | ERR_NONE, 9 | ERR_ILLFORMED, 10 | ERR_OVERFLOW, 11 | } err; 12 | fixed value; 13 | } gResult; 14 | 15 | void ParseFixed( str number ) { 16 | int i = 0; 17 | int value = 0; 18 | // Sign. 19 | int sign = 1; 20 | int ch = number[ i ]; 21 | if ( ch == '-' ) { 22 | sign = -1; 23 | ++i; 24 | } 25 | else if ( ch == '+' ) { 26 | ++i; 27 | } 28 | // Integral part. 29 | while ( ( ch = number[ i ] ) >= '0' && ch <= '9' ) { 30 | value *= 10; 31 | value += ch - '0'; 32 | if ( ( sign == 1 && value > 32767 ) || value > 32768 ) { 33 | gResult.err = ERR_OVERFLOW; 34 | gResult.value = 0.0; 35 | return; 36 | } 37 | ++i; 38 | } 39 | int integral_part = value; 40 | value <<= 16; 41 | // Fractional part. 42 | bool success = false; 43 | if ( ch == '.' ) { 44 | ++i; 45 | int divisor = 1; 46 | int remainder = 0; 47 | while ( ( ch = number[ i ] ) >= '0' && ch <= '9' ) { 48 | if ( divisor < 65536 ) { 49 | divisor *= 10; 50 | int digit = ch - '0'; 51 | value += digit * ( 65536 / divisor ); 52 | remainder *= 10; 53 | remainder += digit * ( 65536 % divisor ); 54 | } 55 | ++i; 56 | } 57 | if ( sign == -1 && integral_part == 32768 && remainder > 0 ) { 58 | gResult.err = ERR_OVERFLOW; 59 | gResult.value = 0.0; 60 | return; 61 | } 62 | value += remainder / divisor; 63 | } 64 | if ( ! ch ) { 65 | gResult.err = ERR_NONE; 66 | gResult.value = ( fixed ) ( sign * value ); 67 | } 68 | else { 69 | gResult.err = ERR_ILLFORMED; 70 | gResult.value = 0.0; 71 | } 72 | } 73 | 74 | } 75 | 76 | // ========================================================================== 77 | strict namespace { 78 | // ========================================================================== 79 | 80 | script "Main" open { 81 | Math.ParseFixed( "12.3456" ); 82 | Print( s: "Error: ", d: Math.gResult.err, s: "\nValue: ", 83 | f: Math.gResult.value ); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /test/stack.bcs: -------------------------------------------------------------------------------- 1 | #include "zcommon.h" 2 | 3 | // ========================================================================== 4 | strict namespace { 5 | // ========================================================================== 6 | 7 | script "Main" open { 8 | using StackTest; 9 | auto stack = StackCreate(); 10 | StackPush( stack, "123" ); 11 | StackPush( stack, "321" ); 12 | StackPush( stack, "789" ); 13 | Print( s: StackPop( stack ) ); 14 | StackPush( stack, "666" ); 15 | Print( s: StackPop( stack ) ); 16 | Print( s: StackPop( stack ) ); 17 | Print( s: StackPop( stack ) ); 18 | } 19 | 20 | } 21 | 22 | // ========================================================================== 23 | strict namespace StackTest { 24 | // ========================================================================== 25 | 26 | struct NodeT { 27 | NodeT? next; 28 | str value; 29 | }; 30 | 31 | struct StackT { 32 | NodeT? top; 33 | }; 34 | 35 | StackT& StackCreate() { 36 | static auto allocated = false; 37 | static StackT stack; 38 | assert ( ! allocated, "Stack already allocated" ); 39 | allocated = true; 40 | return stack; 41 | } 42 | 43 | void StackPush( StackT& stack, str value ) { 44 | auto node = AllocateFreeNode( value ); 45 | node.next = stack.top; 46 | stack.top = node; 47 | } 48 | 49 | // Both allocates and frees a node. 50 | private NodeT? AllocateFreeNode( str value, NodeT? freeNode = null ) { 51 | static struct { 52 | NodeT node; 53 | bool allocated; 54 | } table[ 10 ]; 55 | // Free a node. 56 | if ( freeNode ) { 57 | foreach ( auto entry; table ) { 58 | if ( entry.node == freeNode ) { 59 | entry.allocated = false; 60 | return null; 61 | } 62 | } 63 | assert ( 0 ); 64 | } 65 | // Allocate a node. 66 | else { 67 | foreach ( auto entry; table ) { 68 | if ( ! entry.allocated ) { 69 | entry.allocated = true; 70 | auto node = entry.node; 71 | node.value = value; 72 | return node; 73 | } 74 | } 75 | assert ( 0, "No more free nodes available" ); 76 | } 77 | } 78 | 79 | str StackPop( StackT& stack ) { 80 | assert ( stack.top ); 81 | auto node = stack.top; 82 | stack.top = stack.top.next; 83 | AllocateFreeNode( "", node ); 84 | return node.value; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /test/list.bcs: -------------------------------------------------------------------------------- 1 | #include "zcommon.h" 2 | 3 | // ========================================================================== 4 | strict namespace { 5 | // ========================================================================== 6 | 7 | script "Main" open { 8 | using ListTest; 9 | auto list = ListCreate(); 10 | foreach ( auto ch; "abcde" ) { 11 | auto node = ListCreateNode( ch ); 12 | ListAppend( list, node ); 13 | } 14 | ListDetach( list, list.head.next ); 15 | ListDetach( list, list.tail ); 16 | Print( s: "Size: ", i: list.size ); 17 | auto node = list.head; 18 | while ( node ) { 19 | Print( s: "node=", c: node.value ); 20 | node = node.next; 21 | } 22 | } 23 | 24 | } 25 | 26 | // ========================================================================== 27 | strict namespace ListTest { 28 | // ========================================================================== 29 | 30 | struct ListT { 31 | struct NodeT { 32 | NodeT? next; 33 | int value; 34 | }? head, tail; 35 | int size; 36 | }; 37 | 38 | ListT& ListCreate() { 39 | static auto allocated = false; 40 | static ListT list; 41 | assert ( ! allocated, "List already allocated" ); 42 | allocated = true; 43 | return list; 44 | } 45 | 46 | void ListAppend( ListT& list, NodeT& node ) { 47 | if ( list.head ) { 48 | list.tail.next = node; 49 | } 50 | else { 51 | list.head = node; 52 | } 53 | list.tail = node; 54 | ++list.size; 55 | } 56 | 57 | NodeT& ListCreateNode( int value ) { 58 | static auto numAllocated = 0; 59 | static NodeT availableNodes[ 10 ]; 60 | assert ( numAllocated < availableNodes.length(), 61 | "No more free nodes available" ); 62 | auto node = availableNodes[ numAllocated ]; 63 | node.value = value; 64 | ++numAllocated; 65 | return node; 66 | } 67 | 68 | void ListDetach( ListT& list, NodeT? targetNode ) { 69 | if ( targetNode == list.head ) { 70 | list.head = list.head.next; 71 | if ( targetNode == list.tail ) { 72 | list.tail = null; 73 | } 74 | } 75 | else if ( targetNode == list.tail ) { 76 | auto node = list.head; 77 | while ( node.next && node.next != targetNode ) { 78 | node = node.next; 79 | } 80 | node.next = null; 81 | list.tail = node; 82 | } 83 | else { 84 | auto node = list.head; 85 | while ( node.next && node.next != targetNode ) { 86 | node = node.next; 87 | } 88 | node.next = node.next.next; 89 | } 90 | --list.size; 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/codegen/asm.c: -------------------------------------------------------------------------------- 1 | #include "phase.h" 2 | 3 | static void write_arg( struct codegen* codegen, struct inline_asm_arg* arg ); 4 | static void write_string_arg( struct codegen* codegen, 5 | struct inline_asm_arg* arg ); 6 | static void write_expr_arg( struct codegen* codegen, 7 | struct inline_asm_arg* arg ); 8 | 9 | void p_visit_inline_asm( struct codegen* codegen, 10 | struct inline_asm* inline_asm ) { 11 | struct list_iter i; 12 | list_iterate( &inline_asm->args, &i ); 13 | c_unoptimized_opc( codegen, inline_asm->opcode ); 14 | while ( ! list_end( &i ) ) { 15 | write_arg( codegen, list_data( &i ) ); 16 | list_next( &i ); 17 | } 18 | } 19 | 20 | static void write_arg( struct codegen* codegen, struct inline_asm_arg* arg ) { 21 | switch ( arg->type ) { 22 | case INLINE_ASM_ARG_NUMBER: 23 | c_arg( codegen, arg->value.number ); 24 | break; 25 | case INLINE_ASM_ARG_STRING: 26 | write_string_arg( codegen, arg ); 27 | break; 28 | case INLINE_ASM_ARG_LABEL: 29 | if ( ! arg->value.label->point ) { 30 | arg->value.label->point = c_create_point( codegen ); 31 | } 32 | c_point_arg( codegen, arg->value.label->point ); 33 | break; 34 | case INLINE_ASM_ARG_VAR: 35 | c_arg( codegen, arg->value.var->index ); 36 | break; 37 | case INLINE_ASM_ARG_PARAM: 38 | c_arg( codegen, arg->value.param->index ); 39 | break; 40 | case INLINE_ASM_ARG_FUNC: 41 | if ( arg->value.func->type == FUNC_EXT ) { 42 | struct func_ext* impl = arg->value.func->impl; 43 | c_arg( codegen, impl->id ); 44 | } 45 | else { 46 | struct func_user* impl = arg->value.func->impl; 47 | c_arg( codegen, impl->index ); 48 | } 49 | break; 50 | case INLINE_ASM_ARG_EXPR: 51 | write_expr_arg( codegen, arg ); 52 | break; 53 | default: 54 | C_UNREACHABLE( codegen ); 55 | } 56 | } 57 | 58 | static void write_string_arg( struct codegen* codegen, 59 | struct inline_asm_arg* arg ) { 60 | c_append_string( codegen, arg->value.string ); 61 | c_arg( codegen, arg->value.string->index_runtime ); 62 | } 63 | 64 | static void write_expr_arg( struct codegen* codegen, 65 | struct inline_asm_arg* arg ) { 66 | struct indexed_string* string = NULL; 67 | if ( arg->value.expr->has_str ) { 68 | string = t_lookup_string( codegen->task, arg->value.expr->value ); 69 | if ( string ) { 70 | c_append_string( codegen, string ); 71 | } 72 | } 73 | if ( string ) { 74 | c_arg( codegen, string->index_runtime ); 75 | } 76 | else { 77 | c_arg( codegen, arg->value.expr->value ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/jm_header/utility.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "utility" 4 | 5 | #include "zcommon.h" 6 | #include "config.h" 7 | #include "utility.h" 8 | 9 | // ========================================================================== 10 | strict namespace Jm.Utility { 11 | // ========================================================================== 12 | 13 | void Init() { 14 | if ( GetCvar( "util_ishosted" ) == 0 ) { 15 | if ( GameType() != GAME_SINGLE_PLAYER ) { 16 | ConsoleCommand( "set util_ishosted 1" ); 17 | } 18 | else { 19 | ConsoleCommand( "set util_ishosted 2" ); 20 | } 21 | } 22 | } 23 | 24 | bool IsOnline() { 25 | return ( GetCvar( "util_ishosted" ) == 1 ); 26 | } 27 | 28 | // Convert a par time string into seconds. 29 | int ParToSeconds( str par ) { 30 | auto pos = par.length() - 1; 31 | // Seconds: 32 | auto base = 1; 33 | auto seconds = 0; 34 | while ( pos >= 0 && par[ pos ] != ':' ) { 35 | if ( par[ pos ] >= '0' && par[ pos ] <= '9' ) { 36 | seconds += ( par[ pos ] - '0' ) * base; 37 | base *= 10; 38 | } 39 | --pos; 40 | } 41 | // Minutes: 42 | base = 1; 43 | auto minutes = 0; 44 | while ( pos >= 0 ) { 45 | if ( par[ pos ] >= '0' && par[ pos ] <= '9' ) { 46 | minutes += ( par[ pos ] - '0' ) * base; 47 | base *= 10; 48 | } 49 | --pos; 50 | } 51 | return ( minutes * 60 ) + seconds; 52 | } 53 | 54 | str ZeroPad( int number ) { 55 | return ( number >= 0 && number <= 9 ? "0" : "" ); 56 | } 57 | 58 | str OrdinalSuffix( int value ) { 59 | switch ( value ) { 60 | case 11: 61 | case 12: 62 | case 13: 63 | return "th"; 64 | default: 65 | switch ( value % 10 ) { 66 | case 1: return "st"; 67 | case 2: return "nd"; 68 | case 3: return "rd"; 69 | case 4: return "th"; 70 | default: 71 | return "th"; 72 | } 73 | } 74 | } 75 | 76 | void ClearMessage( int id ) { 77 | HudMessage( s: ""; HUDMSG_PLAIN, id, 0, 0.0, 0.0, 1.0 ); 78 | } 79 | 80 | void ClearMessageBold( int id ) { 81 | HudMessageBold( s: ""; HUDMSG_PLAIN, id, 0, 0.0, 0.0, 1.0 ); 82 | } 83 | 84 | // Function to calculate the centiseconds from any remaining tics after 85 | // subtracting full seconds from the total tics. 86 | int CalCseconds( int tics ) { 87 | auto centiseconds = 0; 88 | if ( tics > 0 ) { 89 | // TODO: Explain this algorithm better than this comment! 90 | auto percent = 10000 / ( ( Jm.Cfg.TICS_IN_SECOND * 100 ) / tics ); 91 | centiseconds = ( 1000 * percent ) / 1000; 92 | } 93 | return centiseconds; 94 | } 95 | 96 | str GetMonthName( int month ) { 97 | static str names[] = { 98 | "January", "February", "March", "April", "May", "June", 99 | "July", "August", "September", "October", "November", "December" }; 100 | return ( month >= 1 && month <= names.length() ? names[ month - 1 ] : "" ); 101 | } 102 | 103 | } 104 | 105 | #endif -------------------------------------------------------------------------------- /src/parse/token/output.c: -------------------------------------------------------------------------------- 1 | #include "phase.h" 2 | 3 | static void output_source( struct parse* parse, struct str* output ); 4 | static void output_token( struct parse* parse, struct str* output ); 5 | 6 | void p_preprocess( struct parse* parse ) { 7 | parse->read_flags = READF_NL | READF_SPACETAB; 8 | struct str output; 9 | str_init( &output ); 10 | output_source( parse, &output ); 11 | if ( output.length > 0 && output.value[ output.length - 1 ] != '\n' ) { 12 | str_append( &output, NEWLINE_CHAR ); 13 | } 14 | printf( "%s", output.value ); 15 | str_deinit( &output ); 16 | } 17 | 18 | static void output_source( struct parse* parse, struct str* output ) { 19 | while ( true ) { 20 | p_read_eoptiontk( parse ); 21 | if ( parse->token->type != TK_END ) { 22 | output_token( parse, output ); 23 | } 24 | else { 25 | break; 26 | } 27 | } 28 | p_confirm_ifdircs_closed( parse ); 29 | } 30 | 31 | // TODO: Get the original text of the token. 32 | static void output_token( struct parse* parse, struct str* output ) { 33 | p_decorate_token( parse->token, output, true ); 34 | } 35 | 36 | void p_decorate_token( struct token* token, struct str* string, 37 | bool expand_horzspace ) { 38 | // TODO: Make sure we encode every token, because I did not actually do that 39 | // before adding the static assert. 40 | STATIC_ASSERT( TK_TOTAL == 157 ); 41 | switch ( token->type ) { 42 | case TK_NL: 43 | str_append( string, NEWLINE_CHAR ); 44 | break; 45 | case TK_HORZSPACE: 46 | if ( expand_horzspace ) { 47 | for ( int i = 0; i < token->length; ++i ) { 48 | str_append( string, token->text ); 49 | } 50 | } 51 | else { 52 | str_append( string, " " ); 53 | } 54 | break; 55 | case TK_LIT_STRING: 56 | str_append( string, "\"" ); 57 | for ( int i = 0; i < token->length; ++i ) { 58 | if ( token->text[ i ] == '"' ) { 59 | str_append( string, "\\" ); 60 | } 61 | char ch[] = { token->text[ i ], 0 }; 62 | str_append( string, ch ); 63 | } 64 | str_append( string, "\"" ); 65 | break; 66 | case TK_LIT_CHAR: 67 | str_append( string, "'" ); 68 | str_append( string, token->text ); 69 | str_append( string, "'" ); 70 | break; 71 | case TK_LIT_OCTAL: 72 | str_append( string, "0o" ); 73 | str_append( string, token->text ); 74 | break; 75 | case TK_LIT_HEX: 76 | str_append( string, "0x" ); 77 | str_append( string, token->text ); 78 | break; 79 | case TK_LIT_BINARY: 80 | str_append( string, "0b" ); 81 | str_append( string, token->text ); 82 | break; 83 | case TK_LIT_RADIX: 84 | for ( int i = 0; i < token->length; ++i ) { 85 | if ( token->text[ i ] == '_' ) { 86 | str_append( string, "r" ); 87 | } 88 | else { 89 | char ch[] = { token->text[ i ], 0 }; 90 | str_append( string, ch ); 91 | } 92 | } 93 | break; 94 | default: 95 | str_append( string, token->text ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/gbuf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | #include "gbuf.h" 6 | 7 | static void add_segment( struct gbuf* buffer ); 8 | static struct gbuf_seg* alloc_segment( void ); 9 | static size_t count_bytes_used( struct gbuf* buffer ); 10 | 11 | void gbuf_init( struct gbuf* buffer ) { 12 | buffer->head_segment = NULL; 13 | buffer->segment = NULL; 14 | add_segment( buffer ); 15 | } 16 | 17 | static void add_segment( struct gbuf* buffer ) { 18 | if ( ! buffer->segment || ! buffer->segment->next ) { 19 | struct gbuf_seg* segment = alloc_segment(); 20 | if ( buffer->head_segment ) { 21 | buffer->segment->next = segment; 22 | } 23 | else { 24 | buffer->head_segment = segment; 25 | } 26 | buffer->segment = segment; 27 | } 28 | else { 29 | buffer->segment = buffer->segment->next; 30 | } 31 | } 32 | 33 | static struct gbuf_seg* alloc_segment( void ) { 34 | struct gbuf_seg* segment = mem_alloc( sizeof( *segment ) ); 35 | segment->next = NULL; 36 | segment->used = 0; 37 | segment->pos = 0; 38 | return segment; 39 | } 40 | 41 | void gbuf_write( struct gbuf* buffer, const void* data, int length ) { 42 | if ( GBUF_SEGMENT_SIZE - buffer->segment->pos < length ) { 43 | add_segment( buffer ); 44 | } 45 | memcpy( buffer->segment->data + buffer->segment->pos, data, length ); 46 | buffer->segment->pos += length; 47 | if ( buffer->segment->pos > buffer->segment->used ) { 48 | buffer->segment->used = buffer->segment->pos; 49 | } 50 | } 51 | 52 | char* gbuf_alloc_block( struct gbuf* buffer ) { 53 | char* block = mem_alloc( count_bytes_used( buffer ) ); 54 | char* pos = block; 55 | struct gbuf_seg* segment = buffer->head_segment; 56 | while ( segment ) { 57 | memcpy( pos, segment->data, segment->used ); 58 | pos += segment->used; 59 | segment = segment->next; 60 | } 61 | return block; 62 | } 63 | 64 | static size_t count_bytes_used( struct gbuf* buffer ) { 65 | size_t total = 0; 66 | struct gbuf_seg* segment = buffer->head_segment; 67 | while ( segment ) { 68 | total += segment->used; 69 | segment = segment->next; 70 | } 71 | return total; 72 | } 73 | 74 | void gbuf_reset( struct gbuf* buffer ) { 75 | buffer->segment = buffer->head_segment; 76 | struct gbuf_seg* segment = buffer->segment; 77 | while ( segment ) { 78 | segment->pos = 0; 79 | segment->used = 0; 80 | segment = segment->next; 81 | } 82 | } 83 | 84 | bool gbuf_save( struct gbuf* buffer, const char* file_path ) { 85 | FILE* fh = fopen( file_path, "wb" ); 86 | if ( ! fh ) { 87 | return false; 88 | } 89 | struct gbuf_seg* segment = buffer->head_segment; 90 | while ( segment ) { 91 | size_t num_written = fwrite( segment->data, 92 | sizeof( segment->data[ 0 ] ), segment->used, fh ); 93 | if ( num_written != segment->used ) { 94 | break; 95 | } 96 | segment = segment->next; 97 | } 98 | fclose( fh ); 99 | return ( segment == NULL ); 100 | } 101 | -------------------------------------------------------------------------------- /test/jm_header/hs.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "hs" 4 | 5 | #include "zcommon.h" 6 | #include "config.h" 7 | #include "utility.h" 8 | #include "luk.h" 9 | #include "hs.h" 10 | 11 | // ========================================================================== 12 | strict namespace Jm.Hs { 13 | // ========================================================================== 14 | 15 | private enum : fixed { LINE_NOTICE_Y = 0.55 }; 16 | private enum : fixed { TBL_SHOW_TIME = 8.0 }; 17 | 18 | int gLoadStatus = STATUS_NONE; 19 | // Stores a ranker's high score load script number so other files can wait 20 | // for the ranker to complete loading the high score. 21 | str gLoadScript = ""; 22 | // Script of the ranker that shows the high score details table. 23 | str gDetailsScript = ""; 24 | private bool gCheated[ Cfg.MAX_PLAYERS ]; 25 | private bool gManualDisable = false; 26 | // High score finish time. We put some of the finish time handling here 27 | // because both rankers use the same code. 28 | private int gTics = Cfg.VALUE_NONE; 29 | 30 | bool IsAutoShowDetailsTable() { 31 | return ( GetCvar( "jm_show_rdetails" ) == 1 ); 32 | } 33 | 34 | bool IsCheater( int player ) { 35 | return ( gCheated[ player ] || IsCheatsEnabled() ); 36 | } 37 | 38 | bool IsCheatsEnabled() { 39 | return ( GetCvar( "sv_cheats" ) == 1 ); 40 | } 41 | 42 | bool IsEnabled() { 43 | return ( Utility.IsOnline() && Luk.IsEnabled() && ! gManualDisable ); 44 | } 45 | 46 | void Disable() { 47 | gManualDisable = true; 48 | } 49 | 50 | int GetFinishTime() { 51 | return gTics; 52 | } 53 | 54 | void SetFinishTime( int tics ) { 55 | gTics = tics; 56 | } 57 | 58 | bool IsSet() { 59 | return ( gTics != Cfg.VALUE_NONE ); 60 | } 61 | 62 | bool IsNew( int time ) { 63 | return ( gTics == Cfg.VALUE_NONE || time < gTics ); 64 | } 65 | 66 | void Disallow( int player ) { 67 | gCheated[ player ] = true; 68 | } 69 | 70 | script 999 net { 71 | ShowDetails( ( int ) TBL_SHOW_TIME ); 72 | } 73 | 74 | // Shows high score details table. 75 | void ShowDetails( int showTime ) { 76 | SetFont( "SMALLFONT" ); 77 | // High score disabled. 78 | if ( ! IsEnabled() ) { 79 | HudMessage( 80 | s: "\ciThe high score is disabled"; 81 | HUDMSG_PLAIN, LINE_NOTICE, 0, 1.5, LINE_NOTICE_Y, 3.0 ); 82 | } 83 | // High score loading. 84 | else if ( gLoadStatus != STATUS_LOADED ) { 85 | // High score is loading. 86 | if ( gLoadStatus == STATUS_WORKING ) { 87 | HudMessage( 88 | s: "\ciThe high score is loading. Please retry in a bit"; 89 | HUDMSG_PLAIN, LINE_NOTICE, 0, 1.5, LINE_NOTICE_Y, 3.0 ); 90 | } 91 | // Error. 92 | else { 93 | HudMessage( 94 | s: "\ciThe high score failed to load"; 95 | HUDMSG_PLAIN, LINE_NOTICE, 0, 1.5, LINE_NOTICE_Y, 3.0 ); 96 | } 97 | } 98 | // No high score set. 99 | else if ( ! IsSet() ) { 100 | HudMessage( 101 | s: "\ciNo high score is set for the map"; 102 | HUDMSG_PLAIN, LINE_NOTICE, 0, 1.5, LINE_NOTICE_Y, 3.0 ); 103 | } 104 | // Show. 105 | else { 106 | Acs_NamedExecuteAlways( gDetailsScript, 0, showTime ); 107 | } 108 | } 109 | 110 | } 111 | 112 | #endif -------------------------------------------------------------------------------- /test/bigint.bcs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | NOTE: Restrictions on operands: 4 | 5 | - Must be positive. 6 | - Leading zeroes not allowed. 7 | 8 | */ 9 | 10 | #include "zcommon.h" 11 | 12 | // ========================================================================== 13 | strict namespace { 14 | // ========================================================================== 15 | 16 | script "Main" open { 17 | str a = StrParam( d: INT_MAX ); 18 | str b = StrParam( d: INT_MAX - 1000'000'000 ); 19 | Print( i: BigInt.Lt( a, b ) ); 20 | Print( s: BigInt.Add( a, b ) ); 21 | Print( s: BigInt.Sub( a, b ) ); 22 | } 23 | 24 | } 25 | 26 | // ========================================================================== 27 | strict namespace BigInt { 28 | // ========================================================================== 29 | 30 | str Add( str a, str b ) { 31 | return buildmsg ( StrParam() ) { 32 | // Calculate sum. 33 | str sum = ( auto() ) { 34 | return buildmsg ( StrParam() ) { 35 | int i_a = a.length() - 1; 36 | int i_b = b.length() - 1; 37 | int carry = 0; 38 | while ( i_a >= 0 || i_b >= 0 ) { 39 | int sum = ( i_a >= 0 ? a[ i_a ] - '0' : 0 ) + 40 | ( i_b >= 0 ? b[ i_b ] - '0' : 0 ) + carry; 41 | append( d: sum - ( int( sum >= 10 ) * 10 ) ); 42 | carry = int( sum >= 10 ); 43 | --i_a; 44 | --i_b; 45 | } 46 | append( c: carry * '1' ); 47 | } 48 | }(); 49 | // Output sum. 50 | int i = sum.length() - 1; 51 | while ( i >= 0 ) { 52 | append( c: sum[ i ] ); 53 | --i; 54 | } 55 | } 56 | } 57 | 58 | str Sub( str a, str b ) { 59 | return buildmsg ( StrParam() ) { 60 | // Negative. 61 | if ( Lt( a, b ) ) { 62 | append( c: '-' ); 63 | str temp_a = a; 64 | a = b; 65 | b = temp_a; 66 | } 67 | // Calculate difference. 68 | str diff = ( auto() ) { 69 | return buildmsg ( StrParam() ) { 70 | int i_a = a.length() - 1; 71 | int i_b = b.length() - 1; 72 | int borrow = 0; 73 | while ( i_a >= 0 || i_b >= 0 ) { 74 | int digit_a = ( i_a >= 0 ? a[ i_a ] - '0' : 0 ); 75 | int digit_b = ( i_b >= 0 ? b[ i_b ] - '0' : 0 ); 76 | append( d: ( digit_a - borrow ) + 77 | ( int( digit_a - borrow < digit_b ) * 10 ) - digit_b ); 78 | borrow = int( digit_a - borrow < digit_b ); 79 | --i_a; 80 | --i_b; 81 | } 82 | } 83 | }(); 84 | // Output difference. 85 | int i = diff.length() - 1; 86 | while ( i >= 1 && diff[ i ] == '0' ) { 87 | --i; 88 | } 89 | while ( i >= 0 ) { 90 | append( c: diff[ i ] ); 91 | --i; 92 | } 93 | } 94 | } 95 | 96 | bool Lt( str a, str b ) { 97 | int length = b.length(); 98 | if ( a.length() == length ) { 99 | int i = 0; 100 | while ( i < length && a[ i ] < b[ i ] ) { 101 | ++i; 102 | } 103 | return ( i == length ); 104 | } 105 | else { 106 | return ( a.length() < length ); 107 | } 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /test/jm_header/map_message.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "mapmsg" 4 | 5 | #include "zcommon.h" 6 | #include "config.h" 7 | #include "utility.h" 8 | #include "jm.h" 9 | #include "hs.h" 10 | #include "solo_ranker.h" 11 | #include "map_message.h" 12 | 13 | // ========================================================================== 14 | strict namespace Jm.MapMsg { 15 | // ========================================================================== 16 | 17 | // Seconds to wait for the record holder name to load before proceeding 18 | // further. 19 | private enum { TIMEOUT = 2 }; 20 | // How long to display the map startup message on screen. 21 | private enum : fixed { DISPLAY_TIME = 3.0 }; 22 | 23 | // String versions with appropriate colors of the map skills available. 24 | private str gNames[] = { 25 | "\ccVaried\c-", 26 | "\cqVery Easy\c-", 27 | "\cdEasy\c-", 28 | "\chModerate\c-", 29 | "\cgHard\c-", 30 | "\cmVery Hard\c-" }; 31 | private str gAuthor = ""; 32 | private str gName = ""; 33 | private str gSkill = ""; 34 | private int gNumber = Cfg.VALUE_NONE; 35 | private str gPar = ""; 36 | private bool gIsSet = false; 37 | 38 | void Set( str name, str author, enum Skill skill, str par ) { 39 | gAuthor = author; 40 | gName = name; 41 | gNumber = GetLevelInfo( LEVELINFO_LEVELNUM ); 42 | if ( skill < 0 || skill >= SKILL_TOTAL ) { 43 | skill = SKILL_VARIED; 44 | } 45 | gSkill = gNames[ skill ]; 46 | SetPar( par ); 47 | gIsSet = true; 48 | } 49 | 50 | script "MapMsg.Show" enter { 51 | Delay( 66 ); 52 | if ( ! gIsSet ) { 53 | terminate; 54 | } 55 | // High score. 56 | if ( Hs.IsEnabled() ) { 57 | // Wait for the ranker to complete loading the high score. 58 | if ( Hs.gLoadStatus == Hs.STATUS_WORKING ) { 59 | NamedScriptWait( Hs.gLoadScript ); 60 | } 61 | // Auto show high score details table if enabled. We put this code 62 | // here so we can show the details table at the same time as the 63 | // map message appears. 64 | if ( Hs.IsSet() && Hs.IsAutoShowDetailsTable() ) { 65 | Hs.ShowDetails( ( raw ) DISPLAY_TIME ); 66 | } 67 | } 68 | Show(); 69 | } 70 | 71 | private void Show() { 72 | SetFont( "SMALLFONT" ); 73 | buildmsg ( HudMessage( HUDMSG_FADEOUT | HUDMSG_LOG, 412, 0, 1.5, 0.3, 74 | DISPLAY_TIME, 1.0 ) ) { 75 | append( 76 | s: "\cf", s: gName, s: " - ", 77 | s: "MAP", s: Jm.Utility.ZeroPad( gNumber ), 78 | i: gNumber, s: " - ", 79 | s: "Author: ", s: gAuthor, s: "\n\n", 80 | s: "\ciLevel of Difficulty\n", s: gSkill, s: "\n\n", 81 | s: "\ciPar Time\n", s: "\cn", s: gPar 82 | ); 83 | // We don't need to display the record holder's name in the map 84 | // message if the record details auto show feature is enabled. This 85 | // only applies to the solo ranker. 86 | if ( Jm.gMapType == Jm.MAP_SOLO && Hs.IsSet() && 87 | ! Hs.IsAutoShowDetailsTable() ) { 88 | append( 89 | s: "\n\n", 90 | s: "\ciMap record set by:", s: "\n", 91 | s: SoloRanker.GetHsAuthorName() 92 | ); 93 | } 94 | } 95 | } 96 | 97 | // Removes the whitespace found in the par time, so it looks nicer when shown 98 | // in the map message. 99 | private void SetPar( str par ) { 100 | gPar = ( auto() ) { 101 | return buildmsg ( StrParam() ) { 102 | foreach ( int ch; par ) { 103 | if ( ch != ' ' ) { 104 | append( c: ch ); 105 | } 106 | } 107 | } 108 | }(); 109 | } 110 | 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/parse/asm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "phase.h" 4 | #include "codegen/phase.h" 5 | #include "codegen/pcode.h" 6 | 7 | static struct inline_asm* alloc_inline_asm( void ); 8 | static void read_opcode( struct parse* parse, struct inline_asm* inline_asm ); 9 | static void read_arg( struct parse* parse, struct inline_asm* inline_asm ); 10 | static struct inline_asm_arg* alloc_inline_asm_arg( struct pos* pos ); 11 | 12 | void p_read_asm( struct parse* parse, struct stmt_reading* reading ) { 13 | parse->create_nltk = true; 14 | struct inline_asm* inline_asm = alloc_inline_asm(); 15 | p_test_tk( parse, TK_GT ); 16 | p_read_tk( parse ); 17 | read_opcode( parse, inline_asm ); 18 | if ( parse->tk != TK_NL ) { 19 | while ( true ) { 20 | read_arg( parse, inline_asm ); 21 | if ( parse->tk == TK_COMMA ) { 22 | p_read_tk( parse ); 23 | } 24 | else { 25 | break; 26 | } 27 | } 28 | } 29 | p_test_tk( parse, TK_NL ); 30 | parse->create_nltk = false; 31 | p_read_tk( parse ); 32 | reading->node = &inline_asm->node; 33 | } 34 | 35 | static struct inline_asm* alloc_inline_asm( void ) { 36 | struct inline_asm* inline_asm = mem_alloc( sizeof( *inline_asm ) ); 37 | inline_asm->node.type = NODE_INLINE_ASM; 38 | inline_asm->name = NULL; 39 | inline_asm->next = NULL; 40 | list_init( &inline_asm->args ); 41 | inline_asm->opcode = 0; 42 | inline_asm->obj_pos = 0; 43 | return inline_asm; 44 | } 45 | 46 | static void read_opcode( struct parse* parse, struct inline_asm* inline_asm ) { 47 | switch ( parse->tk ) { 48 | case TK_ID: 49 | case TK_TERMINATE: 50 | case TK_RESTART: 51 | case TK_SUSPEND: 52 | case TK_GOTO: 53 | inline_asm->name = parse->tk_text; 54 | inline_asm->pos = parse->tk_pos; 55 | p_read_tk( parse ); 56 | break; 57 | default: 58 | p_unexpect_diag( parse ); 59 | p_unexpect_last_name( parse, NULL, "instruction name" ); 60 | p_bail( parse ); 61 | } 62 | } 63 | 64 | static void read_arg( struct parse* parse, struct inline_asm* inline_asm ) { 65 | struct inline_asm_arg* arg = alloc_inline_asm_arg( &parse->tk_pos ); 66 | list_append( &inline_asm->args, arg ); 67 | if ( 68 | parse->tk == TK_LIT_DECIMAL || 69 | parse->tk == TK_LIT_OCTAL || 70 | parse->tk == TK_LIT_HEX || 71 | parse->tk == TK_LIT_BINARY || 72 | parse->tk == TK_LIT_RADIX || 73 | parse->tk == TK_LIT_CHAR ) { 74 | arg->value.number = p_extract_literal_value( parse ); 75 | p_read_tk( parse ); 76 | } 77 | else if ( parse->tk == TK_LIT_FIXED ) { 78 | arg->value.number = p_extract_fixed_literal_value( parse->tk_text ); 79 | p_read_tk( parse ); 80 | } 81 | // FIXME: missing maximum string length check. 82 | else if ( parse->tk == TK_LIT_STRING ) { 83 | arg->type = INLINE_ASM_ARG_STRING; 84 | struct indexed_string* string = t_intern_string( parse->task, 85 | parse->tk_text, parse->tk_length ); 86 | string->in_source_code = true; 87 | arg->value.string = string; 88 | p_read_tk( parse ); 89 | } 90 | else if ( parse->tk == TK_ID ) { 91 | arg->type = INLINE_ASM_ARG_ID; 92 | arg->value.id = parse->tk_text; 93 | p_read_tk( parse ); 94 | } 95 | else { 96 | p_test_tk( parse, TK_PAREN_L ); 97 | p_read_tk( parse ); 98 | struct expr_reading expr; 99 | p_init_expr_reading( &expr, false, false, false, true ); 100 | p_read_expr( parse, &expr ); 101 | arg->type = INLINE_ASM_ARG_EXPR; 102 | arg->value.expr = expr.output_node; 103 | p_test_tk( parse, TK_PAREN_R ); 104 | p_read_tk( parse ); 105 | } 106 | } 107 | 108 | static struct inline_asm_arg* alloc_inline_asm_arg( struct pos* pos ) { 109 | struct inline_asm_arg* arg = mem_alloc( sizeof( *arg ) ); 110 | arg->type = INLINE_ASM_ARG_NUMBER; 111 | arg->value.number = 0; 112 | arg->pos = *pos; 113 | return arg; 114 | } 115 | -------------------------------------------------------------------------------- /test/functions.bcs: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | strict namespace FuncTest { 3 | // ========================================================================== 4 | 5 | // Compare two strings for equality, up to the specified length. 6 | int StrCmp( str a, str b, int length = -1 ) { 7 | int i = 0; 8 | while ( true ) { 9 | if ( length ) { 10 | if ( a[ i ] == b[ i ] ) { 11 | if ( a[ i ] ) { 12 | --length; 13 | ++i; 14 | } 15 | else { 16 | return 0; 17 | } 18 | } 19 | else if ( a[ i ] < b[ i ] ) { 20 | return -1; 21 | } 22 | else { 23 | return 1; 24 | } 25 | } 26 | else { 27 | return 0; 28 | } 29 | } 30 | } 31 | 32 | // Like StrCmp(), but case-insensitive. 33 | int StriCmp( str a, str b, int length = -1 ) { 34 | return StrCmp( StrToLower( a ), StrToLower( b ), length ); 35 | } 36 | 37 | // Get substring. 38 | str StrMid( str string, int start, int length ) { 39 | return buildmsg ( StrParam() ) { 40 | if ( start < 0 ) { 41 | start = 0; 42 | } 43 | int i = start; 44 | int k = start + length; 45 | int c = 0; 46 | while ( i < k && ( c = string[ i ] ) ) { 47 | append( c: c ); 48 | ++i; 49 | } 50 | } 51 | } 52 | 53 | // Get substring, starting at offset 0. 54 | str StrLeft( str string, int length ) { 55 | return StrMid( string, 0, length ); 56 | } 57 | 58 | // Get substring, starting at the end and reading backwards. 59 | str StrRight( str string, int length ) { 60 | return StrMid( string, string.length() - length, length ); 61 | } 62 | 63 | // Get string as uppercase. Character encoding assumed to be ASCII. 64 | str StrToUpper( str string ) { 65 | return buildmsg ( StrParam() ) { 66 | foreach ( auto ch; string ) { 67 | if ( ch >= 97 && ch <= 122 ) { 68 | append( c: ch - 32 ); 69 | } 70 | else { 71 | append( c: ch ); 72 | } 73 | } 74 | } 75 | } 76 | 77 | // Get string as lowercase. 78 | str StrToLower( str string ) { 79 | return buildmsg ( StrParam() ) { 80 | foreach ( auto ch; string ) { 81 | if ( ch >= 65 && ch <= 90 ) { 82 | append( c: ch + 32 ); 83 | } 84 | else { 85 | append( c: ch ); 86 | } 87 | } 88 | } 89 | } 90 | 91 | // Supports up to 10 arguments, integer or string. 92 | void Printf( str format, raw arg1 = 0, raw arg2 = 0, raw arg3 = 0, 93 | raw arg4 = 0, raw arg5 = 0, raw arg6 = 0, raw arg7 = 0, raw arg8 = 0, 94 | raw arg9 = 0, raw arg10 = 0 ) { 95 | buildmsg ( Print() ) { 96 | int num = 0; 97 | int pos = 0; 98 | while ( num < 10 ) { 99 | // Select arg. 100 | raw arg = 0; 101 | switch ( num + 1 ) { 102 | case 1: arg = arg1; break; 103 | case 2: arg = arg2; break; 104 | case 3: arg = arg3; break; 105 | case 5: arg = arg5; break; 106 | case 6: arg = arg6; break; 107 | case 7: arg = arg7; break; 108 | case 8: arg = arg8; break; 109 | case 9: arg = arg9; break; 110 | case 10: arg = arg10; break; 111 | default: 112 | goto done; 113 | } 114 | // Search for the next specifier. 115 | while ( true ) { 116 | if ( ! format[ pos ] ) { 117 | goto done; 118 | } 119 | else if ( format[ pos ] == '%' ) { 120 | break; 121 | } 122 | else { 123 | append( c: format[ pos ] ); 124 | ++pos; 125 | } 126 | } 127 | // Output the arg based on the specifier. 128 | switch ( format[ pos + 1 ] ) { 129 | case 's': 130 | append( s: arg ); 131 | pos += 2; 132 | break; 133 | case 'd': 134 | append( i: arg ); 135 | pos += 2; 136 | break; 137 | default: 138 | goto done; 139 | } 140 | ++num; 141 | } 142 | done: 143 | } 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /test/sorting.bcs: -------------------------------------------------------------------------------- 1 | #include "zcommon.h" 2 | 3 | // ========================================================================== 4 | strict namespace { 5 | // ========================================================================== 6 | 7 | enum { LIST_MAX_SIZE = 20 }; 8 | 9 | // Test script. 10 | script "Main" open { 11 | static int list[ LIST_MAX_SIZE ]; 12 | for ( int i = 0; i < list.length(); ++i ) { 13 | list[ i ] = Random( 1, 9 ); 14 | } 15 | buildmsg ( Print() ) { 16 | // Unsorted: 17 | Append( s: "Unsorted:" ); 18 | foreach ( auto value; list ) { 19 | Append( s: " ", d: value ); 20 | } 21 | // Select sort algorithm. 22 | // BSort( list ); 23 | // SSort( list ); 24 | // ISort( list ); 25 | MSort( list ); 26 | // Sorted: 27 | Append( s: "\n" ); 28 | Append( s: "Sorted:" ); 29 | foreach ( auto value; list ) { 30 | Append( s: " ", d: value ); 31 | } 32 | } 33 | } 34 | 35 | // Bubble sort. 36 | void BSort( int[]& list ) { 37 | int left = list.length(); 38 | while ( left ) { 39 | int i = 0; 40 | int k = left - 1; 41 | while ( i < k ) { 42 | if ( list[ i ] > list[ i + 1 ] ) { 43 | int temp = list[ i + 1 ]; 44 | list[ i + 1 ] = list[ i ]; 45 | list[ i ] = temp; 46 | } 47 | ++i; 48 | } 49 | --left; 50 | } 51 | } 52 | 53 | // Selection sort. 54 | void SSort( int[]& list ) { 55 | int sorted = 0; 56 | while ( sorted < list.length() ) { 57 | int lowest = sorted; 58 | int i = sorted + 1; 59 | while ( i < list.length() ) { 60 | if ( list[ i ] < list[ lowest ] ) { 61 | lowest = i; 62 | } 63 | ++i; 64 | } 65 | int temp = list[ sorted ]; 66 | list[ sorted ] = list[ lowest ]; 67 | list[ lowest ] = temp; 68 | ++sorted; 69 | } 70 | } 71 | 72 | // Insertion sort. 73 | void ISort( int[]& list ) { 74 | int sorted = 0; 75 | while ( sorted < list.length() ) { 76 | int value = list[ sorted ]; 77 | int i = sorted; 78 | while ( i && value < list[ i - 1 ] ) { 79 | list[ i ] = list[ i - 1 ]; 80 | --i; 81 | } 82 | list[ i ] = value; 83 | ++sorted; 84 | } 85 | } 86 | 87 | // Merge sort. 88 | void MSort( int[]& list ) { 89 | assert ( list.length() <= LIST_MAX_SIZE, 90 | "too many values to sort for " + __FUNCTION__ + "()" ); 91 | MsortHalf( list, 0, list.length() ); 92 | } 93 | 94 | private void MSortHalf( int[]& list, int pos, int size ) { 95 | static int temp[ LIST_MAX_SIZE ]; 96 | int sizeL = size / 2; 97 | int sizeR = size - sizeL; 98 | // Sort left side. 99 | if ( sizeL > 1 ) { 100 | MSortHalf( list, pos, sizeL ); 101 | } 102 | // Sort right side. 103 | if ( sizeR > 1 ) { 104 | MSortHalf( list, pos + sizeL, sizeR ); 105 | } 106 | // Merge sides. 107 | int i = pos; 108 | int j = pos + sizeL; 109 | int k = 0; 110 | while ( sizeL && sizeR ) { 111 | if ( list[ i ] < list[ j ] ) { 112 | temp[ k ] = list[ i ]; 113 | --sizeL; 114 | ++i; 115 | } 116 | else { 117 | temp[ k ] = list[ j ]; 118 | --sizeR; 119 | ++j; 120 | } 121 | ++k; 122 | } 123 | // Add leftover elements. 124 | while ( sizeL ) { 125 | temp[ k ] = list[ i ]; 126 | --sizeL; 127 | ++i; 128 | ++k; 129 | } 130 | while ( sizeR ) { 131 | temp[ k ] = list[ j ]; 132 | --sizeR; 133 | ++j; 134 | ++k; 135 | } 136 | // Save. 137 | i = 0; 138 | while ( i < size ) { 139 | list[ pos + i ] = temp[ i ]; 140 | ++i; 141 | } 142 | } 143 | 144 | // Shell sort. 145 | void HSort( int[]& list ) { 146 | // Figure out initial increment. 147 | int h = 0; 148 | int hNext = 1; 149 | do { 150 | h = hNext; 151 | hNext = 3 * h + 1; 152 | } while ( hNext < list.length() ); 153 | // Sort. 154 | while ( h ) { 155 | int sorted = h; 156 | while ( sorted < list.length() ) { 157 | int i = sorted; 158 | int value = list[ i ]; 159 | while ( i >= h && value < list[ i - h ] ) { 160 | list[ i ] = list[ i - h ]; 161 | i -= h; 162 | } 163 | list[ i ] = value; 164 | ++sorted; 165 | } 166 | h = ( h - 1 ) / 3; 167 | } 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/cache/archive.c: -------------------------------------------------------------------------------- 1 | #include "cache.h" 2 | 3 | // Save 4 | // ========================================================================== 5 | 6 | #define WF( saver, field ) \ 7 | f_wf( saver->w, field ) 8 | #define WV( saver, field, value ) \ 9 | f_wv( saver->w, field, value, sizeof( *( value ) ) ) 10 | #define WS( saver, field, value ) \ 11 | f_ws( saver->w, field, value ) 12 | 13 | enum { 14 | F_ARCHIVE, 15 | F_COMPILETIME, 16 | F_DEPENDENCY, 17 | F_END, 18 | F_ENTRY, 19 | F_ID, 20 | F_PATH, 21 | }; 22 | 23 | struct saver { 24 | struct field_writer* w; 25 | struct cache* cache; 26 | }; 27 | 28 | static void save_archive( struct saver* saver ); 29 | static void save_table( struct saver* saver ); 30 | static void save_entry( struct saver* saver, struct cache_entry* entry ); 31 | static void save_dependency_list( struct saver* saver, 32 | struct cache_entry* entry ); 33 | 34 | void cache_save_archive( struct cache* cache, struct field_writer* writer ) { 35 | struct saver saver; 36 | saver.w = writer; 37 | saver.cache = cache; 38 | save_archive( &saver ); 39 | } 40 | 41 | static void save_archive( struct saver* saver ) { 42 | WF( saver, F_ARCHIVE ); 43 | save_table( saver ); 44 | WF( saver, F_END ); 45 | } 46 | 47 | static void save_table( struct saver* saver ) { 48 | struct cache_entry* entry = saver->cache->entries.head; 49 | while ( entry ) { 50 | save_entry( saver, entry ); 51 | entry = entry->next; 52 | } 53 | } 54 | 55 | static void save_entry( struct saver* saver, struct cache_entry* entry ) { 56 | WF( saver, F_ENTRY ); 57 | WS( saver, F_PATH, entry->path.value ); 58 | WV( saver, F_COMPILETIME, &entry->compile_time ); 59 | WV( saver, F_ID, &entry->id ); 60 | save_dependency_list( saver, entry ); 61 | WF( saver, F_END ); 62 | } 63 | 64 | static void save_dependency_list( struct saver* saver, 65 | struct cache_entry* entry ) { 66 | struct cache_dependency* dep = entry->dependency; 67 | while ( dep ) { 68 | WF( saver, F_DEPENDENCY ); 69 | WS( saver, F_PATH, dep->path.value ); 70 | WF( saver, F_END ); 71 | dep = dep->next; 72 | } 73 | } 74 | 75 | // Restore 76 | // ========================================================================== 77 | 78 | #define RF( restorer, field ) \ 79 | f_rf( restorer->r, field ) 80 | #define RV( restorer, field, value ) \ 81 | f_rv( restorer->r, field, value, sizeof( *( value ) ) ) 82 | #define RS( restorer, field ) \ 83 | f_rs( restorer->r, field ) 84 | 85 | struct restorer { 86 | struct field_reader* r; 87 | struct cache* cache; 88 | }; 89 | 90 | static void restore_archive( struct restorer* restorer ); 91 | static void restore_table( struct restorer* restorer ); 92 | static void restore_entry( struct restorer* restorer ); 93 | static void restore_dependency_list( struct restorer* restorer, 94 | struct cache_entry* entry ); 95 | static void restore_dependency( struct restorer* restorer, 96 | struct cache_entry* entry ); 97 | 98 | void cache_restore_archive( struct cache* cache, 99 | struct field_reader* reader ) { 100 | struct restorer restorer; 101 | restorer.r = reader; 102 | restorer.cache = cache; 103 | restore_archive( &restorer ); 104 | } 105 | 106 | static void restore_archive( struct restorer* restorer ) { 107 | RF( restorer, F_ARCHIVE ); 108 | restore_table( restorer ); 109 | RF( restorer, F_END ); 110 | } 111 | 112 | static void restore_table( struct restorer* restorer ) { 113 | while ( f_peek( restorer->r ) == F_ENTRY ) { 114 | restore_entry( restorer ); 115 | } 116 | } 117 | 118 | static void restore_entry( struct restorer* restorer ) { 119 | RF( restorer, F_ENTRY ); 120 | struct cache_entry* entry = cache_alloc_entry(); 121 | str_append( &entry->path, RS( restorer, F_PATH ) ); 122 | RV( restorer, F_COMPILETIME, &entry->compile_time ); 123 | RV( restorer, F_ID, &entry->id ); 124 | restore_dependency_list( restorer, entry ); 125 | RF( restorer, F_END ); 126 | cache_append_entry( &restorer->cache->entries, entry ); 127 | } 128 | 129 | static void restore_dependency_list( struct restorer* restorer, 130 | struct cache_entry* entry ) { 131 | while ( f_peek( restorer->r ) == F_DEPENDENCY ) { 132 | restore_dependency( restorer, entry ); 133 | } 134 | } 135 | 136 | static void restore_dependency( struct restorer* restorer, 137 | struct cache_entry* entry ) { 138 | RF( restorer, F_DEPENDENCY ); 139 | struct cache_dependency* dep = cache_alloc_dependency( restorer->cache, 140 | RS( restorer, F_PATH ) ); 141 | cache_append_dependency( entry, dep ); 142 | RF( restorer, F_END ); 143 | } 144 | -------------------------------------------------------------------------------- /test/jm_header/jm.bcs: -------------------------------------------------------------------------------- 1 | #if 1 2 | 3 | #library "jm" 4 | 5 | #include "zcommon.h" 6 | #include "jm.h" 7 | #include "utility.h" 8 | #include "timer.h" 9 | #include "map_message.h" 10 | #include "telepatch.h" 11 | #include "hs.h" 12 | #include "solo_ranker.h" 13 | #include "team_ranker.h" 14 | #include "svmz.h" 15 | #include "who_exited.h" 16 | #include "auto_fist.h" 17 | 18 | // ========================================================================== 19 | strict namespace Jm { 20 | // ========================================================================== 21 | 22 | enum Mode { 23 | MODE_COOP, 24 | MODE_SVMZ, 25 | }; 26 | 27 | enum MapType gMapType = MAP_SOLO; 28 | private enum Mode gMode = MODE_COOP; 29 | private int gFlags = F_NONE; 30 | private str gPar = ""; 31 | private int gPoints = 0; 32 | 33 | void InitSolo( str par ) { 34 | Init( MAP_SOLO, par ); 35 | } 36 | 37 | void InitTeam( str par, int points ) { 38 | Init( MAP_TEAM, par ); 39 | gPoints = points; 40 | } 41 | 42 | private void Init( enum MapType mapType, str par ) { 43 | Utility.Init(); 44 | gMapType = mapType; 45 | gPar = par; 46 | if ( GetCvar( "survival" ) ) { 47 | gMode = MODE_SVMZ; 48 | Hs.Disable(); 49 | if ( mapType == MAP_TEAM ) { 50 | TeamRanker.PrepareForSvmz(); 51 | } 52 | } 53 | else { 54 | Timer.SetPar( par ); 55 | if ( GetCvar( "sv_disallowsuicide" ) ) { 56 | ConsoleCommand( "sv_disallowsuicide false" ); 57 | } 58 | } 59 | } 60 | 61 | void EnableFlag( int flag ) { 62 | gFlags |= flag; 63 | } 64 | 65 | void DisableFlags( int flag ) { 66 | gFlags ^= flag; 67 | } 68 | 69 | void Run() { 70 | if ( gMode == MODE_COOP ) { 71 | if ( gFlags & F_MANUALTIMER ) { 72 | Timer.ShowTime( 0 ); 73 | } 74 | else { 75 | Timer.Run(); 76 | } 77 | if ( gMapType == MAP_SOLO ) { 78 | Acs_NamedExecute( "SoloRanker.Start", 0 ); 79 | } 80 | else { 81 | Acs_NamedExecute( "TeamRanker.Start", 0, gPoints ); 82 | } 83 | } 84 | else { 85 | RunSvmz( "map map01" ); 86 | } 87 | } 88 | 89 | void RunSvmz( str mapRestartCommand ) { 90 | if ( Svmz.Init() ) { 91 | ConsoleCommand( mapRestartCommand ); 92 | } 93 | if ( gMapType == MAP_TEAM ) { 94 | // Clear the points table from the previous round if the players failed 95 | // to complete the map. If not done, the table from the previous round 96 | // will overlap with the new one. 97 | TeamRanker.ClearPointsTable(); 98 | Acs_NamedExecute( "TeamRanker.Start", 0, gPoints ); 99 | } 100 | Acs_NamedExecute( "Svmz.Start", 0, gPar ); 101 | } 102 | 103 | void StartTimer() { 104 | if ( gFlags & F_MANUALTIMER ) { 105 | Timer.Run(); 106 | } 107 | else { 108 | PrintBold( 109 | s: "\cJm.StartTimer() error: timer is not set to manual" ); 110 | } 111 | } 112 | 113 | void SetMapMessage( str name, str author, enum MapMsg.Skill skill ) { 114 | MapMsg.Set( name, author, skill, gPar ); 115 | } 116 | 117 | void AddPoint() { 118 | if ( gMapType == MAP_TEAM ) { 119 | Acs_NamedExecuteAlways( "TeamRanker.AddPoint", 0 ); 120 | } 121 | else { 122 | PrintBold( s: "\ciCan't give a point on a non-team map" ); 123 | } 124 | } 125 | 126 | void FinishLine() { 127 | if ( gMode == MODE_COOP ) { 128 | if ( gMapType == MAP_SOLO ) { 129 | SoloRanker.Finish(); 130 | } 131 | else { 132 | AddPoint(); 133 | } 134 | } 135 | else { 136 | Svmz.MakeEscapee(); 137 | if ( gMapType == MAP_TEAM ) { 138 | AddPoint(); 139 | } 140 | } 141 | } 142 | 143 | void DisallowHs( int player ) { 144 | Hs.Disallow( player ); 145 | } 146 | 147 | void Exit() { 148 | if ( gMode == MODE_COOP || 149 | ( gMode == MODE_SVMZ && Svmz.ValidateExit() ) ) { 150 | WhoExited.ShowAndExit(); 151 | } 152 | } 153 | 154 | bool IsValidExit() { 155 | if ( gMode == MODE_SVMZ ) { 156 | return Svmz.ValidateExit(); 157 | } 158 | else { 159 | return true; 160 | } 161 | } 162 | 163 | script 931 death { 164 | if ( gMode == MODE_COOP && ! GetCvar( "jm_disable_telepatch" ) ) { 165 | // This function changes the activator of the script, so we better have 166 | // this block of code as the last block of the script. 167 | Telepatch.OnDeath( PlayerNumber() ); 168 | } 169 | } 170 | 171 | script 932 respawn { 172 | if ( gMode == MODE_COOP ) { 173 | Telepatch.OnRespawn( PlayerNumber() ); 174 | } 175 | } 176 | 177 | script 933 ( int player ) disconnect { 178 | if ( gMode == MODE_COOP ) { 179 | Telepatch.OnDisconnect( player ); 180 | } 181 | } 182 | 183 | } 184 | 185 | #endif -------------------------------------------------------------------------------- /src/parse/phase.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "phase.h" 4 | #include "cache/cache.h" 5 | 6 | static void run_phase( struct parse* parse ); 7 | 8 | void p_init( struct parse* parse, struct task* task, struct cache* cache ) { 9 | parse->task = task; 10 | // NOTE: parse->queue not initialized. 11 | parse->token = NULL; 12 | parse->source = NULL; 13 | parse->free_source = NULL; 14 | parse->source_entry = NULL; 15 | parse->source_entry_free = NULL; 16 | parse->last_id = 0; 17 | str_init( &parse->temp_text ); 18 | str_init( &parse->token_presentation ); 19 | parse->read_flags = READF_CONCATSTRINGS | READF_ESCAPESEQ; 20 | parse->concat_strings = false; 21 | parse->macro_head = NULL; 22 | parse->macro_free = NULL; 23 | parse->macro_param_free = NULL; 24 | parse->macro_expan = NULL; 25 | parse->macro_expan_free = NULL; 26 | parse->macro_arg_free = NULL; 27 | parse->ifdirc = NULL; 28 | parse->ifdirc_free = NULL; 29 | 30 | parse->line = 0; 31 | parse->column = 0; 32 | parse->cache = cache; 33 | parse->create_nltk = false; 34 | p_init_stream( parse ); 35 | parse->ns = NULL; 36 | parse->ns_fragment = NULL; 37 | parse->local_vars = NULL; 38 | parse->lib = NULL; 39 | parse->main_lib_lines = 0; 40 | parse->included_lines = 0; 41 | parse->lang = LANG_BCS; 42 | parse->main_source_deinited = false; 43 | parse->variadic_macro_context = false; 44 | parse->include_history_entry = NULL; 45 | t_init_pos_id( &parse->zcommon.pos, INTERNALFILE_COMPILER ); 46 | parse->zcommon.included = false; 47 | t_init_pos_id( &parse->wadauthor.pos, INTERNALFILE_COMPILER ); 48 | parse->wadauthor.specified = false; 49 | parse->wadauthor.enabled = false; 50 | } 51 | 52 | void p_run( struct parse* parse ) { 53 | bool success = false; 54 | jmp_buf bail, *prev_bail = parse->task->bail; 55 | if ( setjmp( bail ) == 0 ) { 56 | parse->task->bail = &bail; 57 | run_phase( parse ); 58 | success = true; 59 | } 60 | parse->task->bail = prev_bail; 61 | p_deinit_tk( parse ); 62 | if ( ! success ) { 63 | t_bail( parse->task ); 64 | } 65 | } 66 | 67 | static void run_phase( struct parse* parse ) { 68 | struct library* lib = t_add_library( parse->task ); 69 | lib->lang = ( parse->task->options->lang_specified ) ? 70 | parse->task->options->lang : p_determine_lang_from_file_path( 71 | parse->task->options->source_file ); 72 | parse->lang = lib->lang; 73 | parse->lang_limits = t_get_lang_limits( lib->lang ); 74 | t_create_builtins( parse->task, lib->lang ); 75 | lib->wadauthor = ( lib->lang == LANG_ACS ); 76 | parse->task->library_main = lib; 77 | parse->lib = lib; 78 | parse->ns_fragment = lib->upmost_ns_fragment; 79 | parse->ns = parse->ns_fragment->ns; 80 | p_define_predef_macros( parse ); 81 | p_define_cmdline_macros( parse ); 82 | p_create_cmdline_library_links( parse ); 83 | p_load_main_source( parse ); 84 | if ( parse->task->options->preprocess ) { 85 | p_preprocess( parse ); 86 | } 87 | else { 88 | p_read_tk( parse ); 89 | p_read_target_lib( parse ); 90 | } 91 | } 92 | 93 | int p_determine_lang_from_file_path( const char* path ) { 94 | const char* ext = ""; 95 | for ( int i = 0; path[ i ]; ++i ) { 96 | if ( path[ i ] == '.' ) { 97 | ext = path + i + 1; 98 | } 99 | } 100 | return ( strcasecmp( ext, "bcs" ) == 0 ) ? 101 | LANG_BCS : 102 | LANG_ACS; 103 | } 104 | 105 | void p_diag( struct parse* parse, int flags, ... ) { 106 | va_list args; 107 | va_start( args, flags ); 108 | t_diag_args( parse->task, flags, &args ); 109 | va_end( args ); 110 | } 111 | 112 | void p_unexpect_diag( struct parse* parse ) { 113 | p_diag( parse, DIAG_POS_ERR | DIAG_SYNTAX, &parse->tk_pos, 114 | "unexpected %s", p_present_token_temp( parse, parse->tk ) ); 115 | } 116 | 117 | void p_unexpect_item( struct parse* parse, struct pos* pos, enum tk tk ) { 118 | p_unexpect_name( parse, pos, p_present_token_temp( parse, tk ) ); 119 | } 120 | 121 | void p_unexpect_name( struct parse* parse, struct pos* pos, 122 | const char* subject ) { 123 | p_diag( parse, DIAG_POS, ( pos ? pos : &parse->tk_pos ), 124 | "expecting %s here, or", subject ); 125 | } 126 | 127 | void p_unexpect_last( struct parse* parse, struct pos* pos, enum tk tk ) { 128 | p_unexpect_last_name( parse, pos, p_present_token_temp( parse, tk ) ); 129 | } 130 | 131 | void p_unexpect_last_name( struct parse* parse, struct pos* pos, 132 | const char* subject ) { 133 | p_diag( parse, DIAG_POS, ( pos ? pos : &parse->tk_pos ), 134 | "expecting %s here", subject ); 135 | } 136 | 137 | void p_bail( struct parse* parse ) { 138 | t_bail( parse->task ); 139 | } 140 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if defined( _WIN32 ) || defined( _WIN64 ) 10 | # define OS_WINDOWS 1 11 | #else 12 | # define OS_WINDOWS 0 13 | #endif 14 | 15 | extern const char* c_version; 16 | 17 | void mem_init( void ); 18 | void* mem_alloc( size_t ); 19 | void* mem_realloc( void*, size_t ); 20 | void* mem_slot_alloc( size_t ); 21 | void mem_free( void* ); 22 | void mem_free_all( void ); 23 | 24 | #define ARRAY_SIZE( a ) ( sizeof( a ) / sizeof( a[ 0 ] ) ) 25 | #define STATIC_ASSERT( ... ) \ 26 | STATIC_ASSERT_IMPL( __VA_ARGS__,, ) 27 | #define STATIC_ASSERT_IMPL( cond, msg, ... ) \ 28 | extern int STATIC_ASSERT__##msg[ !! ( cond ) ] 29 | #define UNREACHABLE() printf( \ 30 | "%s:%d: internal error: unreachable code\n", \ 31 | __FILE__, __LINE__ ); 32 | 33 | // Make sure the data types are of sizes we want. 34 | STATIC_ASSERT( CHAR_BIT == 8, CHAR_BIT_must_be_8 ); 35 | STATIC_ASSERT( sizeof( char ) == 1, char_must_be_1_byte ); 36 | STATIC_ASSERT( sizeof( short ) == 2, short_must_be_2_bytes ); 37 | STATIC_ASSERT( sizeof( int ) == 4, int_must_be_4_bytes ); 38 | STATIC_ASSERT( sizeof( long long ) == 8, long_long_must_be_8_bytes ); 39 | 40 | typedef signed char i8; 41 | typedef signed short i16; 42 | typedef signed int i32; 43 | typedef signed long long i64; 44 | typedef unsigned char u8; 45 | typedef unsigned short u16; 46 | typedef unsigned int u32; 47 | typedef unsigned long long u64; 48 | 49 | struct str { 50 | char* value; 51 | int length; 52 | int buffer_length; 53 | }; 54 | 55 | void str_init( struct str* ); 56 | void str_deinit( struct str* ); 57 | void str_copy( struct str*, const char* value, int length ); 58 | void str_grow( struct str*, int length ); 59 | void str_append( struct str*, const char* cstr ); 60 | void str_append_sub( struct str*, const char* cstr, int length ); 61 | void str_append_number( struct str*, int number ); 62 | void str_append_format( struct str* str, const char* format, ... ); 63 | void str_append_format_va( struct str* str, const char* format, 64 | va_list* args ); 65 | void str_clear( struct str* ); 66 | 67 | // Singly linked list 68 | // -------------------------------------------------------------------------- 69 | 70 | struct list_link { 71 | struct list_link* next; 72 | void* data; 73 | }; 74 | 75 | struct list { 76 | struct list_link* head; 77 | struct list_link* tail; 78 | int size; 79 | }; 80 | 81 | struct list_iter { 82 | struct list_link* prev; 83 | struct list_link* link; 84 | }; 85 | 86 | void list_init( struct list* list ); 87 | int list_size( struct list* list ); 88 | void* list_head( struct list* list ); 89 | void* list_tail( struct list* list ); 90 | void list_append( struct list*, void* data ); 91 | void list_prepend( struct list*, void* data ); 92 | void list_iterate( struct list* list, struct list_iter* iter ); 93 | bool list_end( struct list_iter* iter ); 94 | void list_next( struct list_iter* iter ); 95 | void* list_data( struct list_iter* iter ); 96 | void list_insert_after( struct list* list, 97 | struct list_iter* iter, void* data ); 98 | void list_insert_before( struct list* list, 99 | struct list_iter* iter, void* data ); 100 | // Updates the data at the specified node and returns the old data. 101 | void* list_replace( struct list* list, 102 | struct list_iter* iter, void* data ); 103 | void list_merge( struct list* receiver, struct list* giver ); 104 | // Removes the first node of the list and returns the data of the removed node. 105 | void* list_shift( struct list* list ); 106 | void list_deinit( struct list* list ); 107 | 108 | // -------------------------------------------------------------------------- 109 | 110 | struct options { 111 | struct list includes; 112 | struct list defines; 113 | struct list library_links; 114 | const char* source_file; 115 | const char* object_file; 116 | int tab_size; 117 | int lang; 118 | bool acc_err; 119 | bool acc_stats; 120 | bool one_column; 121 | bool help; 122 | bool preprocess; 123 | bool write_asserts; 124 | bool lang_specified; 125 | bool show_version; 126 | bool legacy_ns_dot; 127 | bool legacy_array_length_func; 128 | bool legacy_str_length_func; 129 | struct { 130 | const char* dir_path; 131 | int lifetime; 132 | bool enable; 133 | bool print; 134 | bool clear; 135 | } cache; 136 | }; 137 | 138 | #if OS_WINDOWS 139 | 140 | #include 141 | #include 142 | 143 | // NOTE: Volume information is not included. Maybe add it later. 144 | struct fileid { 145 | int id_high; 146 | int id_low; 147 | }; 148 | 149 | #define NEWLINE_CHAR "\r\n" 150 | #define OS_PATHSEP "\\" 151 | 152 | struct fs_query { 153 | WIN32_FILE_ATTRIBUTE_DATA attrs; 154 | const char* path; 155 | DWORD err; 156 | bool attrs_obtained; 157 | }; 158 | 159 | struct fs_timestamp { 160 | time_t value; 161 | }; 162 | 163 | #define strcasecmp _stricmp 164 | 165 | #else 166 | 167 | #include 168 | #include 169 | 170 | struct fileid { 171 | dev_t device; 172 | ino_t number; 173 | }; 174 | 175 | #define NEWLINE_CHAR "\n" 176 | #define OS_PATHSEP "/" 177 | 178 | struct fs_query { 179 | const char* path; 180 | struct stat stat; 181 | int err; 182 | bool stat_obtained; 183 | }; 184 | 185 | struct fs_timestamp { 186 | time_t value; 187 | }; 188 | 189 | #endif 190 | 191 | struct file_contents { 192 | char* data; 193 | int err; 194 | bool obtained; 195 | }; 196 | 197 | struct fs_result { 198 | int err; 199 | }; 200 | 201 | bool c_read_fileid( struct fileid*, const char* path ); 202 | bool c_same_fileid( struct fileid*, struct fileid* ); 203 | bool c_read_full_path( const char* path, struct str* ); 204 | void c_extract_dirname( struct str* ); 205 | const char* c_get_file_ext( const char* path ); 206 | 207 | int alignpad( int size, int align_size ); 208 | 209 | void fs_init_query( struct fs_query* query, const char* path ); 210 | bool fs_exists( struct fs_query* query ); 211 | bool fs_is_dir( struct fs_query* query ); 212 | bool fs_get_mtime( struct fs_query* query, struct fs_timestamp* timestamp ); 213 | bool fs_create_dir( const char* path, struct fs_result* result ); 214 | const char* fs_get_tempdir( void ); 215 | void fs_get_file_contents( const char* path, struct file_contents* contents ); 216 | void fs_strip_trailing_pathsep( struct str* path ); 217 | bool fs_delete_file( const char* path ); 218 | bool c_is_absolute_path( const char* path ); 219 | 220 | #endif 221 | -------------------------------------------------------------------------------- /doc/grammar_acs95.txt: -------------------------------------------------------------------------------- 1 | ACS95 Grammar 2 | =============================================================================== 3 | 4 | Special terminals: 5 | E (Empty): Indicates the rule is optional 6 | 7 | NOTE: This grammar attempts to follow closely the grammar that the original acc 8 | compiler implements, but to simplify the development of the bcc compiler, the 9 | grammar is not 100% accurate. Some errors that are syntactic errors in the 10 | original acc are now semantic errors in bcc. 11 | 12 | ------------------------------------------------------------------------------- 13 | 14 | module: 15 | 16 | 17 | module-body: 18 | 19 | E 20 | 21 | module-item-list: 22 | 23 | 24 | 25 | module-item: 26 | 27 | 28 |