Compare commits
	
		
			13 Commits
		
	
	
		
			f5ddbf4a69
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cce7e96779 | |||
| 30339f0ece | |||
| b6e9c6f4db | |||
| d34ce4efd2 | |||
| 752cf011c3 | |||
| 7647f870a6 | |||
| 75d145096d | |||
| d61a707591 | |||
| 5a4d30e162 | |||
| dd2ffebca2 | |||
| 7dc9d76d23 | |||
| 9a032b1ebd | |||
| 946f4a3b5c | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | |||||||
| __pycache__ | __pycache__ | ||||||
| *.jpg | *.jpg | ||||||
|  | *.png | ||||||
							
								
								
									
										181
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | |||||||
|  | <h3>rivet - Register / Instruction Visualizer and Explainer Tool</h3> | ||||||
|  |  | ||||||
|  | --- | ||||||
|  | ## Table of contents | ||||||
|  | <!-- TOC --> | ||||||
|  |   * [Table of contents](#table-of-contents) | ||||||
|  |   * [Introduction](#introduction) | ||||||
|  |   * [Requirements](#requirements) | ||||||
|  |   * [Usage](#usage) | ||||||
|  |   * [Options](#options) | ||||||
|  |   * [Examples](#examples) | ||||||
|  |       * [config.json](#configjson) | ||||||
|  |       * [dark.json](#darkjson) | ||||||
|  |       * [blueprint.json](#blueprintjson) | ||||||
|  |       * [transparent.json](#transparentjson) | ||||||
|  |   * [Config](#config) | ||||||
|  | <!-- TOC --> | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Introduction | ||||||
|  |  | ||||||
|  | This tool lets you generate visual explanations of binary instructions, or describe the contents of a register. | ||||||
|  |  | ||||||
|  | The layout and style can be customized to fit your needs. | ||||||
|  |  | ||||||
|  | For a full description of the schema format, please check out [format.md](format.md) | ||||||
|  |  | ||||||
|  | ## Requirements | ||||||
|  |  | ||||||
|  | - [Python 3+](https://www.python.org/) | ||||||
|  | - [Pygame](https://pypi.org/project/pygame/) - used to render the images | ||||||
|  | - [Beautiful Soup 4](https://pypi.org/project/beautifulsoup4/) - used to parse XML files | ||||||
|  | - [PyYAML](https://pypi.org/project/PyYAML/) - used to parse YAML files | ||||||
|  |  | ||||||
|  | ## Usage | ||||||
|  |  | ||||||
|  | Basic usage: | ||||||
|  | ```bash | ||||||
|  | python3 main.py schema.xml | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Directory mode + config: | ||||||
|  | ```bash | ||||||
|  | python3 main.py -o out/ -c config.json -d schemas/ | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Options | ||||||
|  |  | ||||||
|  | Several command line options are available: | ||||||
|  | - `-o PATH` sets the output path. If not given, the output file will be located in the same directory as the input file, and given the same name (different extension) | ||||||
|  | - `-d` enables directory mode. If set, the input and output paths are directories and all files inside the input directory are processed | ||||||
|  | - `-c PATH` sets the config path. For more details on available config parameters, please read the [Config section](#config). | ||||||
|  |  | ||||||
|  | ## Examples | ||||||
|  |  | ||||||
|  | The following images were generated from this schema ([example1.yaml](example1.yaml)): | ||||||
|  |  | ||||||
|  | <details> | ||||||
|  | <summary>Show / hide</summary> | ||||||
|  |  | ||||||
|  | ```yaml | ||||||
|  | structures: | ||||||
|  |   main: | ||||||
|  |     bits: 32 | ||||||
|  |     ranges: | ||||||
|  |       31-28: | ||||||
|  |         name: cond | ||||||
|  |       27: | ||||||
|  |         name: 0 | ||||||
|  |       26: | ||||||
|  |         name: 1 | ||||||
|  |       25: | ||||||
|  |         name: I | ||||||
|  |       24: | ||||||
|  |         name: P | ||||||
|  |         description: pre / post indexing bit | ||||||
|  |         values: | ||||||
|  |           0: post, add offset after transfer | ||||||
|  |           1: pre, add offset before transfer | ||||||
|  |       23: | ||||||
|  |         name: U | ||||||
|  |         description: up / down bit | ||||||
|  |         values: | ||||||
|  |           0: down, subtract offset from base | ||||||
|  |           1: up, addition offset to base | ||||||
|  |       22: | ||||||
|  |         name: B | ||||||
|  |         description: byte / word bit | ||||||
|  |         values: | ||||||
|  |           0: transfer word quantity | ||||||
|  |           1: transfer byte quantity | ||||||
|  |       21: | ||||||
|  |         name: W | ||||||
|  |         description: write-back bit | ||||||
|  |         values: | ||||||
|  |           0: no write-back | ||||||
|  |           1: write address into base | ||||||
|  |       20: | ||||||
|  |         name: L | ||||||
|  |         description: load / store bit | ||||||
|  |         values: | ||||||
|  |           0: store to memory | ||||||
|  |           1: load from memory | ||||||
|  |       19-16: | ||||||
|  |         name: Rn | ||||||
|  |         description: base register | ||||||
|  |       15-12: | ||||||
|  |         name: Rd | ||||||
|  |         description: source / destination register | ||||||
|  |       11-0: | ||||||
|  |         name: offset | ||||||
|  |         depends-on: 25 | ||||||
|  |         values: | ||||||
|  |           0: | ||||||
|  |             description: offset is an immediate value | ||||||
|  |             structure: immediateOffset | ||||||
|  |           1: | ||||||
|  |             description: offset is a register | ||||||
|  |             structure: registerOffset | ||||||
|  |   immediateOffset: | ||||||
|  |     bits: 12 | ||||||
|  |     ranges: | ||||||
|  |       11-0: | ||||||
|  |         name: 12-bit immediate offset | ||||||
|  |         description: unsigned number | ||||||
|  |   registerOffset: | ||||||
|  |     bits: 12 | ||||||
|  |     ranges: | ||||||
|  |       11-4: | ||||||
|  |         name: shift | ||||||
|  |         description: shift applied to Rm | ||||||
|  |       3-0: | ||||||
|  |         name: Rm | ||||||
|  |         description: offset register | ||||||
|  | ``` | ||||||
|  | </details> | ||||||
|  |  | ||||||
|  | #### config.json | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #### dark.json | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #### blueprint.json | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #### transparent.json | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Config | ||||||
|  |  | ||||||
|  | The config file may change the following values to customize the layout and style: | ||||||
|  | - `defaultFontFamily`: the default font family | ||||||
|  | - `defaultFontSize`: the default font size | ||||||
|  | - `italicFontFamily`: the italic font family (for value description) | ||||||
|  | - `italicFontSize`: the italic font size | ||||||
|  | - `backgroundColor`: the image background color (ex: [222, 250, 206]) | ||||||
|  | - `textColor`: the default text color | ||||||
|  | - `linkColor`: the color of linking lines and arrows | ||||||
|  | - `borderColor`: the color of register borders | ||||||
|  | - `bitWidth`: the width of 1 bit (in pixels) | ||||||
|  | - `bitHeight`: the height of 1 bit (in pixels) | ||||||
|  | - `descriptionMargin`: the space between descriptions (in pixels) | ||||||
|  | - `dashLength`: the length of one dash (for dashed lines) | ||||||
|  | - `dashSpace`: the space between dashes (for dashed lines) | ||||||
|  | - `arrowSize`: the arrow size (height in pixels, width for horizontal arrow) | ||||||
|  | - `margins`: the margins from the borders of the image (in pixels, [top, right, bottom, left]) | ||||||
|  | - `arrowMargin`: the margin between arrows and registers (in pixels) | ||||||
|  | - `valuesGap`: the gap between values (in pixels) | ||||||
|  | - `arrowLabelDistance`: the distance between arrows and their label (in pixels) | ||||||
|  | - `forceDescsOnSide`: if true, descriptions are placed on the side of the register, otherwise, they are placed as close as possible to the bit | ||||||
|  | - `leftLabels`: if true, descriptions are put on the left, otherwise, they default to the right hand side | ||||||
|  | - `width`: the image width (in pixels) | ||||||
|  | - `height`: the image height (in pixels) | ||||||
|  |  | ||||||
|  | Some config examples are already included: | ||||||
|  | - [config.json](config.json): Default configuration values (black on white) | ||||||
|  | - [dark.json](dark.json): Dark theme (white on black) | ||||||
|  | - [blueprint.json](blueprint.json): Blueprint theme (white on blue) | ||||||
|  | - [transparent.json](transparent.json): Transparent background, for easy integration in other documents (grey on transparent) | ||||||
| @@ -17,5 +17,9 @@ | |||||||
|     "margins": [20, 20, 20, 20], |     "margins": [20, 20, 20, 20], | ||||||
|     "arrowMargin": 4, |     "arrowMargin": 4, | ||||||
|     "valuesGap": 5, |     "valuesGap": 5, | ||||||
|     "arrowLabelDistance": 5 |     "arrowLabelDistance": 5, | ||||||
|  |     "forceDescsOnSide": false, | ||||||
|  |     "leftLabels": false, | ||||||
|  |     "width": 1200, | ||||||
|  |     "height": 800 | ||||||
| } | } | ||||||
							
								
								
									
										11
									
								
								config.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								config.py
									
									
									
									
									
								
							| @@ -1,6 +1,9 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  |  | ||||||
| import json | import json | ||||||
| import re | import re | ||||||
|  |  | ||||||
|  |  | ||||||
| class Config: | class Config: | ||||||
|     DEFAULT_FONT_FAMILY = "Ubuntu Mono" |     DEFAULT_FONT_FAMILY = "Ubuntu Mono" | ||||||
|     DEFAULT_FONT_SIZE = 16 |     DEFAULT_FONT_SIZE = 16 | ||||||
| @@ -21,11 +24,16 @@ class Config: | |||||||
|     ARROW_MARGIN = 4 |     ARROW_MARGIN = 4 | ||||||
|     VALUES_GAP = 5 |     VALUES_GAP = 5 | ||||||
|     ARROW_LABEL_DISTANCE = 5 |     ARROW_LABEL_DISTANCE = 5 | ||||||
|  |     FORCE_DESCS_ON_SIDE = False | ||||||
|  |     LEFT_LABELS = False | ||||||
|  |     WIDTH = 1200 | ||||||
|  |     HEIGHT = 800 | ||||||
|      |      | ||||||
|     def __init__(self, path: str = "config.json") -> None: |     def __init__(self, path: str = "config.json") -> None: | ||||||
|         self.load(path) |         self.load(path) | ||||||
|  |  | ||||||
|     def load(self, path: str) -> None: |     @staticmethod | ||||||
|  |     def load(path: str) -> None: | ||||||
|         with open(path, "r") as f: |         with open(path, "r") as f: | ||||||
|             config = json.load(f) |             config = json.load(f) | ||||||
|              |              | ||||||
| @@ -34,5 +42,6 @@ class Config: | |||||||
|                 if hasattr(Config, k): |                 if hasattr(Config, k): | ||||||
|                     setattr(Config, k, v) |                     setattr(Config, k, v) | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|     def formatKey(key: str) -> str: |     def formatKey(key: str) -> str: | ||||||
|         return re.sub(r"([a-z])([A-Z])", r"\1_\2", key).upper() |         return re.sub(r"([a-z])([A-Z])", r"\1_\2", key).upper() | ||||||
							
								
								
									
										
											BIN
										
									
								
								example_blueprint.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_blueprint.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 63 KiB | 
							
								
								
									
										
											BIN
										
									
								
								example_dark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_dark.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 62 KiB | 
							
								
								
									
										
											BIN
										
									
								
								example_normal.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_normal.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 60 KiB | 
							
								
								
									
										
											BIN
										
									
								
								example_transparent.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_transparent.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 43 KiB | 
							
								
								
									
										125
									
								
								format.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								format.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | # Schema Format | ||||||
|  |  | ||||||
|  | _**Supported syntaxes: JSON, XML, YAML**_ | ||||||
|  |  | ||||||
|  | The following description uses the JSON syntax | ||||||
|  |  | ||||||
|  | For examples in different formats, see [example1.yaml](example1.yaml), [example2.yaml](example2.yaml), [example3.json](example3.json) and [example4.xml](example4.xml). | ||||||
|  |  | ||||||
|  | ## Main layout | ||||||
|  |  | ||||||
|  | A schema contains a dictionary of structures. There must be at least one defined structure named "main"  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "structures": { | ||||||
|  |     "main": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "struct1": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "struct2": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     ... | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Structure | ||||||
|  |  | ||||||
|  | A structure has a given number of bits and one or multiple ranges. Each range of bits can have a name, a description and / or values with special meaning (see [Range](#range)). A range's structure can also depend on another range's value (see [Dependencies](#dependencies)) | ||||||
|  |  | ||||||
|  | The range name (or key) defines the left- and rightmost bits (e.g. 7-4 goes from bit 7 to bit 4). Bits are displayed in big-endian, i.e. the leftmost bit has the highest value. | ||||||
|  | ```json | ||||||
|  | "main": { | ||||||
|  |   "bits": 8, | ||||||
|  |   "ranges": { | ||||||
|  |     "7-4": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "3-2": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "1": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "0": { | ||||||
|  |       ... | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Range | ||||||
|  |  | ||||||
|  | A range represents a group of consecutive bits. It can have a name (display in the bit cells), a description (displayed under the structure) and / or values. | ||||||
|  |  | ||||||
|  | For values depending on other ranges, see [Dependencies](#dependencies). | ||||||
|  |  | ||||||
|  | > **Note**<br> | ||||||
|  | > In YAML, make sure to wrap values in quotes because some values can be interpreted as octal notation (e.g. 010) | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | "3-2": { | ||||||
|  |   "name": "op", | ||||||
|  |   "description": "Logical operation", | ||||||
|  |   "values": { | ||||||
|  |     "00": "AND", | ||||||
|  |     "01": "OR", | ||||||
|  |     "10": "XOR", | ||||||
|  |     "11": "NAND" | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Dependencies | ||||||
|  |  | ||||||
|  | The structure of one range may depend on the value of another. To represent this situation, first indicate on the child range the range on which it depends: | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | "7-4": { | ||||||
|  |   ... | ||||||
|  |   "depends-on": "0" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Then, in its values, indicate which structure to use. A description can also be added (displayed above the horizontal dependency arrow) | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | "7-4": { | ||||||
|  |   ... | ||||||
|  |   "depends-on": "0", | ||||||
|  |   "values": { | ||||||
|  |     "0": { | ||||||
|  |       "description": "immediate value", | ||||||
|  |       "structure": "immediateValue" | ||||||
|  |     }, | ||||||
|  |     "1": { | ||||||
|  |       "description": "value in register", | ||||||
|  |       "structure": "registerValue" | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Finally, add the sub-structures to the structure dictionary: | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "structures": { | ||||||
|  |     "main": { | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "immediateValue": { | ||||||
|  |       "bits": 4, | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     "registerValue": { | ||||||
|  |       "bits": 4, | ||||||
|  |       ... | ||||||
|  |     }, | ||||||
|  |     ... | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
							
								
								
									
										70
									
								
								main.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										70
									
								
								main.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,6 +1,70 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | from __future__ import annotations | ||||||
|  |  | ||||||
|  | import argparse | ||||||
|  | import os | ||||||
|  |  | ||||||
| from schema import InstructionSetSchema | from schema import InstructionSetSchema | ||||||
|  |  | ||||||
|  | description = """Examples: | ||||||
|  |     - Default theme (black on white): | ||||||
|  |         python main.py schema.xml -o out.jpg | ||||||
|  |  | ||||||
|  |     - Dark theme (white on black): | ||||||
|  |         python main.py schema.xml -o out.jpg -c dark.json | ||||||
|  |  | ||||||
|  |     - Blueprint theme (white on blue): | ||||||
|  |         python main.py schema.xml -o out.jpg -c blueprint.json | ||||||
|  |  | ||||||
|  |     - Transparent background: | ||||||
|  |         python main.py schema.xml -o out.png -c transparent.json | ||||||
|  |      | ||||||
|  |     - Directory mode: | ||||||
|  |         python main.py -d -o images/ schemas/ | ||||||
|  | """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def processFile(inPath: str, outPath: str, confPath: str, display: bool) -> None: | ||||||
|  |     schema = InstructionSetSchema(inPath, confPath, display) | ||||||
|  |     schema.save(outPath) | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     schema = InstructionSetSchema("example1.yaml") |     parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawTextHelpFormatter) | ||||||
|     schema.save("example1_v2.jpg") |     parser.add_argument("schema", help="Path to the schema description. Accepted formats are: YAML, JSON and XML") | ||||||
|     input() |     parser.add_argument("-o", "--output", help="Output path. By default, the output file will have the same name as the schema description with the extension .png") | ||||||
|  |     parser.add_argument("-c", "--config", help="Path to the config file", default="config.json") | ||||||
|  |     parser.add_argument("-D", "--display", help="Enable pygame display of the result", action="store_true") | ||||||
|  |     parser.add_argument("-d", "--directory", help="Enable directory mode. If set, the input and output paths are directories and all files inside the input directory are processed", action="store_true") | ||||||
|  |      | ||||||
|  |     args = parser.parse_args() | ||||||
|  |      | ||||||
|  |     if args.directory: | ||||||
|  |         if not os.path.isdir(args.schema): | ||||||
|  |             print(f"{args.schema} is not a directory") | ||||||
|  |             exit(-1) | ||||||
|  |          | ||||||
|  |         output = args.output | ||||||
|  |         if output is None: | ||||||
|  |             output = args.schema | ||||||
|  |          | ||||||
|  |         if not os.path.isdir(output): | ||||||
|  |             print(f"{output} is not a directory") | ||||||
|  |             exit(-1) | ||||||
|  |          | ||||||
|  |         paths = os.listdir(args.schema) | ||||||
|  |         for path in paths: | ||||||
|  |             inPath = os.path.join(args.schema, path) | ||||||
|  |             outPath = os.path.join(output, path) | ||||||
|  |             outPath = os.path.splitext(outPath)[0] + ".png" | ||||||
|  |              | ||||||
|  |             processFile(inPath, outPath, args.config, args.display) | ||||||
|  |      | ||||||
|  |     else: | ||||||
|  |         output = args.output | ||||||
|  |         if output is None: | ||||||
|  |             output = os.path.splitext(args.schema)[0] + ".png" | ||||||
|  |          | ||||||
|  |         processFile(args.schema, output, args.config, args.display) | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								range.py
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								range.py
									
									
									
									
									
								
							| @@ -1,5 +1,7 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
| from typing import Union |  | ||||||
|  | from typing import Union, Optional | ||||||
|  |  | ||||||
|  |  | ||||||
| class Range: | class Range: | ||||||
|     def __init__(self, |     def __init__(self, | ||||||
| @@ -7,21 +9,23 @@ class Range: | |||||||
|                  end: int, |                  end: int, | ||||||
|                  name: str, |                  name: str, | ||||||
|                  description: str = "", |                  description: str = "", | ||||||
|                  values: dict[str, Union[str, dict]] = None, |                  values: Optional[dict[str, Union[str, dict]]] = None, | ||||||
|                  dependsOn: str = None) -> None: |                  dependsOn: Optional[tuple[int, int]] = None) -> None: | ||||||
|          |          | ||||||
|         self.start = start |         self.start: int = start | ||||||
|         self.end = end |         self.end: int = end | ||||||
|         self.name = name |         self.name: str = name | ||||||
|         self.description = description |         self.description: str = description | ||||||
|         self.values = values |         self.values: Optional[dict[str, Union[str, dict]]] = values | ||||||
|         self.dependsOn = dependsOn |         self.dependsOn: Optional[tuple[int, int]] = dependsOn | ||||||
|  |         self.lastValueY: int = -1 | ||||||
|      |      | ||||||
|     @property |     @property | ||||||
|     def bits(self) -> int: |     def bits(self) -> int: | ||||||
|         return self.end - self.start + 1 |         return self.end - self.start + 1 | ||||||
|  |  | ||||||
|     def load(start: int, end: int, data: dict): |     @staticmethod | ||||||
|  |     def load(start: int, end: int, data: dict) -> Range: | ||||||
|         values = None |         values = None | ||||||
|         bits = end - start + 1 |         bits = end - start + 1 | ||||||
|          |          | ||||||
| @@ -42,9 +46,11 @@ class Range: | |||||||
|                      values, |                      values, | ||||||
|                      dependsOn) |                      dependsOn) | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|     def parseSpan(span: str) -> tuple[int, int]: |     def parseSpan(span: str) -> tuple[int, int]: | ||||||
|         startEnd = span.split("-") |         startEnd = span.split("-") | ||||||
|         if len(startEnd) == 1: startEnd.append(startEnd[0]) |         if len(startEnd) == 1: | ||||||
|  |             startEnd.append(startEnd[0]) | ||||||
|         start = int(startEnd[1]) |         start = int(startEnd[1]) | ||||||
|         end = int(startEnd[0]) |         end = int(startEnd[0]) | ||||||
|         return (start, end) |         return (start, end) | ||||||
							
								
								
									
										152
									
								
								renderer.py
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								renderer.py
									
									
									
									
									
								
							| @@ -1,43 +1,62 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
|  | import os | ||||||
| from typing import TYPE_CHECKING | from typing import TYPE_CHECKING | ||||||
| if TYPE_CHECKING: |  | ||||||
|     from config import Config |  | ||||||
|     from range import Range |  | ||||||
|     from schema import InstructionSetSchema |  | ||||||
|  |  | ||||||
| import pygame | import pygame | ||||||
|  |  | ||||||
| from structure import Structure | from structure import Structure | ||||||
| from vec import Vec | from vec import Vec | ||||||
|  |  | ||||||
|  | if TYPE_CHECKING: | ||||||
|  |     from config import Config | ||||||
|  |     from range import Range | ||||||
|  |     from schema import InstructionSetSchema | ||||||
|  |  | ||||||
|  |  | ||||||
| class Renderer: | class Renderer: | ||||||
|     WIDTH = 1200 |     def __init__(self, config: Config, display: bool = False) -> None: | ||||||
|     HEIGHT = 800 |         self.config: Config = config | ||||||
|      |         self.display: bool = display | ||||||
|     def __init__(self, config: Config) -> None: |  | ||||||
|         self.config = config |  | ||||||
|         pygame.init() |         pygame.init() | ||||||
|         self.win = pygame.display.set_mode([Renderer.WIDTH, Renderer.HEIGHT]) |         if self.display: | ||||||
|         self.font = pygame.font.SysFont(self.config.DEFAULT_FONT_FAMILY, self.config.DEFAULT_FONT_SIZE) |             self.win: pygame.Surface = pygame.display.set_mode([self.config.WIDTH, self.config.HEIGHT]) | ||||||
|         self.italicFont = pygame.font.SysFont(self.config.ITALIC_FONT_FAMILY, self.config.ITALIC_FONT_SIZE, italic=True) |  | ||||||
|          |          | ||||||
|         self.margins = self.config.MARGINS |         self.surf: pygame.Surface = pygame.Surface([self.config.WIDTH, self.config.HEIGHT], pygame.SRCALPHA) | ||||||
|  |  | ||||||
|  |         self.font: pygame.font.Font = pygame.font.SysFont( | ||||||
|  |             self.config.DEFAULT_FONT_FAMILY, | ||||||
|  |             self.config.DEFAULT_FONT_SIZE) | ||||||
|  |  | ||||||
|  |         self.italicFont: pygame.font.Font = pygame.font.SysFont( | ||||||
|  |             self.config.ITALIC_FONT_FAMILY, | ||||||
|  |             self.config.ITALIC_FONT_SIZE, italic=True) | ||||||
|  |          | ||||||
|  |         self.margins: list[int, int, int, int] = self.config.MARGINS | ||||||
|      |      | ||||||
|     def render(self, schema: InstructionSetSchema) -> None: |     def render(self, schema: InstructionSetSchema) -> None: | ||||||
|  |         self.surf.fill(self.config.BACKGROUND_COLOR) | ||||||
|          |          | ||||||
|  |         mainStruct: Structure = schema.structures["main"] | ||||||
|  |         ox = self.margins[3] | ||||||
|  |         if self.config.LEFT_LABELS: | ||||||
|  |             ox = self.config.WIDTH - self.margins[3] - mainStruct.bits * self.config.BIT_WIDTH | ||||||
|  |          | ||||||
|  |         self.drawStructure(schema.structures["main"], schema.structures, ox, self.margins[0]) | ||||||
|  |          | ||||||
|  |         if self.display: | ||||||
|  |             name = os.path.basename(schema.path) | ||||||
|  |             pygame.display.set_caption(f"Rivet - {name}") | ||||||
|             self.win.fill(self.config.BACKGROUND_COLOR) |             self.win.fill(self.config.BACKGROUND_COLOR) | ||||||
|          |             self.win.blit(self.surf, [0, 0]) | ||||||
|         self.drawStructure(schema.structures["main"], schema.structures, self.margins[3], self.margins[0]) |  | ||||||
|          |  | ||||||
|             pygame.display.flip() |             pygame.display.flip() | ||||||
|      |      | ||||||
|     def save(self, path: str) -> None: |     def save(self, path: str) -> None: | ||||||
|         pygame.image.save(self.win, path) |         pygame.image.save(self.surf, path) | ||||||
|      |      | ||||||
|     def drawStructure(self, |     def drawStructure(self, | ||||||
|                       struct: Structure, |                       struct: Structure, | ||||||
|                       structures: dict[str, |                       structures: dict[str, Structure], | ||||||
|                       Structure], |  | ||||||
|                       ox: float = 0, |                       ox: float = 0, | ||||||
|                       oy: float = 0) -> float: |                       oy: float = 0) -> float: | ||||||
|          |          | ||||||
| @@ -52,23 +71,37 @@ class Renderer: | |||||||
|         startBit = struct.start |         startBit = struct.start | ||||||
|          |          | ||||||
|         # Draw rectangle around structure |         # Draw rectangle around structure | ||||||
|         pygame.draw.rect(self.win, borderCol, [bitsX, bitsY, bitsWidth, bitH], 2) |         pygame.draw.rect(self.surf, borderCol, [bitsX, bitsY, bitsWidth, bitH], 2) | ||||||
|          |          | ||||||
|         for i in range(struct.bits): |         for i in range(struct.bits): | ||||||
|             bitX = ox + i * bitW |             bitX = ox + i * bitW | ||||||
|              |              | ||||||
|             bitITxt = self.font.render(str(struct.bits - i - 1 + startBit), True, txtCol) |             bitITxt = self.font.render(str(struct.bits - i - 1 + startBit), True, txtCol) | ||||||
|             self.win.blit(bitITxt, [ |             self.surf.blit(bitITxt, [ | ||||||
|                 bitX + (bitW - bitITxt.get_width())/2, |                 bitX + (bitW - bitITxt.get_width())/2, | ||||||
|                 oy + (bitH - bitITxt.get_height())/2 |                 oy + (bitH - bitITxt.get_height())/2 | ||||||
|             ]) |             ]) | ||||||
|              |              | ||||||
|             # Draw separator |             # Draw separator | ||||||
|             if i != 0: |             if i != 0: | ||||||
|                 pygame.draw.line(self.win, borderCol, [bitX, bitsY], [bitX, bitsY + bitH]) |                 pygame.draw.line(self.surf, borderCol, [bitX, bitsY], [bitX, bitsY + bitH]) | ||||||
|          |          | ||||||
|         ranges = struct.getSortedRanges() |         ranges = struct.getSortedRanges() | ||||||
|         descX = ox + max(0, (struct.bits-12) * bitW) |         if self.config.LEFT_LABELS: | ||||||
|  |             ranges.reverse() | ||||||
|  |          | ||||||
|  |         if self.config.FORCE_DESCS_ON_SIDE: | ||||||
|  |             if self.config.LEFT_LABELS: | ||||||
|  |                 descX = self.config.WIDTH - self.margins[3] - structures["main"].bits * bitW | ||||||
|  |             else: | ||||||
|  |                 descX = self.margins[3] + structures["main"].bits * bitW | ||||||
|  |          | ||||||
|  |         else: | ||||||
|  |             if self.config.LEFT_LABELS: | ||||||
|  |                 descX = ox + struct.bits * bitW | ||||||
|  |             else: | ||||||
|  |                 descX = ox | ||||||
|  |          | ||||||
|         descY = bitsY + bitH * 2 |         descY = bitsY + bitH * 2 | ||||||
|          |          | ||||||
|         # Names + simple descriptions |         # Names + simple descriptions | ||||||
| @@ -81,8 +114,8 @@ class Renderer: | |||||||
|             nameTxt = self.font.render(range_.name, True, txtCol) |             nameTxt = self.font.render(range_.name, True, txtCol) | ||||||
|             nameX = rStartX + (rWidth - nameTxt.get_width())/2 |             nameX = rStartX + (rWidth - nameTxt.get_width())/2 | ||||||
|             nameY = bitsY + (bitH - nameTxt.get_height())/2 |             nameY = bitsY + (bitH - nameTxt.get_height())/2 | ||||||
|             pygame.draw.rect(self.win, bgCol, [rStartX + bitW/2, nameY, rWidth - bitW, nameTxt.get_height()], 0) |             pygame.draw.rect(self.surf, bgCol, [rStartX + bitW/2, nameY, rWidth - bitW, nameTxt.get_height()], 0) | ||||||
|             self.win.blit(nameTxt, [nameX, nameY]) |             self.surf.blit(nameTxt, [nameX, nameY]) | ||||||
|  |  | ||||||
|             if range_.description: |             if range_.description: | ||||||
|                 descX, descY = self.drawDescription(range_, rStartX, bitsY, rWidth, descX, descY) |                 descX, descY = self.drawDescription(range_, rStartX, bitsY, rWidth, descX, descY) | ||||||
| @@ -103,7 +136,7 @@ class Renderer: | |||||||
|         y0 = bitsY + bitH * 1.25 |         y0 = bitsY + bitH * 1.25 | ||||||
|         y1 = bitsY + bitH * 1.5 |         y1 = bitsY + bitH * 1.5 | ||||||
|          |          | ||||||
|         pygame.draw.lines(self.win, self.config.LINK_COLOR, False, [ |         pygame.draw.lines(self.surf, self.config.LINK_COLOR, False, [ | ||||||
|             [x0, y0], |             [x0, y0], | ||||||
|             [x0, y1], |             [x0, y1], | ||||||
|             [x1, y1], |             [x1, y1], | ||||||
| @@ -114,10 +147,16 @@ class Renderer: | |||||||
|         bitH = self.config.BIT_HEIGHT |         bitH = self.config.BIT_HEIGHT | ||||||
|         arrowMargin = self.config.ARROW_MARGIN |         arrowMargin = self.config.ARROW_MARGIN | ||||||
|          |          | ||||||
|         pygame.draw.lines(self.win, self.config.LINK_COLOR, False, [ |         if endX > startX: | ||||||
|  |             endX -= arrowMargin | ||||||
|  |          | ||||||
|  |         else: | ||||||
|  |             endX += arrowMargin | ||||||
|  |          | ||||||
|  |         pygame.draw.lines(self.surf, self.config.LINK_COLOR, False, [ | ||||||
|             [startX, startY + bitH*1.5], |             [startX, startY + bitH*1.5], | ||||||
|             [startX, endY + bitH/2], |             [startX, endY + bitH/2], | ||||||
|             [endX - arrowMargin, endY + bitH/2] |             [endX, endY + bitH/2] | ||||||
|         ]) |         ]) | ||||||
|      |      | ||||||
|     def drawDescription(self, |     def drawDescription(self, | ||||||
| @@ -131,6 +170,9 @@ class Renderer: | |||||||
|         bitW = self.config.BIT_WIDTH |         bitW = self.config.BIT_WIDTH | ||||||
|         bitH = self.config.BIT_HEIGHT |         bitH = self.config.BIT_HEIGHT | ||||||
|          |          | ||||||
|  |         if self.config.LEFT_LABELS: | ||||||
|  |             descX = min(descX, rStartX + rWidth/2 - bitW) | ||||||
|  |         else: | ||||||
|             descX = max(descX, rStartX + rWidth/2 + bitW) |             descX = max(descX, rStartX + rWidth/2 + bitW) | ||||||
|          |          | ||||||
|         self.drawUnderbracket(rStartX, rStartX + rWidth, rStartY) |         self.drawUnderbracket(rStartX, rStartX + rWidth, rStartY) | ||||||
| @@ -139,12 +181,17 @@ class Renderer: | |||||||
|         self.drawLink(midX, rStartY, descX, descY) |         self.drawLink(midX, rStartY, descX, descY) | ||||||
|          |          | ||||||
|         descTxt = self.font.render(range_.description, True, self.config.TEXT_COLOR) |         descTxt = self.font.render(range_.description, True, self.config.TEXT_COLOR) | ||||||
|         self.win.blit(descTxt, [descX, descY + (bitH - descTxt.get_height())/2]) |         txtX = descX | ||||||
|  |          | ||||||
|  |         if self.config.LEFT_LABELS: | ||||||
|  |             txtX -= descTxt.get_width() | ||||||
|  |          | ||||||
|  |         self.surf.blit(descTxt, [txtX, descY + (bitH - descTxt.get_height())/2]) | ||||||
|          |          | ||||||
|         descY += descTxt.get_height() |         descY += descTxt.get_height() | ||||||
|          |          | ||||||
|         if range_.values is not None and range_.dependsOn is None: |         if range_.values is not None and range_.dependsOn is None: | ||||||
|             descX, descY = self.drawValues(range_.values, descX, descY) |             txtX, descY = self.drawValues(range_.values, txtX, descY) | ||||||
|          |          | ||||||
|         descY += self.config.DESCRIPTION_MARGIN |         descY += self.config.DESCRIPTION_MARGIN | ||||||
|          |          | ||||||
| @@ -158,15 +205,14 @@ class Renderer: | |||||||
|         for val, desc in sorted(values.items(), key=lambda vd: vd[0]): |         for val, desc in sorted(values.items(), key=lambda vd: vd[0]): | ||||||
|             descY += gap |             descY += gap | ||||||
|             valTxt = self.italicFont.render(f"{val} = {desc}", True, textCol) |             valTxt = self.italicFont.render(f"{val} = {desc}", True, textCol) | ||||||
|             self.win.blit(valTxt, [descX + bitW/2, descY]) |             self.surf.blit(valTxt, [descX + bitW/2, descY]) | ||||||
|             descY += valTxt.get_height() |             descY += valTxt.get_height() | ||||||
|          |          | ||||||
|         return (descX, descY) |         return (descX, descY) | ||||||
|  |  | ||||||
|     def drawDependency(self, |     def drawDependency(self, | ||||||
|                        struct: Structure, |                        struct: Structure, | ||||||
|                        structures: dict[str, |                        structures: dict[str, Structure], | ||||||
|                        Structure], |  | ||||||
|                        bitsX: float, |                        bitsX: float, | ||||||
|                        bitsY: float, |                        bitsY: float, | ||||||
|                        range_: Range, |                        range_: Range, | ||||||
| @@ -182,8 +228,13 @@ class Renderer: | |||||||
|         rWidth = range_.bits * bitW |         rWidth = range_.bits * bitW | ||||||
|          |          | ||||||
|         self.drawUnderbracket(rStartX, rStartX + rWidth, bitsY) |         self.drawUnderbracket(rStartX, rStartX + rWidth, bitsY) | ||||||
|         prevY = bitsY + bitH * 1.5 |  | ||||||
|         dependRange = struct.ranges[range_.dependsOn] |         dependRange = struct.ranges[range_.dependsOn] | ||||||
|  |         prevRangeY = bitsY + bitH * 1.5 | ||||||
|  |         if dependRange.lastValueY == -1: | ||||||
|  |             prevDependY = bitsY + bitH * 1.5 | ||||||
|  |         else: | ||||||
|  |             prevDependY = dependRange.lastValueY | ||||||
|  |          | ||||||
|         dependStartI = struct.bits - dependRange.end - 1 |         dependStartI = struct.bits - dependRange.end - 1 | ||||||
|         dependStartX = bitsX + dependStartI * bitW |         dependStartX = bitsX + dependStartI * bitW | ||||||
|         dependWidth = dependRange.bits * bitW |         dependWidth = dependRange.bits * bitW | ||||||
| @@ -191,7 +242,7 @@ class Renderer: | |||||||
|         self.drawUnderbracket(dependStartX, dependStartX + dependWidth, bitsY) |         self.drawUnderbracket(dependStartX, dependStartX + dependWidth, bitsY) | ||||||
|          |          | ||||||
|         for val, data in sorted(range_.values.items(), key=lambda vd: vd[0]): |         for val, data in sorted(range_.values.items(), key=lambda vd: vd[0]): | ||||||
|             self.drawArrow(dependMid, prevY, dependMid, descY - arrowMargin) |             self.drawArrow(dependMid, prevDependY, dependMid, descY - arrowMargin) | ||||||
|              |              | ||||||
|             valRanges = {} |             valRanges = {} | ||||||
|             for i in range(dependRange.bits): |             for i in range(dependRange.bits): | ||||||
| @@ -208,19 +259,28 @@ class Renderer: | |||||||
|              |              | ||||||
|             self.drawStructure(valStruct, structures, dependStartX, descY) |             self.drawStructure(valStruct, structures, dependStartX, descY) | ||||||
|              |              | ||||||
|             valueRight = dependStartX + dependWidth |             y = descY + bitH * 1.5 | ||||||
|             self.drawArrow(valueRight + arrowMargin, |              | ||||||
|                            descY + bitH*1.5, |             # Arrow from left to right | ||||||
|                            rStartX - arrowMargin, |             if dependRange.end > range_.start: | ||||||
|                            descY + bitH*1.5, |                 x1 = dependStartX + dependWidth + arrowMargin | ||||||
|                            data["description"]) |                 x2 = rStartX - arrowMargin | ||||||
|  |              | ||||||
|  |             # Arrow from right to left | ||||||
|  |             else: | ||||||
|  |                 x1 = dependStartX - arrowMargin | ||||||
|  |                 x2 = rStartX + rWidth + arrowMargin | ||||||
|  |              | ||||||
|  |             self.drawArrow(x1, y, x2, y, data["description"]) | ||||||
|              |              | ||||||
|             self.drawArrow(rStartX + rWidth - bitW, |             self.drawArrow(rStartX + rWidth - bitW, | ||||||
|                            prevY, |                            prevRangeY, | ||||||
|                            rStartX + rWidth - bitW, |                            rStartX + rWidth - bitW, | ||||||
|                            descY + bitH - arrowMargin) |                            descY + bitH - arrowMargin) | ||||||
|  |  | ||||||
|             prevY = descY + bitH*2 + arrowMargin |             prevDependY = descY + bitH*2 + arrowMargin | ||||||
|  |             prevRangeY = prevDependY | ||||||
|  |             dependRange.lastValueY = prevDependY | ||||||
|             descY = self.drawStructure(structures[data["structure"]], structures, rStartX, descY) |             descY = self.drawStructure(structures[data["structure"]], structures, rStartX, descY) | ||||||
|          |          | ||||||
|         return (descX, descY) |         return (descX, descY) | ||||||
| @@ -243,21 +303,21 @@ class Renderer: | |||||||
|         for i in range(dashes): |         for i in range(dashes): | ||||||
|             a = start + d * i * (dashLen + dashSpace) |             a = start + d * i * (dashLen + dashSpace) | ||||||
|             b = a + d*dashLen |             b = a + d*dashLen | ||||||
|             pygame.draw.line(self.win, linkCol, [a.x, a.y], [b.x, b.y]) |             pygame.draw.line(self.surf, linkCol, [a.x, a.y], [b.x, b.y]) | ||||||
|          |          | ||||||
|         n = Vec(d.y, -d.x) |         n = Vec(d.y, -d.x) | ||||||
|          |          | ||||||
|         width = arrowSize / 1.5 |         width = arrowSize / 1.5 | ||||||
|         p1 = end - d * arrowSize + n * width |         p1 = end - d * arrowSize + n * width | ||||||
|         p2 = end - d * arrowSize - n * width |         p2 = end - d * arrowSize - n * width | ||||||
|         pygame.draw.polygon(self.win, linkCol, [ |         pygame.draw.polygon(self.surf, linkCol, [ | ||||||
|             [end.x, end.y], |             [end.x, end.y], | ||||||
|             [p1.x, p1.y], |             [p1.x, p1.y], | ||||||
|             [p2.x, p2.y]], 0) |             [p2.x, p2.y]], 0) | ||||||
|          |          | ||||||
|         if label: |         if label: | ||||||
|             txt = self.italicFont.render(label, True, textCol) |             txt = self.italicFont.render(label, True, textCol) | ||||||
|             self.win.blit(txt, [ |             self.surf.blit(txt, [ | ||||||
|                 (start.x + end.x - txt.get_width())/2, |                 (start.x + end.x - txt.get_width())/2, | ||||||
|                 (start.y + end.y)/2 + arrowLabelDist |                 (start.y + end.y)/2 + arrowLabelDist | ||||||
|             ]) |             ]) | ||||||
							
								
								
									
										15
									
								
								schema.py
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								schema.py
									
									
									
									
									
								
							| @@ -1,5 +1,6 @@ | |||||||
| import json | import json | ||||||
| import os | import os | ||||||
|  |  | ||||||
| import yaml | import yaml | ||||||
|  |  | ||||||
| from config import Config | from config import Config | ||||||
| @@ -7,15 +8,19 @@ from renderer import Renderer | |||||||
| from structure import Structure | from structure import Structure | ||||||
| from xml_loader import XMLLoader | from xml_loader import XMLLoader | ||||||
|  |  | ||||||
|  |  | ||||||
| class UnsupportedFormatException(Exception): | class UnsupportedFormatException(Exception): | ||||||
|     ... |     ... | ||||||
|  |  | ||||||
|  |  | ||||||
| class InstructionSetSchema: | class InstructionSetSchema: | ||||||
|     VALID_EXTENSIONS = ("yaml", "json", "xml") |     VALID_EXTENSIONS = ("yaml", "json", "xml") | ||||||
|      |      | ||||||
|     def __init__(self, path: str, configPath: str = "config.json") -> None: |     def __init__(self, path: str, configPath: str = "config.json", display: bool = False) -> None: | ||||||
|         self.config = Config(configPath) |         self.config: Config = Config(configPath) | ||||||
|         self.path = path |         self.display: bool = display | ||||||
|  |         self.path: str = path | ||||||
|  |         self.structures: dict[str, Structure] = {} | ||||||
|         self.load() |         self.load() | ||||||
|      |      | ||||||
|     def load(self) -> None: |     def load(self) -> None: | ||||||
| @@ -43,6 +48,8 @@ class InstructionSetSchema: | |||||||
|             self.structures[id_] = Structure.load(id_, data) |             self.structures[id_] = Structure.load(id_, data) | ||||||
|      |      | ||||||
|     def save(self, path: str) -> None: |     def save(self, path: str) -> None: | ||||||
|         renderer = Renderer(self.config) |         renderer = Renderer(self.config, self.display) | ||||||
|         renderer.render(self) |         renderer.render(self) | ||||||
|         renderer.save(path) |         renderer.save(path) | ||||||
|  |         if self.display: | ||||||
|  |             input("Press ENTER to quit") | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								structure.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								structure.py
									
									
									
									
									
								
							| @@ -1,18 +1,21 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from range import Range | from range import Range | ||||||
|  |  | ||||||
|  |  | ||||||
| class Structure: | class Structure: | ||||||
|     def __init__(self, |     def __init__(self, | ||||||
|                  name: str, |                  name: str, | ||||||
|                  bits: int, |                  bits: int, | ||||||
|                  ranges: dict[str, Range], |                  ranges: dict[tuple[int, int], Range], | ||||||
|                  start: int = 0) -> None: |                  start: int = 0) -> None: | ||||||
|          |          | ||||||
|         self.name = name |         self.name: str = name | ||||||
|         self.bits = bits |         self.bits: int = bits | ||||||
|         self.ranges = ranges |         self.ranges: dict[tuple[int, int], Range] = ranges | ||||||
|         self.start = start |         self.start: int = start | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|     def load(id_: str, data: dict) -> Structure: |     def load(id_: str, data: dict) -> Structure: | ||||||
|         ranges = {} |         ranges = {} | ||||||
|         for rSpan, rData in data["ranges"].items(): |         for rSpan, rData in data["ranges"].items(): | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								transparent.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								transparent.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | { | ||||||
|  |     "backgroundColor": [0, 0, 0, 0], | ||||||
|  |     "textColor": [128, 128, 128], | ||||||
|  |     "linkColor": [128, 128, 128], | ||||||
|  |     "bitIColor": [128, 128, 128], | ||||||
|  |     "borderColor": [128, 128, 128] | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								vec.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								vec.py
									
									
									
									
									
								
							| @@ -1,10 +1,12 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from math import sqrt | from math import sqrt | ||||||
|  |  | ||||||
|  |  | ||||||
| class Vec: | class Vec: | ||||||
|     def __init__(self, x: float = 0, y: float = 0) -> None: |     def __init__(self, x: float = 0, y: float = 0) -> None: | ||||||
|         self.x = x |         self.x: float = x | ||||||
|         self.y = y |         self.y: float = y | ||||||
|      |      | ||||||
|     def __add__(self, v: Vec) -> Vec: |     def __add__(self, v: Vec) -> Vec: | ||||||
|         return Vec(self.x+v.x, self.y+v.y) |         return Vec(self.x+v.x, self.y+v.y) | ||||||
| @@ -23,5 +25,6 @@ class Vec: | |||||||
|      |      | ||||||
|     def norm(self) -> Vec: |     def norm(self) -> Vec: | ||||||
|         mag = self.mag() |         mag = self.mag() | ||||||
|         if mag == 0: return Vec() |         if mag == 0: | ||||||
|         return self/mag |             return Vec() | ||||||
|  |         return self / mag | ||||||
|   | |||||||
| @@ -1,11 +1,15 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
| from bs4 import BeautifulSoup |  | ||||||
| from typing import TYPE_CHECKING | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  | from bs4 import BeautifulSoup | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from io import TextIOWrapper |     from io import TextIOWrapper | ||||||
|  |  | ||||||
|  |  | ||||||
| class XMLLoader: | class XMLLoader: | ||||||
|  |     @staticmethod | ||||||
|     def load(file_: TextIOWrapper) -> dict: |     def load(file_: TextIOWrapper) -> dict: | ||||||
|         schema = {} |         schema = {} | ||||||
|         bs = BeautifulSoup(file_.read(), "xml") |         bs = BeautifulSoup(file_.read(), "xml") | ||||||
| @@ -18,9 +22,11 @@ class XMLLoader: | |||||||
|         schema["structures"] = structures |         schema["structures"] = structures | ||||||
|         return schema |         return schema | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|     def parseStructure(structElmt: any) -> dict: |     def parseStructure(structElmt: any) -> dict: | ||||||
|         struct = {} |         struct = { | ||||||
|         struct["bits"] = structElmt.get("bits") |             "bits": structElmt.get("bits") | ||||||
|  |         } | ||||||
|         ranges = {} |         ranges = {} | ||||||
|         rangeElmts = structElmt.findAll("range") |         rangeElmts = structElmt.findAll("range") | ||||||
|         for rangeElmt in rangeElmts: |         for rangeElmt in rangeElmts: | ||||||
| @@ -30,11 +36,14 @@ class XMLLoader: | |||||||
|         struct["ranges"] = ranges |         struct["ranges"] = ranges | ||||||
|         return struct |         return struct | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|     def parseRange(rangeElmt: any) -> dict: |     def parseRange(rangeElmt: any) -> dict: | ||||||
|         range_ = {} |         range_ = { | ||||||
|         range_["name"] = rangeElmt.get("name") |             "name": rangeElmt.get("name") | ||||||
|  |         } | ||||||
|         desc = rangeElmt.find("description") |         desc = rangeElmt.find("description") | ||||||
|         if desc is not None: range_["description"] = desc.getText() |         if desc is not None: | ||||||
|  |             range_["description"] = desc.getText() | ||||||
|          |          | ||||||
|         valuesElmt = rangeElmt.find("values") |         valuesElmt = rangeElmt.find("values") | ||||||
|         if valuesElmt is not None: |         if valuesElmt is not None: | ||||||
| @@ -45,6 +54,7 @@ class XMLLoader: | |||||||
|          |          | ||||||
|         return range_ |         return range_ | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|     def parseValues(valuesElmt: any) -> dict: |     def parseValues(valuesElmt: any) -> dict: | ||||||
|         values = {} |         values = {} | ||||||
|         caseElmts = valuesElmt.findAll("case") |         caseElmts = valuesElmt.findAll("case") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user