diff --git a/HomeKeyPlugInsFR.HC b/HomeKeyPlugInsFR.HC new file mode 100644 index 0000000..8ce5343 --- /dev/null +++ b/HomeKeyPlugInsFR.HC @@ -0,0 +1,285 @@ +// FKey by L0Wigh + +asm +{ + FN_KEY_SCAN_DECODE_TABLE:: + DU8 0, CH_ESC, "&‚\"'(-Š_‡…)=", CH_BACKSPACE, '\t'; + DU8 "azertyuiop^$$", '\n', "<", "qs"; + DU8 "dfghjklm—*", 0, "*wxcv"; + DU8 "bn,;:!", 0, '*', 0, CH_SPACE, 0, 0, 0, 0, 0, 0; + DU8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0; + FS_KEY_SCAN_DECODE_TABLE:: + DU8 0, CH_SHIFT_ESC, "1234567890ø+", CH_BACKSPACE, '\t'; + DU8 "AZERTYUIOP??", '\n', 0, "QS"; + DU8 "DFGHJKLM%\\", 0, "*WXCV"; + DU8 "BN?./?", 0, '*', 0, CH_SPACE, 0, 0, 0, 0, 0, 0; + DU8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0; + FC_KEY_SCAN_DECODE_TABLE:: + DU8 0, CH_ESC, "1234567890-=", CH_BACKSPACE, '\t'; + DU8 CH_CTRLQ, CH_CTRLW, CH_CTRLE, CH_CTRLR, CH_CTRLT, CH_CTRLY, CH_CTRLU, + CH_CTRLI, CH_CTRLO, CH_CTRLP, "[]", '\n', 0, CH_CTRLA, CH_CTRLS; + DU8 CH_CTRLD, CH_CTRLF, CH_CTRLG, CH_CTRLH, CH_CTRLJ, CH_CTRLK, CH_CTRLL, + ";'\`", 0, "\\", CH_CTRLZ, CH_CTRLX, CH_CTRLC, CH_CTRLV; + DU8 CH_CTRLB, CH_CTRLN, CH_CTRLM, ",./", 0, '*', 0, CH_SPACE, 0, 0, 0, 0, 0, 0; + DU8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0; +}; + +MemCpy(NORMAL_KEY_SCAN_DECODE_TABLE, FN_KEY_SCAN_DECODE_TABLE, 64); +MemCpy(SHIFT_KEY_SCAN_DECODE_TABLE, FS_KEY_SCAN_DECODE_TABLE, 64); +MemCpy(CTRL_KEY_SCAN_DECODE_TABLE, FC_KEY_SCAN_DECODE_TABLE, 64); +U0 TimeIns() +{ + CDate cdt; + cdt = Now; + "$$IV,1$$----%D %T----$$IV,0$$\n", cdt, cdt; +} + +Bool MyPutKey(I64 ch,I64 sc) +{//ch=ASCII; sc=scan_code + + //See $LK,"Char",A="HI:Char"$ for definition of scan codes. + //See $LK,"Key Allocations",A="FI:::/Doc/KeyAlloc.DD"$. + //See $LK,"Keyboard Devices",A="HI:Keyboard Devices/System"$. + + //You can customize keys. This routine + //is called before the main editor + //key hndlr $LK,"DocPutKey",A="MN:DocPutKey"$(). + //You can intercept any key. + + //Return TRUE if you completely + //handled the key. + I64 i; + U8 *st1,*st2; + + // Adding support for ISO layout + if (sc&0x7F==0x56 && !(sc&SCF_CTRL||sc&SCF_KEY_DESC)){ + if(sc&SCF_ALT) + '|'; + else if(sc&SCF_SHIFT^^sc&SCF_CAPS) + '>'; + else + '<'; + return TRUE; + } + + if (sc&SCF_ALT && !(sc&SCF_CTRL)) { + switch (ch) { + case 0: + switch (sc.u8[0]) { + case SC_F1: + if (sc&SCF_SHIFT) { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/LTPURPLE"); + else + "$$LTPURPLE$$"; + } else { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/PURPLE"); + else + "$$PURPLE$$"; + } + return TRUE; + case SC_F2: + if (sc&SCF_SHIFT) { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/LTRED"); + else + "$$LTRED$$"; + } else { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/RED"); + else + "$$RED$$"; + } + return TRUE; + case SC_F3: + if (sc&SCF_SHIFT) { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/LTGREEN"); + else + "$$LTGREEN$$"; + } else { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/GREEN"); + else + "$$GREEN$$"; + } + return TRUE; + case SC_F4: + if (sc&SCF_SHIFT) { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/Default Color"); + else + "$$FG$$"; + } else { + if (sc&SCF_KEY_DESC) + KeyDescSet("Dol/BLUE"); + else + "$$BLUE$$"; + } + return TRUE; + case SC_F7: + if (!(sc&SCF_SHIFT)) { + if (sc&SCF_KEY_DESC) + KeyDescSet("Cmd/TimeIns"); + else + TimeIns; + } + return TRUE; + } + break; + case 'a': + if (sc&SCF_KEY_DESC) + KeyDescSet("Cmd/AutoComplete On"); + else + AutoComplete(ON); + return TRUE; + case 'A': + if (sc&SCF_KEY_DESC) + KeyDescSet("Cmd/AutoComplete Off"); + else + AutoComplete; + return TRUE; + case 'h': + if (sc&SCF_KEY_DESC) + KeyDescSet("Cmd/WinTileHorz"); + else + WinTileHorz; + return TRUE; + case 'm': + if (sc&SCF_KEY_DESC) + KeyDescSet("Cmd/WinMax"); + else { + WinBorder; + WinMax; + } + return TRUE; + case 'v': + if (sc&SCF_KEY_DESC) + KeyDescSet("Cmd/WinTileVert"); + else + WinTileVert; + return TRUE; + case 'l': + if (sc&SCF_KEY_DESC) + KeyDescSet("Edit/Put Link to Cur Pos on Clip"); + else { + ClipDel; + st1=FileNameAbs(BIBLE_FILENAME); + st2=FileNameAbs(DocPut->filename.name); + if (!StrCmp(st1,st2)) { + Free(st1); + st1=BibleLine2Verse(DocPut->cur_entry->y+1,','); + DocPrint(sys_clip_doc,"$$LK,\"BF:%s\"$$",st1); + } else + DocPrint(sys_clip_doc,"$$LK,\"FL:%s,%d\"$$", + st2,DocPut->cur_entry->y+1); + Free(st1); + Free(st2); + } + return TRUE; + case 'L': + if (sc&SCF_KEY_DESC) + KeyDescSet("Edit/Place Anchor, Put Link to Clip"); + else { + i=RandU32; + ClipDel; + DocPrint(sys_clip_doc,"$$LK,\"\",A=\"FA:%s,ANC%d\"$$", + DocPut->filename.name,i); + "$$AN,\"\",A=\"ANC%d\"$$",i; + } + return TRUE; + //Ins your own ALT-key plug-ins + case '‚': + if (!(sc&SCF_KEY_DESC)) + '~'; + return TRUE; + case '\"': + if (!(sc&SCF_KEY_DESC)) + '#'; + return TRUE; + case '\'': + if (!(sc&SCF_KEY_DESC)) + '{'; + return TRUE; + case '(': + if (!(sc&SCF_KEY_DESC)) + '['; + return TRUE; + case '-': + if (!(sc&SCF_KEY_DESC)) + '|'; + return TRUE; + case 'Š': + if (!(sc&SCF_KEY_DESC)) + '`'; + return TRUE; + case '_': + if (!(sc&SCF_KEY_DESC)) + '\\'; + return TRUE; + case '‡': + if (!(sc&SCF_KEY_DESC)) + '^'; + return TRUE; + case '…': + if (!(sc&SCF_KEY_DESC)) + '@'; + return TRUE; + case ')': + if (!(sc&SCF_KEY_DESC)) + ']'; + return TRUE; + case '=': + if (!(sc&SCF_KEY_DESC)) + '}'; + return TRUE; + } + } + return FALSE; +} + +Bool MyPutS(U8 *) +{ + return FALSE; +} + +KeyDevAdd(&MyPutKey,&MyPutS,0x20000000,TRUE); + +// just in case of issues +// case '1': +// if (sc&SCF_KEY_DESC) +// KeyDescSet("Dol/ã"); +// else +// 'ã'; +// return TRUE; +// case '2': +// if (sc&SCF_KEY_DESC) +// KeyDescSet("Dol/é"); +// else +// 'é'; +// return TRUE; +// case '3': +// if (sc&SCF_KEY_DESC) +// KeyDescSet("Dol/è"); +// else +// 'è'; +// return TRUE; +// case '4': +// if (sc&SCF_KEY_DESC) +// KeyDescSet("Dol/ê"); +// else +// 'ê'; +// return TRUE; +// case '9': +// if (sc&SCF_KEY_DESC) +// KeyDescSet("Dol/Indent 5"); +// else +// "$$ID,5$$"; +// return TRUE; +// case '0': +// if (sc&SCF_KEY_DESC) +// KeyDescSet("Dol/Unindent 5"); +// else +// "$$ID,-5$$"; +// return TRUE; diff --git a/README.md b/README.md index 7d482fd..7725176 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ### Preface -TinkerOS is essentially TempleOS renamed with some changes that allow it to run bare metal on some more modern machines since it "only" requires real or emulated PS/2 keyboard and mouse input and legacy boot support, but does not require legacy / IDE disk support. Though some improvements and bug fixes are added to TinkerOS, 95% of the code is the same as TempleOS and TinkerOS can install and compile TempleOS. TempleOS on the other hand requires hardware which supports Terry's hard coded 640x480 video mode which has been deprecated on some newer machines. Also TempleOS requires an IDE drive interface or legacy ATA SATA support. TinkerOS supports AHCI SATA mode installation. It also has a "Live USB" version which runs from a RAM disk which allows it to be tried without installing or run on systems without supported drives. You can also use the USB version to install it on supported machines that do not have a CD/DVD drive. If your machine uses the newer EFI boot method, it will still need to support legacy booting (via CSM) which you may need to turn on in your BIOS. +VaticanTempleOS is essentially TinkerOS renamed focused on [Automation](https://en.wikipedia.org/wiki/Automation). It contains custom theme and fonts, among the many home-built features and improvements. ### Goals - Keep a TempleOS like look and feel while attempting to keep a fully TempleOS compatible API to the maximum extent possible. TinkerOS should always be able to compile and install TempleOS. diff --git a/Setup.HC b/Setup.HC new file mode 100644 index 0000000..20a37d9 --- /dev/null +++ b/Setup.HC @@ -0,0 +1,25 @@ +DocClear; +"\nWould you like to install the French keyboard layout "; + +if (YorN) +{ + Copy("HomeKeyPlugInsFR.HC", "~/HomeKeyPlugIns.HC"); + "\n\nReboot Now "; + if (YorN) + Reboot; +} +else + "\nSetup aborted !\n"; + +DocClear; + +"\nWould you like to set Gruvbox theme "; + +if (YorN) +{ + #include "~/skins/Gruvbox/Gruvbox.HC" +} +else + "\nSetup aborted !\n"; + + diff --git a/_tools/cleanup.py b/_tools/cleanup.py new file mode 100755 index 0000000..9403006 --- /dev/null +++ b/_tools/cleanup.py @@ -0,0 +1,23 @@ +#!/usr/bin/python3 +import os +import sys +import shutil + +import workspace.build + +# Remove workspace folder +shutil.rmtree("workspace", ignore_errors = True) + +# Remove tup database +shutil.rmtree(".tup", ignore_errors = True) + +# Make build.py remove the stuff it built +workspace.build.clean() + +# Remove files copied from _tools/workspace +tools = os.path.dirname(os.path.realpath(__file__)) +tools_workspace = os.path.join(tools, "workspace") +for copied_script in os.listdir(tools_workspace): + if os.path.exists(copied_script): + os.remove(copied_script) + diff --git a/_tools/get_started.py b/_tools/get_started.py new file mode 100755 index 0000000..65c88e4 --- /dev/null +++ b/_tools/get_started.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +# Copyright Magomed Kostoev +# Published under MIT license + +import os +import shutil + +from lib.network import download +from lib.logging import log +from lib.constants import tools, tools_workspace, tools_cache, tools_cache_kolibri_img + +def generate_script_executing_script(script_to_execute): + script_to_execute = script_to_execute.replace("\\", "\\\\") + contents = "" + contents += "from importlib.machinery import SourceFileLoader\n" + contents += f"SourceFileLoader('__main__', '{script_to_execute}').load_module()\n" + return contents + +def create_workspace_script(name, script_to_execute): + log(f"Installing {name}... ", end = "") + + script_contents = generate_script_executing_script(script_to_execute) + with open(name, "w") as f: + f.write(script_contents) + + log("Done") + +if __name__ == "__main__": + # Check if we have tup installed + if shutil.which("tup") == None: + print("Sorry, I haven't found tup") + print("Possible solutions:") + print("- Install tup") + print("- Add tup installation folder to PATH") + exit() + # Initalize tup here + os.system("tup init") + # Create (in current directory) scripts that execute + # the same named scripts from _tools/workspace + tools_workspace_run_py = os.path.join(tools_workspace, "run.py") + tools_workspace_build_py = os.path.join(tools_workspace, "build.py") + create_workspace_script("run.py", tools_workspace_run_py) + create_workspace_script("build.py", tools_workspace_build_py) + diff --git a/_tools/lib/builds.py b/_tools/lib/builds.py new file mode 100644 index 0000000..2931af9 --- /dev/null +++ b/_tools/lib/builds.py @@ -0,0 +1,21 @@ +import os +import shutil + +from .network import download +from .constants import tools_cache + +def builds_get(path, output_path = None): + url = f"http://builds.kolibrios.org/{path}" + cached_path = f"{tools_cache}/builds.kolibrios.org/{path}" + os.makedirs(os.path.dirname(cached_path), exist_ok = True) + download(url, cached_path, skip_exist = True) + if output_path != None: + shutil.copyfile(cached_path, output_path) + return output_path + return cached_path + +def builds_get_contents(path): + output_path = builds_get(path) + with open(output_path, "rb") as f: + return f.read() + diff --git a/_tools/lib/constants.py b/_tools/lib/constants.py new file mode 100644 index 0000000..7e560b1 --- /dev/null +++ b/_tools/lib/constants.py @@ -0,0 +1,22 @@ +import os + +### PATHS + +# _tools/lib/constants.py +tools_lib_constants_py = os.path.abspath(__file__) + +# _tools/lib +tools_lib = os.path.dirname(tools_lib_constants_py) + +# _tools +tools = os.path.dirname(tools_lib) + +# _tools/workspace +tools_workspace = os.path.join(tools, "workspace") + +# _tools/cache +tools_cache = os.path.join(tools, "cache") + +# _tools/cache/kolibri.img +tools_cache_kolibri_img = os.path.join(tools_cache, "kolibri.img") + diff --git a/_tools/lib/logging.py b/_tools/lib/logging.py new file mode 100644 index 0000000..b221457 --- /dev/null +++ b/_tools/lib/logging.py @@ -0,0 +1,21 @@ +import shutil + +def log(s, end = "\n"): + print(s, end = end, flush = True) + +def require_tools(names): + assert(type(names) == list or type(names) == tuple) + for name in names: + assert(type(name) == str) + + not_found = [] + for name in names: + if shutil.which(name) is None: + not_found.append(name) + + if len(not_found) != 0: + log("Sorry, I can't find some tools:") + for name in not_found: + print(f"- {name}") + exit(1) + diff --git a/_tools/lib/makeflop.py b/_tools/lib/makeflop.py new file mode 100644 index 0000000..1a4b53a --- /dev/null +++ b/_tools/lib/makeflop.py @@ -0,0 +1,792 @@ +#!/usr/bin/env python3 +# +# makeflop.py +# Version 3 +# Brad Smith, 2019 +# http://rainwarrior.ca +# +# Simple file operations for a FAT12 floppy disk image. +# Public domain. + +import sys +assert sys.version_info[0] == 3, "Python 3 required." + +import struct +import datetime +import os + +class Floppy: + """ + Simple file operations for a FAT12 floppy disk image. + + Floppy() - creates a blank DOS formatted 1.44MB disk. + Floppy(data) - parses a disk from a given array of bytes. + .open(filename) - loads a file and returns a Floppy from its data. + .save(filename) - saves the disk image to a file. + .flush() - Updates the .data member with any pending changes. + .data - A bytearray of the disk image. + + .files() - Returns a list of strings, each is a file or directory. Directories end with a backslash. + .delete_path(path) - Deletes a file or directory (recursive). + .add_dir_path(path) - Creates a new empty directory, if it does not already exist (recursive). Returns cluster of directory, or -1 if failed. + .add_file_path(path,data) - Creates a new file (creating directory if needed) with the given data. Returns False if failed. + .extract_file_path(path) - Returns a bytearray of the file at the given path, None if not found. + .set_volume_id(id=None) - Sets the 32-bit volume ID. Use with no arguments to generate ID from current time. + .set_volume_label(label) - Sets the 11-character volume label. + + .boot_info() - Returns a string displaying boot sector information. + .fat_info() - Returns a very long string of all 12-bit FAT entries. + .files_info() - Returns a string displaying the files() list. + + .add_all(path,prefix) - Adds all files from local path to disk (uppercased). Use prefix to specify a target directory. Returns False if any failed. + .extract_all(path) - Dumps entire contents of disk to local path. + + .find_path(path) - Returns a Floppy.FileEntry. + FileEntry.info() - Returns and information string about a FileEntry. + + This class provides some basic interface to a FAT12 floppy disk image. + Some things are fragile, in particular filenames that are too long, + or references to clusters outside the disk may cause exceptions. + The FAT can be accessed directly with some internal functions (see implementation) + and changes will be applied to the stored .data image with .flush(). + + Example: + f = Floppy.open("disk.img") + print(f.boot_info()) # list boot information about disk + print(f.file_info()) # list files and directories + f.extract_all("disk_dump\\") + f.delete_path("DIR\\FILE.TXT") # delete a file + f.delete_path("DIR") # delete an entire directory + f.add_file_path("DIR\\NEW.TXT",open("new.txt","rb").read()) # add a file, directory automatically created + f.add_dir_path("NEWDIR") # creates a new directory + f.add_all("add\\","ADDED\\") # adds all files from a local directory to to a specified floppy directory. + f.set_volume_id() # generates a new volume ID + f.set_volume_label("MY DISK") # changes the volume label + f.save("new.img") + """ + + EMPTY = 0xE5 # incipit value for an empty directory entry + + def _filestring(s, length): + """Creates an ASCII string, padded to length with spaces ($20).""" + b = bytearray(s.encode("ASCII")) + b = b + bytearray([0x20] * (length - len(b))) + if len(b) != length: + raise self.Error("File string '%s' too long? (%d != %d)" % (s,len(b),length)) + return b + + class Error(Exception): + """Floppy has had an error.""" + pass + + class FileEntry: + """A directory entry for a file.""" + + def __init__(self, data=None, dir_cluster=-1, dir_index=-1): + """Unpacks a 32 byte directory entry into a FileEntry structure.""" + if data == None: + data = bytearray([Floppy.EMPTY]+([0]*31)) + self.data = bytearray(data) + self.path = "" + self.incipit = data[0] + if self.incipit != 0x00 and self.incipit != Floppy.EMPTY: + filename = data[0:8].decode("cp866") + filename = filename.rstrip(" ") + extension = data[8:11].decode("cp866") + extension = extension.rstrip(" ") + self.path = filename + if len(extension) > 0: + self.path = self.path + "." + extension + block = struct.unpack("= 0): + filename = self.path[0:period] + extension = self.path[period+1:] + else: + filename = self.path + if self.incipit != 0x00 and self.incipit != 0xEF: + self.data[0:8] = Floppy._filestring(filename,8) + self.data[8:11] = Floppy._filestring(extension,3) + else: + self.data[0] = self.incipit + self.data[11] = self.attributes + self.data[22:32] = bytearray(struct.pack("> 1) + return (date, time) + + def fat_time_now(): + """Builds the current time as a FAT12 date/time entry.""" + now = datetime.datetime.now() + return Floppy.FileEntry.fat_time(now.year, now.month, now.day, now.hour, now.minute, now.second) + + def set_name(self,s): + """Sets the filename and updates incipit.""" + self.path = s + self.incipit = Floppy._filestring(s,12)[0] + + def set_now(self): + """Updates modified date/time to now.""" + (date,time) = Floppy.FileEntry.fat_time_now() + self.write_date = date + self.write_time = time + + def new_file(name): + """Generate a new file entry.""" + e = Floppy.FileEntry() + e.set_name(name) + e.set_now() + e.attributes = 0x00 + return e + + def new_dir(name): + """Generate a new subdirectory entry.""" + e = Floppy.FileEntry() + e.set_name(name) + e.set_now() + e.attributes = 0x10 + return e + + def new_volume(name): + """Generate a new volume entry.""" + e = Floppy.FileEntry() + if len(name) > 8: + name = name[0:8] + "." + name[8:] + e.set_name(name) + e.set_now() + e.attributes = 0x08 + return e + + def new_terminal(): + """Generate a new directory terminating entry.""" + e = Floppy.FileEntry() + e.incipit = 0x00 + return e + + # blank formatted 1.44MB MS-DOS 5.0 non-system floppy + blank_floppy = [ # boot sector, 2xFAT, "BLANK" volume label, F6 fill + 0xEB,0x3C,0x90,0x4D,0x53,0x44,0x4F,0x53,0x35,0x2E,0x30,0x00,0x02,0x01,0x01,0x00, + 0x02,0xE0,0x00,0x40,0x0B,0xF0,0x09,0x00,0x12,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x29,0xE3,0x16,0x53,0x1B,0x42,0x4C,0x41,0x4E,0x4B, + 0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x41,0x54,0x31,0x32,0x20,0x20,0x20,0xFA,0x33, + 0xC0,0x8E,0xD0,0xBC,0x00,0x7C,0x16,0x07,0xBB,0x78,0x00,0x36,0xC5,0x37,0x1E,0x56, + 0x16,0x53,0xBF,0x3E,0x7C,0xB9,0x0B,0x00,0xFC,0xF3,0xA4,0x06,0x1F,0xC6,0x45,0xFE, + 0x0F,0x8B,0x0E,0x18,0x7C,0x88,0x4D,0xF9,0x89,0x47,0x02,0xC7,0x07,0x3E,0x7C,0xFB, + 0xCD,0x13,0x72,0x79,0x33,0xC0,0x39,0x06,0x13,0x7C,0x74,0x08,0x8B,0x0E,0x13,0x7C, + 0x89,0x0E,0x20,0x7C,0xA0,0x10,0x7C,0xF7,0x26,0x16,0x7C,0x03,0x06,0x1C,0x7C,0x13, + 0x16,0x1E,0x7C,0x03,0x06,0x0E,0x7C,0x83,0xD2,0x00,0xA3,0x50,0x7C,0x89,0x16,0x52, + 0x7C,0xA3,0x49,0x7C,0x89,0x16,0x4B,0x7C,0xB8,0x20,0x00,0xF7,0x26,0x11,0x7C,0x8B, + 0x1E,0x0B,0x7C,0x03,0xC3,0x48,0xF7,0xF3,0x01,0x06,0x49,0x7C,0x83,0x16,0x4B,0x7C, + 0x00,0xBB,0x00,0x05,0x8B,0x16,0x52,0x7C,0xA1,0x50,0x7C,0xE8,0x92,0x00,0x72,0x1D, + 0xB0,0x01,0xE8,0xAC,0x00,0x72,0x16,0x8B,0xFB,0xB9,0x0B,0x00,0xBE,0xE6,0x7D,0xF3, + 0xA6,0x75,0x0A,0x8D,0x7F,0x20,0xB9,0x0B,0x00,0xF3,0xA6,0x74,0x18,0xBE,0x9E,0x7D, + 0xE8,0x5F,0x00,0x33,0xC0,0xCD,0x16,0x5E,0x1F,0x8F,0x04,0x8F,0x44,0x02,0xCD,0x19, + 0x58,0x58,0x58,0xEB,0xE8,0x8B,0x47,0x1A,0x48,0x48,0x8A,0x1E,0x0D,0x7C,0x32,0xFF, + 0xF7,0xE3,0x03,0x06,0x49,0x7C,0x13,0x16,0x4B,0x7C,0xBB,0x00,0x07,0xB9,0x03,0x00, + 0x50,0x52,0x51,0xE8,0x3A,0x00,0x72,0xD8,0xB0,0x01,0xE8,0x54,0x00,0x59,0x5A,0x58, + 0x72,0xBB,0x05,0x01,0x00,0x83,0xD2,0x00,0x03,0x1E,0x0B,0x7C,0xE2,0xE2,0x8A,0x2E, + 0x15,0x7C,0x8A,0x16,0x24,0x7C,0x8B,0x1E,0x49,0x7C,0xA1,0x4B,0x7C,0xEA,0x00,0x00, + 0x70,0x00,0xAC,0x0A,0xC0,0x74,0x29,0xB4,0x0E,0xBB,0x07,0x00,0xCD,0x10,0xEB,0xF2, + 0x3B,0x16,0x18,0x7C,0x73,0x19,0xF7,0x36,0x18,0x7C,0xFE,0xC2,0x88,0x16,0x4F,0x7C, + 0x33,0xD2,0xF7,0x36,0x1A,0x7C,0x88,0x16,0x25,0x7C,0xA3,0x4D,0x7C,0xF8,0xC3,0xF9, + 0xC3,0xB4,0x02,0x8B,0x16,0x4D,0x7C,0xB1,0x06,0xD2,0xE6,0x0A,0x36,0x4F,0x7C,0x8B, + 0xCA,0x86,0xE9,0x8A,0x16,0x24,0x7C,0x8A,0x36,0x25,0x7C,0xCD,0x13,0xC3,0x0D,0x0A, + 0x4E,0x6F,0x6E,0x2D,0x53,0x79,0x73,0x74,0x65,0x6D,0x20,0x64,0x69,0x73,0x6B,0x20, + 0x6F,0x72,0x20,0x64,0x69,0x73,0x6B,0x20,0x65,0x72,0x72,0x6F,0x72,0x0D,0x0A,0x52, + 0x65,0x70,0x6C,0x61,0x63,0x65,0x20,0x61,0x6E,0x64,0x20,0x70,0x72,0x65,0x73,0x73, + 0x20,0x61,0x6E,0x79,0x20,0x6B,0x65,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x72,0x65, + 0x61,0x64,0x79,0x0D,0x0A,0x00,0x49,0x4F,0x20,0x20,0x20,0x20,0x20,0x20,0x53,0x59, + 0x53,0x4D,0x53,0x44,0x4F,0x53,0x20,0x20,0x20,0x53,0x59,0x53,0x00,0x00,0x55,0xAA, + ] + \ + [0xF0,0xFF,0xFF] + ([0]*(0x1200-3)) + \ + [0xF0,0xFF,0xFF] + ([0]*(0x1200-3)) + [ + 0x42,0x4C,0x41,0x4E,0x4B,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xEE,0x7C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + ] + ([0] * (0x4200-0x2620)) + \ + ([0xF6] * (0x168000-0x4200)) + + def __init__(self,data=blank_floppy): + """Create Floppy() instance from image bytes, or pre-formatted blank DOS floppy by default.""" + self.data = bytearray(data) + self._boot_open() + self._fat_open() + + def _boot_open(self): + """Parses the boot sector.""" + if (len(self.data) < 38): + raise self.Error("Not enough data in image for boot sector. (%d bytes)" % len(data)) + boot = struct.unpack("= 54: + self.volume_id = struct.unpack(" len(self.data): + raise self.Error("Not enough data to contain %d x %d byte sectors? (%d bytes)" % + (self.sectors, self.sector_size, len(self.data))) + self.root = self.sector_size * (self.reserved_sects + (self.fat_count * self.fat_sects)) + root_sectors = ((self.root_max * 32) + (self.sector_size-1)) // self.sector_size # round up to fill sector + self.cluster2 = self.root + (self.sector_size * root_sectors) + + def _boot_flush(self): + """Commits changes to the boot sector.""" + boot = struct.pack("= 54: + self.data[39:43] = bytearray(struct.pack("> 4) | (self.data[e+1] << 4) + e += 2 + self.fat.append(entry) + + def _fat_flush(self): + """Commits changes to the FAT table.""" + fat_start = self.reserved_sects * self.sector_size + fat_sects = self.fat_sects * self.sector_size + fat_end = fat_start + fat_sects + # build FAT 0 + e = self.reserved_sects * self.sector_size + for i in range(len(self.fat)): + entry = self.fat[i] + if (i & 1) == 0: + self.data[e+0] = entry & 0xFF + self.data[e+1] = (self.data[e+1] & 0xF0) | ((entry >> 8) & 0x0F) + e += 1 + else: + self.data[e+0] = (self.data[e+0] & 0x0F) | ((entry << 4) & 0xF0) + self.data[e+1] = (entry >> 4) & 0xFF + e += 2 + # copy to all tables + for i in range(1,self.fat_count): + fat2_start = fat_start + (fat_sects * i) + fat2_end = fat2_start + fat_sects + self.data[fat2_start:fat2_end] = self.data[fat_start:fat_end] + + def fat_info(self): + """String of information about the FAT.""" + per_line = 32 + s = "" + for i in range(len(self.fat)): + if (i % per_line) == 0: + s += "%03X: " % i + if (i % per_line) == (per_line // 2): + s += " " # extra space to mark 16 + s += " %03X" % self.fat[i] + if (i % per_line) == (per_line - 1): + s += "\n" + return s + + def _cluster_offset(self, cluster): + """Image offset of a FAT indexed logical cluster.""" + if cluster < 2: + return self.root + return self.cluster2 + (self.sector_size * self.cluster_sects * (cluster-2)) + + def _read_chain(self, cluster, size): + data = bytearray() + """Returns up to size bytes from a chain of clusters starting at cluster.""" + while cluster < 0xFF0: + offset = self._cluster_offset(cluster) + #print("read_chain(%03X,%d) at %08X" % (cluster,size,offset)) + if cluster < 2: + return data + bytearray(self.data[self.root:self.root+size]) # root directory + read = min(size,(self.sector_size * self.cluster_sects)) + data = data + bytearray(self.data[offset:offset+read]) + size -= read + if (size < 1): + return data + cluster = self.fat[cluster] + return data + + def _read_dir_chain(self, cluster): + """Reads an entire directory chain starting at the given cluster (0 for root).""" + if cluster < 2: + # root directory is contiguous + return self.data[self.root:self.root+(self.root_max*32)] + # directories just occupy as many clusters as in their FAT chain, using a dummy max size + return self._read_chain(cluster, self.sector_size*self.sectors//self.cluster_sects) + + def _delete_chain(self, cluster): + """Deletes a FAT chain.""" + while cluster < 0xFF0 and cluster >= 2: + link = self.fat[cluster] + self.fat[cluster] = 0 + cluster = link + + def _add_chain(self,data): + """Adds a block of data to the disk and creates its FAT chain. Returns start cluster, or -1 for failure.""" + cluster_size = self.sector_size * self.cluster_sects + clusters = (len(data) + cluster_size-1) // cluster_size + if clusters < 1: + clusters = 1 + # find a chain of free clusters + chain = [] + for i in range(2,len(self.fat)): + if self.fat[i] == 0: + chain.append(i) + if len(chain) >= clusters: + break + if len(chain) < clusters: + return -1 # out of space + # store the FAT chain + start_cluster = chain[0] + for i in range(0,len(chain)-1): + self.fat[chain[i]] = chain[i+1] + self.fat[chain[len(chain)-1]] = 0xFFF + # store the data in the given clusters + data = bytearray(data) + c = 0 + while len(data) > 0: + write = min(len(data),cluster_size) + offset = self._cluster_offset(chain[c]) + self.data[offset:offset+write] = data[0:write] + c += 1 + data = data[write:] + # done + return start_cluster + + def _files_dir(self, cluster, path): + """Returns a list of files in the directory starting at the given cluster. Recursive.""" + #print("files_dir(%03X,'%s')" % (cluster, path)) + entries = [] + directory = self._read_dir_chain(cluster) + for i in range(len(directory) // 32): + e = self.FileEntry(directory[(i*32):(i*32)+32],cluster,i) + #print(("entry %d (%08X)\n"%(i,self._dir_entry_offset(cluster,i)))+e.info()) + if e.incipit == 0x00: # end of directory + return entries + if e.incipit == Floppy.EMPTY: # empty + continue + if (e.attributes & 0x08) != 0: # volume label + continue + if (e.attributes & 0x10) != 0: # subdirectory + if e.path == "." or e.path == "..": + continue + subdir = path + e.path + "\\" + entries.append(subdir) + entries = entries + self._files_dir(e.cluster,subdir) + else: + entries.append(path + e.path) + return entries + + def files(self): + """Returns a list of files in the image.""" + root = self.sector_size * (self.reserved_sects + (self.fat_count * self.fat_sects)) + return self._files_dir(0,"") + + def files_info(self): + """String of the file list.""" + s = "" + for path in self.files(): + s += path + "\n" + return s + + def _dir_entry_offset(self,cluster,dir_index): + """Find the offset in self.data to a particular directory entry.""" + if (cluster < 2): + return self.root + (32 * dir_index) + if (cluster >= 0xFF0): + raise self.Error("Directory entry %d not in its cluster chain?" % dir_index) + per_cluster = (self.sector_size*self.cluster_sects)//32 + if (dir_index < per_cluster): # within this cluster + return self._cluster_offset(cluster) + (32 * dir_index) + # continue to next cluster + return self._dir_entry_offset(self.fat[cluster],dir_index-per_cluster) + + def delete_file(self, entry): + """Deletes a FileEntry.""" + self._delete_chain(entry.cluster) # delete its FAT chain + offset = self._dir_entry_offset(entry.dir_cluster,entry.dir_index) + self.data[offset+0] = Floppy.EMPTY # empty this entry + + def _find_path_dir(self, cluster, path): + """Recursive find path, breaking out subdirectories progressively.""" + #print("_find_path_dir(%03X,'%s')" % (cluster,path)) + separator = path.find("\\") + path_seek = path + path_next = "" + if separator >= 0: + path_seek = path[0:separator] + path_next = path[separator+1:] + directory = self._read_dir_chain(cluster) + for i in range(len(directory) // 32): + e = self.FileEntry(directory[(i*32):(i*32)+32],cluster,i) + if e.incipit == 0x00: # end of directory + return None + if e.incipit == Floppy.EMPTY: # empty + continue + if (e.attributes & 0x08) != 0: # volume label + continue + if (e.attributes & 0x10) != 0: # subdirectory + if e.path == "." or e.path == "..": + continue + if e.path == path_seek: + if (len(path_next) > 0): + return self._find_path_dir(e.cluster, path_next) + else: + return e + elif e.path == path_seek and path_next == "": + return e + return None + + def find_path(self, path): + """Finds a FileEntry for a given path.""" + return self._find_path_dir(0,path) + + def _delete_tree(self,de): + """Recursively deletes directory entries.""" + #print("_delete_tree\n" + de.info()) + directory = self._read_dir_chain(de.cluster) + for i in range(len(directory) // 32): + e = self.FileEntry(directory[(i*32):(i*32)+32],de.cluster,i) + #print(e.info()) + if e.incipit == 0x00: # end of directory + return + if e.incipit == Floppy.EMPTY: # empty + continue + if (e.attributes & 0x08) != 0: # volume label + continue + if (e.attributes & 0x10) != 0: # subdirectory + if e.path == "." or e.path == "..": + continue + self._delete_tree(e) # recurse + self.delete_file(e) + else: + self.delete_file(e) + + def delete_path(self, path): + """Finds a file or directory and deletes it (recursive), if it exists. Returns True if successful.""" + e = self.find_path(path) + if (e == None): + return False + if (e.attributes & 0x10) != 0: + self._delete_tree(e) + self.delete_file(e) + return True + + def _add_entry(self, dir_cluster, entry): + """ + Adds an entry to a directory starting at the given cluster, appending a new cluster if needed. + Sets entry.dir_cluster and entry.dir_index to match their new directory. + Returns False if out of space. + """ + #print(("_add_entry(%d):\n"%dir_cluster) + entry.info()) + directory = self._read_dir_chain(dir_cluster) + dir_len = len(directory)//32 + i = 0 + terminal = False + while i < dir_len: + e = self.FileEntry(directory[(i*32):(i*32)+32],dir_cluster,i) + #print(e.info()) + if e.incipit == 0x00: + terminal = True # make sure to add another terminal entry after this one + break + if e.incipit == Floppy.EMPTY: + break + i += 1 + # extend directory if out of room + if i >= dir_len: + if dir_cluster < 2: + return False # no room in root + # add a zero-filled page to the end of this directory's FAT chain + chain = self._add_chain(bytearray([0]*(self.sector_size*self.cluster_sects))) + if (chain < 0): + return False # no free clusters + tail = dir_cluster + while self.fat[tail] < 0xFF0: + tail = self.fat[tail] + self.fat[tail] = chain + self.fat[chain] = 0xFFF + # insert entry + entry.dir_cluster = dir_cluster + entry.dir_index = i + offset = self._dir_entry_offset(dir_cluster,i) + self.data[offset:offset+32] = entry.compile() + # add a new terminal if needed + if terminal: + i += 1 + if i < dir_len: # if it was the last entry, no new terminal is needed + offset = self._dir_entry_offset(dir_cluster,i) + self.data[offset:offset+32] = Floppy.FileEntry.new_terminal().compile() + + # success! + return True + + def _add_dir_recursive(self, cluster, path): + """Recursively creates directory, returns cluster of created dir, -1 if failed.""" + #print("_add_dir_recursive(%03X,'%s')"%(cluster,path)) + separator = path.find("\\") + path_seek = path + path_next = "" + if separator >= 0: + path_seek = path[0:separator] + path_next = path[separator+1:] + directory = self._read_dir_chain(cluster) + for i in range(len(directory) // 32): + e = self.FileEntry(directory[(i*32):(i*32)+32],cluster,i) + #print(e.info()) + if e.incipit == 0x00: # end of directory + break + if e.incipit == Floppy.EMPTY: # empty + continue + if (e.attributes & 0x10) != 0: # subdirectory + if e.path == path_seek: # already exists + if len(path_next) < 1: + return e.cluster # return existing directory + else: + return self._add_dir_recursive(e.cluster,path_next) # keep descending + # not found: create the directory + dp0 = Floppy.FileEntry.new_dir("_") # . + dp1 = Floppy.FileEntry.new_dir("__") # .. + dp1.cluster = cluster + dir_block = dp0.compile() + dp1.compile() + bytearray([0] * ((self.sector_size*self.cluster_sects)-64)) + new_cluster = self._add_chain(dir_block) + if new_cluster < 0: + return -1 # out of space + # fix up "." to point to itself + dp0.cluster = new_cluster + offset = self._dir_entry_offset(new_cluster,0) + self.data[offset:offset+32] = dp0.compile() + # fix special directory names (FileEntry.compile() is incapable of . or ..) + self.data[offset+ 0:offset+11] = bytearray(". ".encode("ASCII")) + self.data[offset+32:offset+43] = bytearray(".. ".encode("ASCII")) + # create entry to point to new directory cluster + new_dir = Floppy.FileEntry.new_dir(path_seek) + new_dir.cluster = new_cluster + if not self._add_entry(cluster, new_dir): + self._delete_chain(new_cluster) + return -1 # out of space + # return the entry if tail is reached, or keep descending + if len(path_next) < 1: + return new_cluster + else: + return self._add_dir_recursive(new_cluster,path_next) + + def add_dir_path(self, path): + """ + Recursively ensures that the given directory path exists, creating it if necessary. + Path should not end in backslash. + Returns cluster of directory at path, or -1 if failed. + """ + if (len(path) < 1): + return 0 # root + return self._add_dir_recursive(0,path) + + def add_file_path(self, path, data): + """ + Adds the given data as a file at the given path. + Will automatically create directories to complete the path. + Returns False if failed. + """ + self.delete_path(path) # remove file if it already exists + dir_path = "" + file_path = path + separator = path.rfind("\\") + if (separator >= 0): + dir_path = path[0:separator] + file_path = path[separator+1:] + dir_cluster = self.add_dir_path(dir_path) + if dir_cluster < 0: + return False # couldn't find or create directory + cluster = self._add_chain(data) + if (cluster < 0): + return False # out of space + entry = Floppy.FileEntry.new_file(file_path) + entry.cluster = cluster + entry.size = len(data) + if not self._add_entry(dir_cluster, entry): + self._delete_chain(cluster) + return False # out of space for directory entry + offset = self._dir_entry_offset(entry.dir_cluster, entry.dir_index) + self.data[offset:offset+32] = entry.compile() + return True + + def extract_file_path(self, path): + """Finds a file and returns all of its data. None on failure.""" + e = self.find_path(path) + if e == None: + return None + return self._read_chain(e.cluster, e.size) + + def set_volume_id(self, value=None): + """Sets the volume ID. None for time-based.""" + if (value == None): + (date,time) = Floppy.FileEntry.fat_time_now() + value = time | (date << 16) + self.volume_id = value & 0xFFFFFFFF + + def set_volume_label(self, label): + """Sets the volume label. Creates one if necessary.""" + self.data[38] = 0x29 + self.volume_label = label + self.data[54:62] = Floppy._filestring("FAT12",8) + # adjust existing volume entry in root + directory = self._read_dir_chain(0) + for i in range(len(directory) // 32): + e = self.FileEntry(directory[(i*32):(i*32)+32],0,i) + if e.incipit == 0x00: # end of directory + break + if e.incipit == Floppy.EMPTY: # empty + continue + if (e.attributes & 0x08) != 0: # existing volume label + offset = self._dir_entry_offset(0,i) + self.data[offset:offset+11] = Floppy._filestring(label,11) + return + # volume entry does not exist in root, add one + self._add_entry(0,Floppy.FileEntry.new_volume(label)) + return + + def open(filename): + """Opens a floppy image file and creates a Floppy() instance from it.""" + return Floppy(open(filename,"rb").read()) + + def flush(self): + """Commits all unfinished changes to self.data image.""" + self._fat_flush() + self._boot_flush() + + def save(self, filename): + """Saves image (self.data) to file. Implies flush().""" + self.flush() + open(filename,"wb").write(self.data) + + def extract_all(self, out_directory): + """Extracts all files from image to specified directory.""" + for in_path in self.files(): + out_path = os.path.join(out_directory,in_path) + out_dir = os.path.dirname(out_path) + if not os.path.exists(out_dir): + try: + os.makedirs(out_dir) + except OSError as e: + if e.errono != errno.EEXIST: + raise + if not in_path.endswith("\\"): + open(out_path,"wb").write(self.extract_file_path(in_path)) + print(out_path) + else: + print(out_path) + + def add_all(self, in_directory, prefix=""): + """ + Adds all files from specified directory to image. + Files will be uppercased. Long filenames are not checked and will cause an exception. + prefix can be used to prefix a directory path (ending with \) to the added files. + """ + result = True + def dospath(s): + s = s.upper() + s = s.replace("/","\\") + return s + if len(in_directory) < 1: + in_directory = "." + in_directory = os.path.normpath(in_directory) + os.sep + for (root, dirs, files) in os.walk(in_directory): + base = root[len(in_directory):] + for d in dirs: + dir_path = prefix + dospath(os.path.join(base,d)) + result = result and (self.add_dir_path(dir_path) >= 0) + print(dir_path + "\\") + for f in files: + file_path = prefix + dospath(os.path.join(base,f)) + data = open(os.path.join(root,f),"rb").read() + result = result and self.add_file_path(file_path,data) + print(file_path + " (%d bytes)" % len(data)) + return result diff --git a/_tools/lib/network.py b/_tools/lib/network.py new file mode 100644 index 0000000..9ac9c3a --- /dev/null +++ b/_tools/lib/network.py @@ -0,0 +1,11 @@ +import os +import urllib.request +from .logging import log + +def download(link, path, skip_exist = False): + if skip_exist and os.path.exists(path): + return + log(f"Downloading {path}... ", end = "") + urllib.request.urlretrieve(link, path) + log("Done.") + diff --git a/_tools/lib/platform.py b/_tools/lib/platform.py new file mode 100644 index 0000000..dfe88d0 --- /dev/null +++ b/_tools/lib/platform.py @@ -0,0 +1,16 @@ +import os +import sys + +def path(*args): + return os.sep.join(args) + +def is_win32(): + return True if sys.platform == "win32" else False + +def is_linux(): + return True if sys.platform == "linux" or sys.platform == "linux2" else False + +def is_osx(): + return True if sys.platform == "darwin" else False + + diff --git a/_tools/lib/tupfile_parser.py b/_tools/lib/tupfile_parser.py new file mode 100644 index 0000000..c960e01 --- /dev/null +++ b/_tools/lib/tupfile_parser.py @@ -0,0 +1,102 @@ +# Copyright Magomed Kostoev +# Published under MIT license + +block_beginner2finisher = { + "(": ")", + "{": "}", + "\"": "\"", +} + +def get_to(src, ptr, c, backwards = False): + while src[ptr] != c: + if not backwards: + ptr += 1 + else: + ptr -= 1 + return ptr + +# Assuming we are at beginning of some block (string, +# parenthesed expression, {}-like thing), get to end +# of the block (also handles the blocks openned in the +# way to the block ending character. So it's not just +# stupid searching for block closing character. +def get_to_block_finisher(src, ptr): + assert(src[ptr] in block_beginner2finisher) + + block_beginner = src[ptr] + ptr += 1 + while src[ptr] != block_beginner2finisher[block_beginner]: + # If any block starts here - get to its end and then continue + if src[ptr] in block_beginner2finisher: + ptr = get_to_block_finisher(src, ptr) + ptr += 1 + return ptr + +def get_strnig(src, ptr): + # Strings starts with "\"" + assert(src[ptr] == "\"") + + result = "" + # Skip first "\"" of the string + ptr += 1 + while src[ptr] != "\"": + result += src[ptr] + ptr += 1 + return result + +def parse_rule_output(src, ptr): + # Get straight to the first argument + ptr += len("tup.rule") + # Get to parenthese + ptr = get_to(src, ptr, "(") + # Get to the closing parenthese + ptr = get_to_block_finisher(src, ptr) + # We are at closing parenthese of argument list + # And the last argument is always output file + # Let's get to closing "\"" of the output file name + ptr = get_to(src, ptr, "\"", backwards = True) + # Get into the string + ptr -= 1 + # Then get to the beginning of the string + ptr = get_to(src, ptr, "\"", backwards = True) + # Now we can read the string + return get_strnig(src, ptr) + +def parse_required_compiler(src, ptr): + # Get straignt to the first argument + ptr += len("tup.getconfig") + # Get to parenthese + ptr = get_to(src, ptr, "(") + # Get to start of the requirement string + ptr = get_to(src, ptr, "\"") + # Read the requirement string (like NO_FASM) + requirement_string = get_strnig(src, ptr) + if requirement_string.startswith("NO_"): + return requirement_string[len("NO_"):].lower().replace("_", "-") + else: + return None + +def parse_tupfile_outputs(file_name): + outputs = [] + with open(file_name) as f: + tupfile = f.read() + rule_begin_index = tupfile.find("tup.rule(") + while (rule_begin_index != -1): + outputs.append(parse_rule_output(tupfile, rule_begin_index)) + # Find the next tup.rule call + rule_begin_index = tupfile.find("tup.rule(", rule_begin_index + len("tup.rule(")) + return outputs + +def parse_required_compilers(file_name): + compilers = [] + with open(file_name) as f: + tupfile = f.read() + rule_begin_index = tupfile.find("tup.getconfig(") + while (rule_begin_index != -1): + required_compiler = parse_required_compiler(tupfile, rule_begin_index) + if required_compiler is not None: + compilers.append(required_compiler) + # Find the next tup.getconfig call + rule_begin_index = tupfile.find("tup.getconfig(", rule_begin_index + len("tup.getconfig")) + return compilers + diff --git a/_tools/workspace/build.py b/_tools/workspace/build.py new file mode 100644 index 0000000..21eb87c --- /dev/null +++ b/_tools/workspace/build.py @@ -0,0 +1,37 @@ +import sys +import os + +path_to_tools_workspace = os.path.dirname(os.path.abspath(__file__)) +path_to_tools = os.path.dirname(path_to_tools_workspace) +sys.path.append(path_to_tools) + +from lib.tupfile_parser import parse_required_compilers, parse_tupfile_outputs +from lib.logging import require_tools + +def get_executable_file(output_file_list): + for name in output_file_list: + if name.endswith(".inc"): + continue + return name + +def build(): + required_compilers = parse_required_compilers("Tupfile.lua") + require_tools(required_compilers) + os.system("tup") + output_file_list = parse_tupfile_outputs("Tupfile.lua") + return get_executable_file(output_file_list) + +def clean(): + output_file_list = parse_tupfile_outputs("Tupfile.lua") + for output_file in output_file_list: + if os.path.exists(output_file): + os.remove(output_file) + +def main(argv): + if len(argv) == 2 and argv[1] == "clean": + clean() + else: + build() + +if __name__ == "__main__": + main(sys.argv) diff --git a/_tools/workspace/run.py b/_tools/workspace/run.py new file mode 100644 index 0000000..098fce6 --- /dev/null +++ b/_tools/workspace/run.py @@ -0,0 +1,85 @@ +import os +import sys +import shlex +import shutil +import urllib.request +import subprocess + +path_to_tools_workspace = os.path.dirname(os.path.abspath(__file__)) +path_to_tools = os.path.dirname(path_to_tools_workspace) +sys.path.append(path_to_tools) + +from workspace.build import build + +from lib.builds import builds_get, builds_get_contents +from lib.makeflop import Floppy +from lib.platform import is_win32, path +from lib.logging import log, require_tools +from lib.constants import tools_cache_kolibri_img + +# TODO: Move into _tools/lib +def run_qemu(start_dir = "workspace"): + require_tools(("qemu-system-i386",)) + + qemu_command = f"qemu-system-i386" + flags = "" + flags += "-L . " # IDK why it does not work without this + flags += "-m 128 " + flags += f"-drive format=raw,file={start_dir}/kolibri.img,index=0,if=floppy -boot a " + flags += "-vga vmware " + flags += "-net nic,model=rtl8139 -net user " + if is_win32(): + qemu_full_path = shutil.which(qemu_command) + qemu_directory = os.path.dirname(qemu_full_path) + flags += f"-L {qemu_directory} " + s = f"{qemu_command} {flags}" + qemu_stdout = open(f"{start_dir}/qemu_stdout.log", "w") + qemu_stderr = open(f"{start_dir}/qemu_stderr.log", "w") + if is_win32(): + return subprocess.Popen(s, bufsize = 0, stdout = qemu_stdout, stderr = qemu_stderr, stdin = subprocess.DEVNULL, shell = True, start_new_session = True) + else: + a = shlex.split(s) + return subprocess.Popen(a, bufsize = 0, stdout = qemu_stdout, stderr = qemu_stderr, stdin = subprocess.DEVNULL, start_new_session = True) + +if __name__ == "__main__": + program_name = build() + + os.makedirs("workspace", exist_ok = True) + + # Create a copy of IMG + kolibri_img = builds_get("eng/data/data/kolibri.img", "workspace/kolibri.img") + + # Open the IMG + with open(kolibri_img, "rb") as img: + img_data = img.read() + img = Floppy(img_data) + + # Remove unuseful folders + img.delete_path("GAMES") + img.delete_path("DEMOS") + img.delete_path("3D") + + # Insert faster kernel if no --compressed-kernel flag passed + if "--compressed-kernel" not in sys.argv: + new_kernel = builds_get_contents("eng/data/kernel/trunk/kernel.mnt.pretest") + img.add_file_path("KERNEL.MNT", new_kernel) + + log("Moving program into kolibri image... ", end = "") + with open(program_name, "rb") as file: + file_data = file.read() + if not img.add_file_path(program_name.upper(), file_data): + print(f"Coudn't move {program_name} into IMG") + log("Done") + + log("Adding program to autorun.dat... ", end = "") + lines_to_add = bytes(f"\r\n/SYS/{program_name.upper()}\t\t""\t0\t# Your program", "ascii") + autorun_dat = img.extract_file_path("SETTINGS\AUTORUN.DAT") + place_for_new_lines = autorun_dat.index(b"\r\n/SYS/@TASKBAR")# b"\r\n### Hello, ASM World! ###") + autorun_dat = autorun_dat[:place_for_new_lines] + lines_to_add + autorun_dat[place_for_new_lines:] + img.delete_path("SETTINGS\AUTORUN.DAT") + img.add_file_path("SETTINGS\AUTORUN.DAT", autorun_dat) + log("Done") + + img.save(kolibri_img) + + run_qemu() diff --git a/skins/Gruvbox/Gruvbox.HC b/skins/Gruvbox/Gruvbox.HC new file mode 100644 index 0000000..c95a22d --- /dev/null +++ b/skins/Gruvbox/Gruvbox.HC @@ -0,0 +1,28 @@ +// Stolen from https://github.com/morhetz/gruvbox +public CBGR48 gr_palette_gruv[COLORS_NUM]={ + 0xebebdbdbb2b2, // BLACK + 0x454585858888, // BLUE + 0x989897971a1a, // GREEN + 0x68689d9d6a6a, // CYAN + 0xcccc24241d1d, // RED + 0xb1b162628686, // PURPLE + 0xfefe80801919, // BROWN + 0x3c3c38383636, // LTGRAY + 0x505049494545, // DKGRAY + 0x8383a5a59898, // LTBLUE + 0xb8b8bbbb2626, // LTGREEN + 0x8e8ec0c07c7c, // LTCYAN + 0xfbfb49493434, // LTRED + 0xd3d386869b9b, // LTPURPLE + 0xfafabdbd2f2f, // YELLOW + 0x282828282828 // WHITE +}; + +GrPaletteSet(gr_palette_gruv); + +// NOTE: for whatever reason when you Abort&Exit (Shift+Esc) +// a User Task (usually a terminal) the theme may reset to +// gr_palette_std. I could not figure out why and how to fix that. +// So I just made gr_palette_std to be gr_palette_gruv. +MemCpy(gr_palette_std, gr_palette_gruv, sizeof(gr_palette_gruv)); +