diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..48ff532b2
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,15 @@
+# Ignore changes in generated files
+src/generated/resources/data/** linguist-generated
+
+* text=auto
+
+*.gradle eol=lf diff=java
+*.java eol=lf diff=java
+*.kt eol=lf diff=java
+*.lua eol=lf
+*.md eol=lf diff=markdown
+*.txt eol=lf
+
+*.png binary
+*.jar binary
+*.dfpwm binary
diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml
index 014abc079..c2df1eb81 100644
--- a/.github/workflows/main-ci.yml
+++ b/.github/workflows/main-ci.yml
@@ -12,10 +12,10 @@ jobs:
with:
submodules: true
- - name: Set up Java 16
+ - name: Set up Java 17
uses: actions/setup-java@v1
with:
- java-version: 16
+ java-version: 17
- name: Cache gradle dependencies
uses: actions/cache@v2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 66d760891..c3533f270 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,6 +25,9 @@ jobs:
with:
java-version: 17
+ - name: Pull submodules
+ run: git submodule update --init --recursive
+
- name: Grant execute permission for gradlew
run: chmod +x gradlew
diff --git a/.gitignore b/.gitignore
index 8288d3541..a61a3b460 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,5 @@
*.DS_Store
.project
*.launch
+
+/src/generated/resources/.cache
diff --git a/build.gradle b/build.gradle
index 22f551eb3..a0f2c9a96 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,8 +3,8 @@ plugins {
id "jacoco"
id "maven-publish"
id "com.github.hierynomus.license" version "0.16.1"
- id "org.jetbrains.kotlin.jvm" version "1.5.21"
- id 'fabric-loom' version '0.10-SNAPSHOT'
+ id "org.jetbrains.kotlin.jvm" version "1.6.0"
+ id 'fabric-loom' version '0.11-SNAPSHOT'
id "com.modrinth.minotaur" version "2.+"
id 'com.matthewprenger.cursegradle' version '1.4.0'
}
@@ -40,6 +40,18 @@ sourceSets {
loom {
accessWidenerPath = file("src/main/resources/cc.accesswidener")
+
+ runs {
+ data {
+ server()
+
+ name "Data Generation"
+ vmArg "-Dfabric-api.datagen"
+ vmArg "-Dfabric-api.datagen.output-dir=${file("src/generated/resources")}"
+
+ runDir "build/datagen"
+ }
+ }
}
repositories {
@@ -92,10 +104,11 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
testImplementation 'org.hamcrest:hamcrest:2.2'
- testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
- testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
+ testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0'
+ testImplementation 'org.jetbrains.kotlin:kotlin-reflect:1.6.0'
+ testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
- cctJavadoc 'cc.tweaked:cct-javadoc:1.4.2'
+ cctJavadoc 'cc.tweaked:cct-javadoc:1.4.5'
}
processResources {
diff --git a/doc/events/mouse_click.md b/doc/events/mouse_click.md
index 83d371260..ed4f2e3eb 100644
--- a/doc/events/mouse_click.md
+++ b/doc/events/mouse_click.md
@@ -19,8 +19,8 @@ numerical value depending on which button on your mouse was last pressed when th
Button code Mouse button
1 Left button
- 2 Middle button
- 3 Right button
+ 2 Right button
+ 3 Middle button
## Example
diff --git a/gradle.properties b/gradle.properties
index e5df156c2..f63a329e7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,11 +2,11 @@
org.gradle.jvmargs=-Xmx3G
# Mod properties
-mod_version=1.100.5
+mod_version=1.100.6
# Minecraft properties
mc_version=1.18.2
-fabric_api_version=0.47.8+1.18.2
+fabric_api_version=0.51.1+1.18.2
fabric_loader_version=0.13.3
cloth_api_version=2.0.54
diff --git a/jitpack.yml b/jitpack.yml
index b6056a9b3..10f7636fe 100644
--- a/jitpack.yml
+++ b/jitpack.yml
@@ -1,5 +1,5 @@
before_install:
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
- - source ./install-jdk.sh --feature 16 --license GPL
+ - source ./install-jdk.sh --feature 17 --license GPL
jdk:
- openjdk16
diff --git a/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json b/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json
new file mode 100644
index 000000000..9577ea9b4
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/computer_advanced.json
@@ -0,0 +1,52 @@
+{
+ "variants": {
+ "facing=east,state=blinking": {
+ "y": 90,
+ "model": "computercraft:block/computer_advanced_blinking"
+ },
+ "facing=east,state=off": {
+ "y": 90,
+ "model": "computercraft:block/computer_advanced_off"
+ },
+ "facing=east,state=on": {
+ "y": 90,
+ "model": "computercraft:block/computer_advanced_on"
+ },
+ "facing=north,state=blinking": {
+ "y": 0,
+ "model": "computercraft:block/computer_advanced_blinking"
+ },
+ "facing=north,state=off": {
+ "y": 0,
+ "model": "computercraft:block/computer_advanced_off"
+ },
+ "facing=north,state=on": {
+ "y": 0,
+ "model": "computercraft:block/computer_advanced_on"
+ },
+ "facing=south,state=blinking": {
+ "y": 180,
+ "model": "computercraft:block/computer_advanced_blinking"
+ },
+ "facing=south,state=off": {
+ "y": 180,
+ "model": "computercraft:block/computer_advanced_off"
+ },
+ "facing=south,state=on": {
+ "y": 180,
+ "model": "computercraft:block/computer_advanced_on"
+ },
+ "facing=west,state=blinking": {
+ "y": 270,
+ "model": "computercraft:block/computer_advanced_blinking"
+ },
+ "facing=west,state=off": {
+ "y": 270,
+ "model": "computercraft:block/computer_advanced_off"
+ },
+ "facing=west,state=on": {
+ "y": 270,
+ "model": "computercraft:block/computer_advanced_on"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/computer_command.json b/src/generated/resources/assets/computercraft/blockstates/computer_command.json
new file mode 100644
index 000000000..1f811b2eb
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/computer_command.json
@@ -0,0 +1,52 @@
+{
+ "variants": {
+ "facing=east,state=blinking": {
+ "y": 90,
+ "model": "computercraft:block/computer_command_blinking"
+ },
+ "facing=east,state=off": {
+ "y": 90,
+ "model": "computercraft:block/computer_command_off"
+ },
+ "facing=east,state=on": {
+ "y": 90,
+ "model": "computercraft:block/computer_command_on"
+ },
+ "facing=north,state=blinking": {
+ "y": 0,
+ "model": "computercraft:block/computer_command_blinking"
+ },
+ "facing=north,state=off": {
+ "y": 0,
+ "model": "computercraft:block/computer_command_off"
+ },
+ "facing=north,state=on": {
+ "y": 0,
+ "model": "computercraft:block/computer_command_on"
+ },
+ "facing=south,state=blinking": {
+ "y": 180,
+ "model": "computercraft:block/computer_command_blinking"
+ },
+ "facing=south,state=off": {
+ "y": 180,
+ "model": "computercraft:block/computer_command_off"
+ },
+ "facing=south,state=on": {
+ "y": 180,
+ "model": "computercraft:block/computer_command_on"
+ },
+ "facing=west,state=blinking": {
+ "y": 270,
+ "model": "computercraft:block/computer_command_blinking"
+ },
+ "facing=west,state=off": {
+ "y": 270,
+ "model": "computercraft:block/computer_command_off"
+ },
+ "facing=west,state=on": {
+ "y": 270,
+ "model": "computercraft:block/computer_command_on"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/computer_normal.json b/src/generated/resources/assets/computercraft/blockstates/computer_normal.json
new file mode 100644
index 000000000..a91960e7b
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/computer_normal.json
@@ -0,0 +1,52 @@
+{
+ "variants": {
+ "facing=east,state=blinking": {
+ "y": 90,
+ "model": "computercraft:block/computer_normal_blinking"
+ },
+ "facing=east,state=off": {
+ "y": 90,
+ "model": "computercraft:block/computer_normal_off"
+ },
+ "facing=east,state=on": {
+ "y": 90,
+ "model": "computercraft:block/computer_normal_on"
+ },
+ "facing=north,state=blinking": {
+ "y": 0,
+ "model": "computercraft:block/computer_normal_blinking"
+ },
+ "facing=north,state=off": {
+ "y": 0,
+ "model": "computercraft:block/computer_normal_off"
+ },
+ "facing=north,state=on": {
+ "y": 0,
+ "model": "computercraft:block/computer_normal_on"
+ },
+ "facing=south,state=blinking": {
+ "y": 180,
+ "model": "computercraft:block/computer_normal_blinking"
+ },
+ "facing=south,state=off": {
+ "y": 180,
+ "model": "computercraft:block/computer_normal_off"
+ },
+ "facing=south,state=on": {
+ "y": 180,
+ "model": "computercraft:block/computer_normal_on"
+ },
+ "facing=west,state=blinking": {
+ "y": 270,
+ "model": "computercraft:block/computer_normal_blinking"
+ },
+ "facing=west,state=off": {
+ "y": 270,
+ "model": "computercraft:block/computer_normal_off"
+ },
+ "facing=west,state=on": {
+ "y": 270,
+ "model": "computercraft:block/computer_normal_on"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json b/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json
new file mode 100644
index 000000000..69f41596d
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/monitor_advanced.json
@@ -0,0 +1,964 @@
+{
+ "variants": {
+ "facing=east,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=north,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=south,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=west,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_advanced_d",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_advanced_l",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_advanced_ld",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_advanced_lr",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_advanced_lrd",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_advanced_lru",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_advanced_lrud",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_advanced_lu",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_advanced_lud",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_advanced",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_advanced_r",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_advanced_rd",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_advanced_ru",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_advanced_rud",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_advanced_u",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_advanced_ud",
+ "x": 270,
+ "y": 270
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json b/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json
new file mode 100644
index 000000000..f928cc82c
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/monitor_normal.json
@@ -0,0 +1,964 @@
+{
+ "variants": {
+ "facing=east,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 90,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 0,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 270,
+ "y": 90
+ },
+ "facing=east,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 270,
+ "y": 90
+ },
+ "facing=north,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 90,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 0,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 270,
+ "y": 0
+ },
+ "facing=north,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 270,
+ "y": 0
+ },
+ "facing=south,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 90,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 0,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 270,
+ "y": 180
+ },
+ "facing=south,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 270,
+ "y": 180
+ },
+ "facing=west,orientation=down,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=down,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 90,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=north,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 0,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=d": {
+ "model": "computercraft:block/monitor_normal_d",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=l": {
+ "model": "computercraft:block/monitor_normal_l",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=ld": {
+ "model": "computercraft:block/monitor_normal_ld",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lr": {
+ "model": "computercraft:block/monitor_normal_lr",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lrd": {
+ "model": "computercraft:block/monitor_normal_lrd",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lru": {
+ "model": "computercraft:block/monitor_normal_lru",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lrud": {
+ "model": "computercraft:block/monitor_normal_lrud",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lu": {
+ "model": "computercraft:block/monitor_normal_lu",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=lud": {
+ "model": "computercraft:block/monitor_normal_lud",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=none": {
+ "model": "computercraft:block/monitor_normal",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=r": {
+ "model": "computercraft:block/monitor_normal_r",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=rd": {
+ "model": "computercraft:block/monitor_normal_rd",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=ru": {
+ "model": "computercraft:block/monitor_normal_ru",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=rud": {
+ "model": "computercraft:block/monitor_normal_rud",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=u": {
+ "model": "computercraft:block/monitor_normal_u",
+ "x": 270,
+ "y": 270
+ },
+ "facing=west,orientation=up,state=ud": {
+ "model": "computercraft:block/monitor_normal_ud",
+ "x": 270,
+ "y": 270
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/speaker.json b/src/generated/resources/assets/computercraft/blockstates/speaker.json
new file mode 100644
index 000000000..80f9a8f4f
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/speaker.json
@@ -0,0 +1,19 @@
+{
+ "variants": {
+ "facing=east": {
+ "model": "computercraft:block/speaker",
+ "y": 90
+ },
+ "facing=north": {
+ "model": "computercraft:block/speaker"
+ },
+ "facing=south": {
+ "model": "computercraft:block/speaker",
+ "y": 180
+ },
+ "facing=west": {
+ "model": "computercraft:block/speaker",
+ "y": 270
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/wired_modem_full.json b/src/generated/resources/assets/computercraft/blockstates/wired_modem_full.json
new file mode 100644
index 000000000..bdae6829f
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/wired_modem_full.json
@@ -0,0 +1,16 @@
+{
+ "variants": {
+ "modem=false,peripheral=false": {
+ "model": "computercraft:block/wired_modem_full_off"
+ },
+ "modem=false,peripheral=true": {
+ "model": "computercraft:block/wired_modem_full_off_peripheral"
+ },
+ "modem=true,peripheral=false": {
+ "model": "computercraft:block/wired_modem_full_on"
+ },
+ "modem=true,peripheral=true": {
+ "model": "computercraft:block/wired_modem_full_on_peripheral"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json b/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
new file mode 100644
index 000000000..558c0a7c8
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
@@ -0,0 +1,64 @@
+{
+ "variants": {
+ "facing=down,on=false": {
+ "x": 90,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_advanced_off"
+ },
+ "facing=down,on=true": {
+ "x": 90,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_advanced_on"
+ },
+ "facing=east,on=false": {
+ "x": 0,
+ "y": 90,
+ "model": "computercraft:block/wireless_modem_advanced_off"
+ },
+ "facing=east,on=true": {
+ "x": 0,
+ "y": 90,
+ "model": "computercraft:block/wireless_modem_advanced_on"
+ },
+ "facing=north,on=false": {
+ "x": 0,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_advanced_off"
+ },
+ "facing=north,on=true": {
+ "x": 0,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_advanced_on"
+ },
+ "facing=south,on=false": {
+ "x": 0,
+ "y": 180,
+ "model": "computercraft:block/wireless_modem_advanced_off"
+ },
+ "facing=south,on=true": {
+ "x": 0,
+ "y": 180,
+ "model": "computercraft:block/wireless_modem_advanced_on"
+ },
+ "facing=up,on=false": {
+ "x": 270,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_advanced_off"
+ },
+ "facing=up,on=true": {
+ "x": 270,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_advanced_on"
+ },
+ "facing=west,on=false": {
+ "x": 0,
+ "y": 270,
+ "model": "computercraft:block/wireless_modem_advanced_off"
+ },
+ "facing=west,on=true": {
+ "x": 0,
+ "y": 270,
+ "model": "computercraft:block/wireless_modem_advanced_on"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json b/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json
new file mode 100644
index 000000000..6194bb769
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/blockstates/wireless_modem_normal.json
@@ -0,0 +1,64 @@
+{
+ "variants": {
+ "facing=down,on=false": {
+ "x": 90,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_normal_off"
+ },
+ "facing=down,on=true": {
+ "x": 90,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_normal_on"
+ },
+ "facing=east,on=false": {
+ "x": 0,
+ "y": 90,
+ "model": "computercraft:block/wireless_modem_normal_off"
+ },
+ "facing=east,on=true": {
+ "x": 0,
+ "y": 90,
+ "model": "computercraft:block/wireless_modem_normal_on"
+ },
+ "facing=north,on=false": {
+ "x": 0,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_normal_off"
+ },
+ "facing=north,on=true": {
+ "x": 0,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_normal_on"
+ },
+ "facing=south,on=false": {
+ "x": 0,
+ "y": 180,
+ "model": "computercraft:block/wireless_modem_normal_off"
+ },
+ "facing=south,on=true": {
+ "x": 0,
+ "y": 180,
+ "model": "computercraft:block/wireless_modem_normal_on"
+ },
+ "facing=up,on=false": {
+ "x": 270,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_normal_off"
+ },
+ "facing=up,on=true": {
+ "x": 270,
+ "y": 0,
+ "model": "computercraft:block/wireless_modem_normal_on"
+ },
+ "facing=west,on=false": {
+ "x": 0,
+ "y": 270,
+ "model": "computercraft:block/wireless_modem_normal_off"
+ },
+ "facing=west,on=true": {
+ "x": 0,
+ "y": 270,
+ "model": "computercraft:block/wireless_modem_normal_on"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json b/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json
new file mode 100644
index 000000000..8f38a1426
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_advanced_blinking.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_advanced_top",
+ "front": "computercraft:block/computer_advanced_front_blink",
+ "side": "computercraft:block/computer_advanced_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_advanced_off.json b/src/generated/resources/assets/computercraft/models/block/computer_advanced_off.json
new file mode 100644
index 000000000..a5f377ce2
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_advanced_off.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_advanced_top",
+ "front": "computercraft:block/computer_advanced_front",
+ "side": "computercraft:block/computer_advanced_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json b/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json
new file mode 100644
index 000000000..b85966988
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_advanced_on.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_advanced_top",
+ "front": "computercraft:block/computer_advanced_front_on",
+ "side": "computercraft:block/computer_advanced_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json b/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json
new file mode 100644
index 000000000..991b698f0
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_command_blinking.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_command_top",
+ "front": "computercraft:block/computer_command_front_blink",
+ "side": "computercraft:block/computer_command_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_command_off.json b/src/generated/resources/assets/computercraft/models/block/computer_command_off.json
new file mode 100644
index 000000000..35021b763
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_command_off.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_command_top",
+ "front": "computercraft:block/computer_command_front",
+ "side": "computercraft:block/computer_command_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_command_on.json b/src/generated/resources/assets/computercraft/models/block/computer_command_on.json
new file mode 100644
index 000000000..57016e47a
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_command_on.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_command_top",
+ "front": "computercraft:block/computer_command_front_on",
+ "side": "computercraft:block/computer_command_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json b/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json
new file mode 100644
index 000000000..b56958cc2
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_normal_blinking.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_normal_top",
+ "front": "computercraft:block/computer_normal_front_blink",
+ "side": "computercraft:block/computer_normal_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_normal_off.json b/src/generated/resources/assets/computercraft/models/block/computer_normal_off.json
new file mode 100644
index 000000000..a0b673d23
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_normal_off.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_normal_top",
+ "front": "computercraft:block/computer_normal_front",
+ "side": "computercraft:block/computer_normal_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json b/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json
new file mode 100644
index 000000000..662c3bef5
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/computer_normal_on.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/computer_normal_top",
+ "front": "computercraft:block/computer_normal_front_on",
+ "side": "computercraft:block/computer_normal_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced.json
new file mode 100644
index 000000000..a13f34e05
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_16",
+ "side": "computercraft:block/monitor_advanced_4",
+ "top": "computercraft:block/monitor_advanced_0",
+ "back": "computercraft:block/monitor_advanced_32"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_d.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_d.json
new file mode 100644
index 000000000..ff39d102c
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_d.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_20",
+ "side": "computercraft:block/monitor_advanced_7",
+ "top": "computercraft:block/monitor_advanced_0",
+ "back": "computercraft:block/monitor_advanced_36"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_item.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_item.json
new file mode 100644
index 000000000..aad0ced5f
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_item.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_15",
+ "side": "computercraft:block/monitor_advanced_4",
+ "top": "computercraft:block/monitor_advanced_0",
+ "back": "computercraft:block/monitor_advanced_32"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_l.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_l.json
new file mode 100644
index 000000000..3bfb91292
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_l.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_19",
+ "side": "computercraft:block/monitor_advanced_4",
+ "top": "computercraft:block/monitor_advanced_1",
+ "back": "computercraft:block/monitor_advanced_33"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ld.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ld.json
new file mode 100644
index 000000000..25269670d
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ld.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_31",
+ "side": "computercraft:block/monitor_advanced_7",
+ "top": "computercraft:block/monitor_advanced_1",
+ "back": "computercraft:block/monitor_advanced_45"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lr.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lr.json
new file mode 100644
index 000000000..d94c3a5cf
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lr.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_18",
+ "side": "computercraft:block/monitor_advanced_4",
+ "top": "computercraft:block/monitor_advanced_2",
+ "back": "computercraft:block/monitor_advanced_34"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lrd.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lrd.json
new file mode 100644
index 000000000..0de1d3268
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lrd.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_30",
+ "side": "computercraft:block/monitor_advanced_7",
+ "top": "computercraft:block/monitor_advanced_2",
+ "back": "computercraft:block/monitor_advanced_46"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lru.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lru.json
new file mode 100644
index 000000000..a28abc853
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lru.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_24",
+ "side": "computercraft:block/monitor_advanced_5",
+ "top": "computercraft:block/monitor_advanced_2",
+ "back": "computercraft:block/monitor_advanced_40"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lrud.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lrud.json
new file mode 100644
index 000000000..9759d442e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lrud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_27",
+ "side": "computercraft:block/monitor_advanced_6",
+ "top": "computercraft:block/monitor_advanced_2",
+ "back": "computercraft:block/monitor_advanced_43"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lu.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lu.json
new file mode 100644
index 000000000..038626c68
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lu.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_25",
+ "side": "computercraft:block/monitor_advanced_5",
+ "top": "computercraft:block/monitor_advanced_1",
+ "back": "computercraft:block/monitor_advanced_39"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lud.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lud.json
new file mode 100644
index 000000000..38e9374aa
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_lud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_28",
+ "side": "computercraft:block/monitor_advanced_6",
+ "top": "computercraft:block/monitor_advanced_1",
+ "back": "computercraft:block/monitor_advanced_42"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_r.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_r.json
new file mode 100644
index 000000000..555aab92c
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_r.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_17",
+ "side": "computercraft:block/monitor_advanced_4",
+ "top": "computercraft:block/monitor_advanced_3",
+ "back": "computercraft:block/monitor_advanced_35"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_rd.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_rd.json
new file mode 100644
index 000000000..22e0195b7
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_rd.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_29",
+ "side": "computercraft:block/monitor_advanced_7",
+ "top": "computercraft:block/monitor_advanced_3",
+ "back": "computercraft:block/monitor_advanced_47"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ru.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ru.json
new file mode 100644
index 000000000..6a503312a
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ru.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_23",
+ "side": "computercraft:block/monitor_advanced_5",
+ "top": "computercraft:block/monitor_advanced_3",
+ "back": "computercraft:block/monitor_advanced_41"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_rud.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_rud.json
new file mode 100644
index 000000000..70cd94b68
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_rud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_26",
+ "side": "computercraft:block/monitor_advanced_6",
+ "top": "computercraft:block/monitor_advanced_3",
+ "back": "computercraft:block/monitor_advanced_44"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_u.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_u.json
new file mode 100644
index 000000000..8c2271ce7
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_u.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_22",
+ "side": "computercraft:block/monitor_advanced_5",
+ "top": "computercraft:block/monitor_advanced_0",
+ "back": "computercraft:block/monitor_advanced_38"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ud.json b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ud.json
new file mode 100644
index 000000000..481c8402d
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_advanced_ud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_advanced_21",
+ "side": "computercraft:block/monitor_advanced_6",
+ "top": "computercraft:block/monitor_advanced_0",
+ "back": "computercraft:block/monitor_advanced_37"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal.json
new file mode 100644
index 000000000..b08678497
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_16",
+ "side": "computercraft:block/monitor_normal_4",
+ "top": "computercraft:block/monitor_normal_0",
+ "back": "computercraft:block/monitor_normal_32"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_d.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_d.json
new file mode 100644
index 000000000..3d6137782
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_d.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_20",
+ "side": "computercraft:block/monitor_normal_7",
+ "top": "computercraft:block/monitor_normal_0",
+ "back": "computercraft:block/monitor_normal_36"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_item.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_item.json
new file mode 100644
index 000000000..167ef1121
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_item.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_15",
+ "side": "computercraft:block/monitor_normal_4",
+ "top": "computercraft:block/monitor_normal_0",
+ "back": "computercraft:block/monitor_normal_32"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_l.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_l.json
new file mode 100644
index 000000000..3f863819a
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_l.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_19",
+ "side": "computercraft:block/monitor_normal_4",
+ "top": "computercraft:block/monitor_normal_1",
+ "back": "computercraft:block/monitor_normal_33"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_ld.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_ld.json
new file mode 100644
index 000000000..860b84d7d
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_ld.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_31",
+ "side": "computercraft:block/monitor_normal_7",
+ "top": "computercraft:block/monitor_normal_1",
+ "back": "computercraft:block/monitor_normal_45"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_lr.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lr.json
new file mode 100644
index 000000000..c69bab42d
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lr.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_18",
+ "side": "computercraft:block/monitor_normal_4",
+ "top": "computercraft:block/monitor_normal_2",
+ "back": "computercraft:block/monitor_normal_34"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_lrd.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lrd.json
new file mode 100644
index 000000000..3dd8543a7
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lrd.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_30",
+ "side": "computercraft:block/monitor_normal_7",
+ "top": "computercraft:block/monitor_normal_2",
+ "back": "computercraft:block/monitor_normal_46"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_lru.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lru.json
new file mode 100644
index 000000000..c0fe995ee
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lru.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_24",
+ "side": "computercraft:block/monitor_normal_5",
+ "top": "computercraft:block/monitor_normal_2",
+ "back": "computercraft:block/monitor_normal_40"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_lrud.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lrud.json
new file mode 100644
index 000000000..61545472f
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lrud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_27",
+ "side": "computercraft:block/monitor_normal_6",
+ "top": "computercraft:block/monitor_normal_2",
+ "back": "computercraft:block/monitor_normal_43"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_lu.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lu.json
new file mode 100644
index 000000000..5572eb3bc
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lu.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_25",
+ "side": "computercraft:block/monitor_normal_5",
+ "top": "computercraft:block/monitor_normal_1",
+ "back": "computercraft:block/monitor_normal_39"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_lud.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lud.json
new file mode 100644
index 000000000..ba56cd1cd
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_lud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_28",
+ "side": "computercraft:block/monitor_normal_6",
+ "top": "computercraft:block/monitor_normal_1",
+ "back": "computercraft:block/monitor_normal_42"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_r.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_r.json
new file mode 100644
index 000000000..bcbb7ef76
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_r.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_17",
+ "side": "computercraft:block/monitor_normal_4",
+ "top": "computercraft:block/monitor_normal_3",
+ "back": "computercraft:block/monitor_normal_35"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_rd.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_rd.json
new file mode 100644
index 000000000..469f486c0
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_rd.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_29",
+ "side": "computercraft:block/monitor_normal_7",
+ "top": "computercraft:block/monitor_normal_3",
+ "back": "computercraft:block/monitor_normal_47"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_ru.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_ru.json
new file mode 100644
index 000000000..ca937ffa3
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_ru.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_23",
+ "side": "computercraft:block/monitor_normal_5",
+ "top": "computercraft:block/monitor_normal_3",
+ "back": "computercraft:block/monitor_normal_41"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_rud.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_rud.json
new file mode 100644
index 000000000..21fb19602
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_rud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_26",
+ "side": "computercraft:block/monitor_normal_6",
+ "top": "computercraft:block/monitor_normal_3",
+ "back": "computercraft:block/monitor_normal_44"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_u.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_u.json
new file mode 100644
index 000000000..a93b45be8
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_u.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_22",
+ "side": "computercraft:block/monitor_normal_5",
+ "top": "computercraft:block/monitor_normal_0",
+ "back": "computercraft:block/monitor_normal_38"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/monitor_normal_ud.json b/src/generated/resources/assets/computercraft/models/block/monitor_normal_ud.json
new file mode 100644
index 000000000..ff10350c8
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/monitor_normal_ud.json
@@ -0,0 +1,9 @@
+{
+ "parent": "computercraft:block/monitor_base",
+ "textures": {
+ "front": "computercraft:block/monitor_normal_21",
+ "side": "computercraft:block/monitor_normal_6",
+ "top": "computercraft:block/monitor_normal_0",
+ "back": "computercraft:block/monitor_normal_37"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/speaker.json b/src/generated/resources/assets/computercraft/models/block/speaker.json
new file mode 100644
index 000000000..340881c78
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/speaker.json
@@ -0,0 +1,8 @@
+{
+ "parent": "minecraft:block/orientable",
+ "textures": {
+ "top": "computercraft:block/speaker_top",
+ "front": "computercraft:block/speaker_front",
+ "side": "computercraft:block/speaker_side"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_full_off.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_off.json
new file mode 100644
index 000000000..35bbacc4e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_off.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:block/cube_all",
+ "textures": {
+ "all": "computercraft:block/wired_modem_face"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_full_off_peripheral.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_off_peripheral.json
new file mode 100644
index 000000000..6f2d069b2
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_off_peripheral.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:block/cube_all",
+ "textures": {
+ "all": "computercraft:block/wired_modem_face_peripheral"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_full_on.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_on.json
new file mode 100644
index 000000000..67e3c7b5e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_on.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:block/cube_all",
+ "textures": {
+ "all": "computercraft:block/wired_modem_face_on"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_full_on_peripheral.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_on_peripheral.json
new file mode 100644
index 000000000..6dff54d30
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_full_on_peripheral.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:block/cube_all",
+ "textures": {
+ "all": "computercraft:block/wired_modem_face_peripheral_on"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_off.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_off.json
new file mode 100644
index 000000000..b629be888
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_off.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wired_modem_face",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_off_peripheral.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_off_peripheral.json
new file mode 100644
index 000000000..7fea9f091
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_off_peripheral.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wired_modem_face_peripheral",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_on.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_on.json
new file mode 100644
index 000000000..93a8d9fc0
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_on.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wired_modem_face_on",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wired_modem_on_peripheral.json b/src/generated/resources/assets/computercraft/models/block/wired_modem_on_peripheral.json
new file mode 100644
index 000000000..32cf21f24
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wired_modem_on_peripheral.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wired_modem_face_peripheral_on",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wireless_modem_advanced_off.json b/src/generated/resources/assets/computercraft/models/block/wireless_modem_advanced_off.json
new file mode 100644
index 000000000..5bac5468e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wireless_modem_advanced_off.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wireless_modem_advanced_face",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wireless_modem_advanced_on.json b/src/generated/resources/assets/computercraft/models/block/wireless_modem_advanced_on.json
new file mode 100644
index 000000000..c57b07dd9
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wireless_modem_advanced_on.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wireless_modem_advanced_face_on",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wireless_modem_normal_off.json b/src/generated/resources/assets/computercraft/models/block/wireless_modem_normal_off.json
new file mode 100644
index 000000000..793df335a
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wireless_modem_normal_off.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wireless_modem_normal_face",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/block/wireless_modem_normal_on.json b/src/generated/resources/assets/computercraft/models/block/wireless_modem_normal_on.json
new file mode 100644
index 000000000..5bc2e8e35
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/block/wireless_modem_normal_on.json
@@ -0,0 +1,7 @@
+{
+ "parent": "computercraft:block/modem",
+ "textures": {
+ "front": "computercraft:block/wireless_modem_normal_face_on",
+ "back": "computercraft:block/modem_back"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/computer_advanced.json b/src/generated/resources/assets/computercraft/models/item/computer_advanced.json
new file mode 100644
index 000000000..a22f16b21
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/computer_advanced.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/computer_advanced_blinking"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/computer_command.json b/src/generated/resources/assets/computercraft/models/item/computer_command.json
new file mode 100644
index 000000000..b1e795493
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/computer_command.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/computer_command_blinking"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/computer_normal.json b/src/generated/resources/assets/computercraft/models/item/computer_normal.json
new file mode 100644
index 000000000..11327e98e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/computer_normal.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/computer_normal_blinking"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/monitor_advanced.json b/src/generated/resources/assets/computercraft/models/item/monitor_advanced.json
new file mode 100644
index 000000000..62080d5e9
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/monitor_advanced.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/monitor_advanced_item"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/monitor_normal.json b/src/generated/resources/assets/computercraft/models/item/monitor_normal.json
new file mode 100644
index 000000000..62f812992
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/monitor_normal.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/monitor_normal_item"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/speaker.json b/src/generated/resources/assets/computercraft/models/item/speaker.json
new file mode 100644
index 000000000..b1512e00a
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/speaker.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/speaker"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/wired_modem.json b/src/generated/resources/assets/computercraft/models/item/wired_modem.json
new file mode 100644
index 000000000..aba155aab
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/wired_modem.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/wired_modem_off"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/wired_modem_full.json b/src/generated/resources/assets/computercraft/models/item/wired_modem_full.json
new file mode 100644
index 000000000..339d9842e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/wired_modem_full.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/wired_modem_full_off"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/wireless_modem_advanced.json b/src/generated/resources/assets/computercraft/models/item/wireless_modem_advanced.json
new file mode 100644
index 000000000..4398d6086
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/wireless_modem_advanced.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/wireless_modem_advanced_off"
+}
\ No newline at end of file
diff --git a/src/generated/resources/assets/computercraft/models/item/wireless_modem_normal.json b/src/generated/resources/assets/computercraft/models/item/wireless_modem_normal.json
new file mode 100644
index 000000000..970e4be3e
--- /dev/null
+++ b/src/generated/resources/assets/computercraft/models/item/wireless_modem_normal.json
@@ -0,0 +1,3 @@
+{
+ "parent": "computercraft:block/wireless_modem_normal_off"
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/c/tags/items/ender_pearls.json b/src/generated/resources/data/c/tags/items/ender_pearls.json
new file mode 100644
index 000000000..0b410745d
--- /dev/null
+++ b/src/generated/resources/data/c/tags/items/ender_pearls.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "minecraft:ender_pearl"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/c/tags/items/gold_blocks.json b/src/generated/resources/data/c/tags/items/gold_blocks.json
new file mode 100644
index 000000000..863d47ad2
--- /dev/null
+++ b/src/generated/resources/data/c/tags/items/gold_blocks.json
@@ -0,0 +1,6 @@
+{
+ "replace": false,
+ "values": [
+ "minecraft:gold_block"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/c/tags/items/skulls.json b/src/generated/resources/data/c/tags/items/skulls.json
new file mode 100644
index 000000000..b5a3caaf2
--- /dev/null
+++ b/src/generated/resources/data/c/tags/items/skulls.json
@@ -0,0 +1,11 @@
+{
+ "replace": false,
+ "values": [
+ "minecraft:creeper_head",
+ "minecraft:dragon_head",
+ "minecraft:player_head",
+ "minecraft:skeleton_skull",
+ "minecraft:wither_skeleton_skull",
+ "minecraft:zombie_head"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/c/tags/items/stones.json b/src/generated/resources/data/c/tags/items/stones.json
new file mode 100644
index 000000000..f336e9826
--- /dev/null
+++ b/src/generated/resources/data/c/tags/items/stones.json
@@ -0,0 +1,17 @@
+{
+ "replace": false,
+ "values": [
+ "minecraft:andesite",
+ "minecraft:diorite",
+ "minecraft:granite",
+ "minecraft:infested_stone",
+ "minecraft:stone",
+ "minecraft:polished_andesite",
+ "minecraft:polished_diorite",
+ "minecraft:polished_granite",
+ "minecraft:deepslate",
+ "minecraft:polished_deepslate",
+ "minecraft:infested_deepslate",
+ "minecraft:tuff"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/c/tags/items/wooden_chests.json b/src/generated/resources/data/c/tags/items/wooden_chests.json
new file mode 100644
index 000000000..441d80f5b
--- /dev/null
+++ b/src/generated/resources/data/c/tags/items/wooden_chests.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "minecraft:chest",
+ "minecraft:trapped_chest"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/cable.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/cable.json
new file mode 100644
index 000000000..ecec5122c
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/cable.json
@@ -0,0 +1,43 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:cable"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_modem": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:wired_modem"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:cable"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_modem",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_advanced.json
new file mode 100644
index 000000000..99eeaf6f2
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_advanced.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:computer_advanced"
+ ]
+ },
+ "criteria": {
+ "has_components": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "minecraft:redstone"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:gold_ingot"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:computer_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_components",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_command.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_command.json
new file mode 100644
index 000000000..9ea24782f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_command.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:computer_command"
+ ]
+ },
+ "criteria": {
+ "has_components": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "minecraft:command_block"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:computer_command"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_components",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_normal.json
new file mode 100644
index 000000000..4f4893931
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/computer_normal.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:computer_normal"
+ ]
+ },
+ "criteria": {
+ "has_redstone": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "c:redstone_dusts"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:computer_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_redstone",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_1.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_1.json
new file mode 100644
index 000000000..9f34f0132
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_1.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_1"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_1"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_10.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_10.json
new file mode 100644
index 000000000..00b6063f7
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_10.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_10"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_10"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_11.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_11.json
new file mode 100644
index 000000000..38561cda0
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_11.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_11"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_11"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_12.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_12.json
new file mode 100644
index 000000000..e88addd4f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_12.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_12"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_12"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_13.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_13.json
new file mode 100644
index 000000000..cd19ae33f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_13.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_13"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_13"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_14.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_14.json
new file mode 100644
index 000000000..ef0f17843
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_14.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_14"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_14"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_15.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_15.json
new file mode 100644
index 000000000..678d48d21
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_15.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_15"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_15"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_16.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_16.json
new file mode 100644
index 000000000..f5d5d597a
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_16.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_16"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_16"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_2.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_2.json
new file mode 100644
index 000000000..089eefa55
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_2.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_2"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_2"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_3.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_3.json
new file mode 100644
index 000000000..01ba3291e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_3.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_3"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_3"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_4.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_4.json
new file mode 100644
index 000000000..f9fb5a70e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_4.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_4"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_4"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_5.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_5.json
new file mode 100644
index 000000000..d0d8b138e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_5.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_5"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_5"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_6.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_6.json
new file mode 100644
index 000000000..d8c18541f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_6.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_6"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_6"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_7.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_7.json
new file mode 100644
index 000000000..1d4936bf6
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_7.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_7"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_7"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_8.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_8.json
new file mode 100644
index 000000000..dac96a1f1
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_8.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_8"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_8"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_9.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_9.json
new file mode 100644
index 000000000..bcb7a3b4f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_9.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_9"
+ ]
+ },
+ "criteria": {
+ "has_drive": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:disk_drive"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_9"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_drive",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_drive.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_drive.json
new file mode 100644
index 000000000..7fb18766e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/disk_drive.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:disk_drive"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:disk_drive"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/monitor_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/monitor_advanced.json
new file mode 100644
index 000000000..dab4faad3
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/monitor_advanced.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:monitor_advanced"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:monitor_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/monitor_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/monitor_normal.json
new file mode 100644
index 000000000..fdd3c274c
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/monitor_normal.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:monitor_normal"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:monitor_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/speaker.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/speaker.json
new file mode 100644
index 000000000..90a8720e5
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/speaker.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_advanced/computercraft/speaker"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:pocket_computer_advanced"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:speaker"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_advanced/computercraft/speaker"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..d2c68d9c2
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_advanced/computercraft/wireless_modem_advanced"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:pocket_computer_advanced"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_advanced"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_advanced/computercraft/wireless_modem_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..c43257316
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_normal.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_advanced/computercraft/wireless_modem_normal"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:pocket_computer_advanced"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_advanced/computercraft/wireless_modem_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_advanced.json
new file mode 100644
index 000000000..7fba02295
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_advanced.json
@@ -0,0 +1,45 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_computer_advanced"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_apple": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "minecraft:golden_apple"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_computer_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_apple",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_normal.json
new file mode 100644
index 000000000..5a775c486
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_normal.json
@@ -0,0 +1,45 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_computer_normal"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_apple": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "minecraft:golden_apple"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_computer_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_apple",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/speaker.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/speaker.json
new file mode 100644
index 000000000..c7f1206aa
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/speaker.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_normal/computercraft/speaker"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:pocket_computer_normal"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:speaker"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_normal/computercraft/speaker"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..ece1617c1
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_normal/computercraft/wireless_modem_advanced"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:pocket_computer_normal"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_advanced"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_normal/computercraft/wireless_modem_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..bef52186e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_normal.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:pocket_normal/computercraft/wireless_modem_normal"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:pocket_computer_normal"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:pocket_normal/computercraft/wireless_modem_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printed_book.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printed_book.json
new file mode 100644
index 000000000..5435f8029
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printed_book.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:printed_book"
+ ]
+ },
+ "criteria": {
+ "has_printer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:printer"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:printed_book"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_printer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printed_pages.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printed_pages.json
new file mode 100644
index 000000000..c02672917
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printed_pages.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:printed_pages"
+ ]
+ },
+ "criteria": {
+ "has_printer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:printer"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:printed_pages"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_printer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printer.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printer.json
new file mode 100644
index 000000000..08a2ffeba
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/printer.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:printer"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:printer"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/speaker.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/speaker.json
new file mode 100644
index 000000000..194f7caec
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/speaker.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:speaker"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:speaker"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/speaker.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/speaker.json
new file mode 100644
index 000000000..c54721424
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/speaker.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/computercraft/speaker"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:speaker"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/computercraft/speaker"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..66ee37549
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/computercraft/wireless_modem_advanced"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_advanced"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/computercraft/wireless_modem_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..ae2c6c4ab
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_normal.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/computercraft/wireless_modem_normal"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/computercraft/wireless_modem_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/crafting_table.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/crafting_table.json
new file mode 100644
index 000000000..39187358f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/crafting_table.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/crafting_table"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:crafting_table"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/crafting_table"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_axe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_axe.json
new file mode 100644
index 000000000..e026aaf7e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_axe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/diamond_axe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_axe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/diamond_axe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_hoe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_hoe.json
new file mode 100644
index 000000000..22c965bb3
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_hoe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/diamond_hoe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_hoe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/diamond_hoe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_pickaxe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_pickaxe.json
new file mode 100644
index 000000000..796b0cae0
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_pickaxe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/diamond_pickaxe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_pickaxe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/diamond_pickaxe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_shovel.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_shovel.json
new file mode 100644
index 000000000..6f5105d04
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_shovel.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/diamond_shovel"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_shovel"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/diamond_shovel"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_sword.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_sword.json
new file mode 100644
index 000000000..feaaa0562
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_sword.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/diamond_sword"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_sword"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/diamond_sword"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/netherite_pickaxe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/netherite_pickaxe.json
new file mode 100644
index 000000000..d5a841501
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/netherite_pickaxe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_advanced/minecraft/netherite_pickaxe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_advanced"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:netherite_pickaxe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_advanced/minecraft/netherite_pickaxe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/speaker.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/speaker.json
new file mode 100644
index 000000000..9580aa6c1
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/speaker.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/computercraft/speaker"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:speaker"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/computercraft/speaker"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..7b26f6f86
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/computercraft/wireless_modem_advanced"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_advanced"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/computercraft/wireless_modem_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..3e1817136
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_normal.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/computercraft/wireless_modem_normal"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "computercraft:wireless_modem_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/computercraft/wireless_modem_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/crafting_table.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/crafting_table.json
new file mode 100644
index 000000000..a499c785c
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/crafting_table.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/crafting_table"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:crafting_table"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/crafting_table"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_axe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_axe.json
new file mode 100644
index 000000000..3b3218736
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_axe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/diamond_axe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_axe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/diamond_axe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_hoe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_hoe.json
new file mode 100644
index 000000000..898fb3b0f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_hoe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/diamond_hoe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_hoe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/diamond_hoe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_pickaxe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_pickaxe.json
new file mode 100644
index 000000000..3ec2cbd33
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_pickaxe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/diamond_pickaxe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_pickaxe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/diamond_pickaxe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_shovel.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_shovel.json
new file mode 100644
index 000000000..5991f2b95
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_shovel.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/diamond_shovel"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_shovel"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/diamond_shovel"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_sword.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_sword.json
new file mode 100644
index 000000000..1e4db9448
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_sword.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/diamond_sword"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:diamond_sword"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/diamond_sword"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/netherite_pickaxe.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/netherite_pickaxe.json
new file mode 100644
index 000000000..4c586b2aa
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/netherite_pickaxe.json
@@ -0,0 +1,39 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:turtle_normal/minecraft/netherite_pickaxe"
+ ]
+ },
+ "criteria": {
+ "has_items": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:turtle_normal"
+ ]
+ },
+ {
+ "items": [
+ "minecraft:netherite_pickaxe"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:turtle_normal/minecraft/netherite_pickaxe"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_items",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem.json
new file mode 100644
index 000000000..861de293d
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem.json
@@ -0,0 +1,45 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:wired_modem"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_cable": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:cable"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:wired_modem"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_cable",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_from.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_from.json
new file mode 100644
index 000000000..a54c82034
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_from.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:wired_modem_full_from"
+ ]
+ },
+ "criteria": {
+ "has_modem": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:wired_modem"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:wired_modem_full_from"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_modem",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_to.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_to.json
new file mode 100644
index 000000000..c0eaec110
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_to.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:wired_modem_full_to"
+ ]
+ },
+ "criteria": {
+ "has_modem": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:wired_modem"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:wired_modem_full_to"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_modem",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..4b738939f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,45 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:wireless_modem_advanced"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_wireless": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:wireless_modem_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:wireless_modem_advanced"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_wireless",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..b0ce1c994
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_normal.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:wireless_modem_normal"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "tag": "computercraft:computer"
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:wireless_modem_normal"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/decorations/skull_cloudy.json b/src/generated/resources/data/computercraft/advancements/recipes/decorations/skull_cloudy.json
new file mode 100644
index 000000000..d6b316cee
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/decorations/skull_cloudy.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:skull_cloudy"
+ ]
+ },
+ "criteria": {
+ "has_monitor": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:monitor_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:skull_cloudy"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_monitor",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/advancements/recipes/decorations/skull_dan200.json b/src/generated/resources/data/computercraft/advancements/recipes/decorations/skull_dan200.json
new file mode 100644
index 000000000..168c59893
--- /dev/null
+++ b/src/generated/resources/data/computercraft/advancements/recipes/decorations/skull_dan200.json
@@ -0,0 +1,34 @@
+{
+ "parent": "minecraft:recipes/root",
+ "rewards": {
+ "recipes": [
+ "computercraft:skull_dan200"
+ ]
+ },
+ "criteria": {
+ "has_computer": {
+ "trigger": "minecraft:inventory_changed",
+ "conditions": {
+ "items": [
+ {
+ "items": [
+ "computercraft:computer_normal"
+ ]
+ }
+ ]
+ }
+ },
+ "has_the_recipe": {
+ "trigger": "minecraft:recipe_unlocked",
+ "conditions": {
+ "recipe": "computercraft:skull_dan200"
+ }
+ }
+ },
+ "requirements": [
+ [
+ "has_computer",
+ "has_the_recipe"
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/cable.json b/src/generated/resources/data/computercraft/loot_tables/blocks/cable.json
new file mode 100644
index 000000000..7addfe9db
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/cable.json
@@ -0,0 +1,52 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:cable"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ },
+ {
+ "condition": "minecraft:block_state_property",
+ "block": "computercraft:cable",
+ "properties": {
+ "cable": "true"
+ }
+ }
+ ]
+ },
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:wired_modem"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ },
+ {
+ "condition": "minecraft:inverted",
+ "term": {
+ "condition": "minecraft:block_state_property",
+ "block": "computercraft:cable",
+ "properties": {
+ "modem": "none"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/computer_advanced.json b/src/generated/resources/data/computercraft/loot_tables/blocks/computer_advanced.json
new file mode 100644
index 000000000..cdf402d03
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/computer_advanced.json
@@ -0,0 +1,34 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:dynamic",
+ "name": "computercraft:computer"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:alternative",
+ "terms": [
+ {
+ "condition": "computercraft:block_named"
+ },
+ {
+ "condition": "computercraft:has_id"
+ },
+ {
+ "condition": "minecraft:inverted",
+ "term": {
+ "condition": "computercraft:player_creative"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/computer_command.json b/src/generated/resources/data/computercraft/loot_tables/blocks/computer_command.json
new file mode 100644
index 000000000..cdf402d03
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/computer_command.json
@@ -0,0 +1,34 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:dynamic",
+ "name": "computercraft:computer"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:alternative",
+ "terms": [
+ {
+ "condition": "computercraft:block_named"
+ },
+ {
+ "condition": "computercraft:has_id"
+ },
+ {
+ "condition": "minecraft:inverted",
+ "term": {
+ "condition": "computercraft:player_creative"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/computer_normal.json b/src/generated/resources/data/computercraft/loot_tables/blocks/computer_normal.json
new file mode 100644
index 000000000..cdf402d03
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/computer_normal.json
@@ -0,0 +1,34 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:dynamic",
+ "name": "computercraft:computer"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:alternative",
+ "terms": [
+ {
+ "condition": "computercraft:block_named"
+ },
+ {
+ "condition": "computercraft:has_id"
+ },
+ {
+ "condition": "minecraft:inverted",
+ "term": {
+ "condition": "computercraft:player_creative"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/disk_drive.json b/src/generated/resources/data/computercraft/loot_tables/blocks/disk_drive.json
new file mode 100644
index 000000000..06c4e84e5
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/disk_drive.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:disk_drive"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/monitor_advanced.json b/src/generated/resources/data/computercraft/loot_tables/blocks/monitor_advanced.json
new file mode 100644
index 000000000..8df3e7df6
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/monitor_advanced.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:monitor_advanced"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/monitor_normal.json b/src/generated/resources/data/computercraft/loot_tables/blocks/monitor_normal.json
new file mode 100644
index 000000000..d190321ba
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/monitor_normal.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:monitor_normal"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/printer.json b/src/generated/resources/data/computercraft/loot_tables/blocks/printer.json
new file mode 100644
index 000000000..6d4f43ee8
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/printer.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:printer"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/speaker.json b/src/generated/resources/data/computercraft/loot_tables/blocks/speaker.json
new file mode 100644
index 000000000..d3323bde2
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/speaker.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:speaker"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/turtle_advanced.json b/src/generated/resources/data/computercraft/loot_tables/blocks/turtle_advanced.json
new file mode 100644
index 000000000..cdf402d03
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/turtle_advanced.json
@@ -0,0 +1,34 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:dynamic",
+ "name": "computercraft:computer"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:alternative",
+ "terms": [
+ {
+ "condition": "computercraft:block_named"
+ },
+ {
+ "condition": "computercraft:has_id"
+ },
+ {
+ "condition": "minecraft:inverted",
+ "term": {
+ "condition": "computercraft:player_creative"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/turtle_normal.json b/src/generated/resources/data/computercraft/loot_tables/blocks/turtle_normal.json
new file mode 100644
index 000000000..cdf402d03
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/turtle_normal.json
@@ -0,0 +1,34 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:dynamic",
+ "name": "computercraft:computer"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:alternative",
+ "terms": [
+ {
+ "condition": "computercraft:block_named"
+ },
+ {
+ "condition": "computercraft:has_id"
+ },
+ {
+ "condition": "minecraft:inverted",
+ "term": {
+ "condition": "computercraft:player_creative"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/wired_modem_full.json b/src/generated/resources/data/computercraft/loot_tables/blocks/wired_modem_full.json
new file mode 100644
index 000000000..7ba86d6d6
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/wired_modem_full.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:wired_modem_full"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/loot_tables/blocks/wireless_modem_advanced.json
new file mode 100644
index 000000000..3e2daf5e5
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/wireless_modem_advanced.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:wireless_modem_advanced"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/loot_tables/blocks/wireless_modem_normal.json b/src/generated/resources/data/computercraft/loot_tables/blocks/wireless_modem_normal.json
new file mode 100644
index 000000000..0fba46907
--- /dev/null
+++ b/src/generated/resources/data/computercraft/loot_tables/blocks/wireless_modem_normal.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1.0,
+ "bonus_rolls": 0.0,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "computercraft:wireless_modem_normal"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/cable.json b/src/generated/resources/data/computercraft/recipes/cable.json
new file mode 100644
index 000000000..02911c183
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/cable.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ " # ",
+ "#R#",
+ " # "
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ }
+ },
+ "result": {
+ "item": "computercraft:cable",
+ "count": 6
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/colour.json b/src/generated/resources/data/computercraft/recipes/colour.json
new file mode 100644
index 000000000..f8e494be2
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/colour.json
@@ -0,0 +1,3 @@
+{
+ "type": "computercraft:colour"
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/computer_advanced.json b/src/generated/resources/data/computercraft/recipes/computer_advanced.json
new file mode 100644
index 000000000..b638062fd
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/computer_advanced.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#R#",
+ "#G#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:gold_ingots"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:computer_advanced"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/computer_command.json b/src/generated/resources/data/computercraft/recipes/computer_command.json
new file mode 100644
index 000000000..e9db75032
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/computer_command.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#R#",
+ "#G#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:gold_ingots"
+ },
+ "R": {
+ "item": "minecraft:command_block"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:computer_command"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/computer_normal.json b/src/generated/resources/data/computercraft/recipes/computer_normal.json
new file mode 100644
index 000000000..94c923635
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/computer_normal.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#R#",
+ "#G#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:computer_normal"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk.json b/src/generated/resources/data/computercraft/recipes/disk.json
new file mode 100644
index 000000000..32c3a567d
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk.json
@@ -0,0 +1,3 @@
+{
+ "type": "computercraft:disk"
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_1.json b/src/generated/resources/data/computercraft/recipes/disk_1.json
new file mode 100644
index 000000000..bc8902480
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_1.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:black_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:1118481}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_10.json b/src/generated/resources/data/computercraft/recipes/disk_10.json
new file mode 100644
index 000000000..2b880c729
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_10.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:pink_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:15905484}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_11.json b/src/generated/resources/data/computercraft/recipes/disk_11.json
new file mode 100644
index 000000000..0bccf8ac8
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_11.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:lime_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:8375321}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_12.json b/src/generated/resources/data/computercraft/recipes/disk_12.json
new file mode 100644
index 000000000..e41b90738
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_12.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:yellow_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:14605932}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_13.json b/src/generated/resources/data/computercraft/recipes/disk_13.json
new file mode 100644
index 000000000..f48085a58
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_13.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:light_blue_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:10072818}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_14.json b/src/generated/resources/data/computercraft/recipes/disk_14.json
new file mode 100644
index 000000000..b7fd2108a
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_14.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:magenta_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:15040472}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_15.json b/src/generated/resources/data/computercraft/recipes/disk_15.json
new file mode 100644
index 000000000..fd383fe64
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_15.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:orange_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:15905331}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_16.json b/src/generated/resources/data/computercraft/recipes/disk_16.json
new file mode 100644
index 000000000..5b4fca9dd
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_16.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:white_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:15790320}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_2.json b/src/generated/resources/data/computercraft/recipes/disk_2.json
new file mode 100644
index 000000000..958f09cf6
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_2.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:red_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:13388876}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_3.json b/src/generated/resources/data/computercraft/recipes/disk_3.json
new file mode 100644
index 000000000..2299521ac
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_3.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:green_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:5744206}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_4.json b/src/generated/resources/data/computercraft/recipes/disk_4.json
new file mode 100644
index 000000000..af928981e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_4.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:brown_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:8349260}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_5.json b/src/generated/resources/data/computercraft/recipes/disk_5.json
new file mode 100644
index 000000000..e25cf0ffa
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_5.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:blue_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:3368652}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_6.json b/src/generated/resources/data/computercraft/recipes/disk_6.json
new file mode 100644
index 000000000..270735db3
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_6.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:purple_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:11691749}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_7.json b/src/generated/resources/data/computercraft/recipes/disk_7.json
new file mode 100644
index 000000000..fef7cef21
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_7.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:cyan_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:5020082}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_8.json b/src/generated/resources/data/computercraft/recipes/disk_8.json
new file mode 100644
index 000000000..ba3d26d55
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_8.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:light_gray_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:10066329}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_9.json b/src/generated/resources/data/computercraft/recipes/disk_9.json
new file mode 100644
index 000000000..61eab3de4
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_9.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "group": "computercraft:disk",
+ "ingredients": [
+ {
+ "tag": "c:redstone_dusts"
+ },
+ {
+ "item": "minecraft:paper"
+ },
+ {
+ "item": "minecraft:gray_dye"
+ }
+ ],
+ "result": {
+ "item": "computercraft:disk",
+ "nbt": "{Color:5000268}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/disk_drive.json b/src/generated/resources/data/computercraft/recipes/disk_drive.json
new file mode 100644
index 000000000..6a5e0a277
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/disk_drive.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#R#",
+ "#R#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ }
+ },
+ "result": {
+ "item": "computercraft:disk_drive"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/monitor_advanced.json b/src/generated/resources/data/computercraft/recipes/monitor_advanced.json
new file mode 100644
index 000000000..67d086c50
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/monitor_advanced.json
@@ -0,0 +1,20 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#G#",
+ "###"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:gold_ingots"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:monitor_advanced",
+ "count": 4
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/monitor_normal.json b/src/generated/resources/data/computercraft/recipes/monitor_normal.json
new file mode 100644
index 000000000..72e2ec884
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/monitor_normal.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#G#",
+ "###"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:monitor_normal"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/speaker.json b/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/speaker.json
new file mode 100644
index 000000000..567e5aea4
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/speaker.json
@@ -0,0 +1,20 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:pocket_advanced",
+ "pattern": [
+ "#",
+ "P"
+ ],
+ "key": {
+ "P": {
+ "item": "computercraft:pocket_computer_advanced"
+ },
+ "#": {
+ "item": "computercraft:speaker"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_advanced",
+ "nbt": "{Upgrade:\"computercraft:speaker\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..222c93ae6
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,20 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:pocket_advanced",
+ "pattern": [
+ "#",
+ "P"
+ ],
+ "key": {
+ "P": {
+ "item": "computercraft:pocket_computer_advanced"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_advanced"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_advanced",
+ "nbt": "{Upgrade:\"computercraft:wireless_modem_advanced\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..cd9ff2d17
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_normal.json
@@ -0,0 +1,20 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:pocket_advanced",
+ "pattern": [
+ "#",
+ "P"
+ ],
+ "key": {
+ "P": {
+ "item": "computercraft:pocket_computer_advanced"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_normal"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_advanced",
+ "nbt": "{Upgrade:\"computercraft:wireless_modem_normal\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_computer_advanced.json b/src/generated/resources/data/computercraft/recipes/pocket_computer_advanced.json
new file mode 100644
index 000000000..3756b4a7c
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_computer_advanced.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#A#",
+ "#G#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:gold_ingots"
+ },
+ "A": {
+ "item": "minecraft:golden_apple"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_advanced"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_computer_normal.json b/src/generated/resources/data/computercraft/recipes/pocket_computer_normal.json
new file mode 100644
index 000000000..66e2a44ba
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_computer_normal.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#A#",
+ "#G#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "A": {
+ "item": "minecraft:golden_apple"
+ },
+ "G": {
+ "tag": "c:glass_panes"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_normal"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_computer_upgrade.json b/src/generated/resources/data/computercraft/recipes/pocket_computer_upgrade.json
new file mode 100644
index 000000000..22fdf4c40
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_computer_upgrade.json
@@ -0,0 +1,3 @@
+{
+ "type": "computercraft:pocket_computer_upgrade"
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/speaker.json b/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/speaker.json
new file mode 100644
index 000000000..dc388cbd4
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/speaker.json
@@ -0,0 +1,20 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:pocket_normal",
+ "pattern": [
+ "#",
+ "P"
+ ],
+ "key": {
+ "P": {
+ "item": "computercraft:pocket_computer_normal"
+ },
+ "#": {
+ "item": "computercraft:speaker"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_normal",
+ "nbt": "{Upgrade:\"computercraft:speaker\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..c1e7ef083
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,20 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:pocket_normal",
+ "pattern": [
+ "#",
+ "P"
+ ],
+ "key": {
+ "P": {
+ "item": "computercraft:pocket_computer_normal"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_advanced"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_normal",
+ "nbt": "{Upgrade:\"computercraft:wireless_modem_advanced\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..bf12b0174
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_normal.json
@@ -0,0 +1,20 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:pocket_normal",
+ "pattern": [
+ "#",
+ "P"
+ ],
+ "key": {
+ "P": {
+ "item": "computercraft:pocket_computer_normal"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_normal"
+ }
+ },
+ "result": {
+ "item": "computercraft:pocket_computer_normal",
+ "nbt": "{Upgrade:\"computercraft:wireless_modem_normal\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/printed_book.json b/src/generated/resources/data/computercraft/recipes/printed_book.json
new file mode 100644
index 000000000..e86e66703
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/printed_book.json
@@ -0,0 +1,17 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "ingredients": [
+ {
+ "item": "minecraft:leather"
+ },
+ {
+ "item": "computercraft:printed_page"
+ },
+ {
+ "item": "minecraft:string"
+ }
+ ],
+ "result": {
+ "item": "computercraft:printed_book"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/printed_pages.json b/src/generated/resources/data/computercraft/recipes/printed_pages.json
new file mode 100644
index 000000000..10e045136
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/printed_pages.json
@@ -0,0 +1,17 @@
+{
+ "type": "computercraft:impostor_shapeless",
+ "ingredients": [
+ {
+ "item": "computercraft:printed_page"
+ },
+ {
+ "item": "computercraft:printed_page"
+ },
+ {
+ "item": "minecraft:string"
+ }
+ ],
+ "result": {
+ "item": "computercraft:printed_pages"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/printer.json b/src/generated/resources/data/computercraft/recipes/printer.json
new file mode 100644
index 000000000..fe6a01999
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/printer.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#R#",
+ "#D#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ },
+ "D": {
+ "tag": "c:dyes"
+ }
+ },
+ "result": {
+ "item": "computercraft:printer"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/printout.json b/src/generated/resources/data/computercraft/recipes/printout.json
new file mode 100644
index 000000000..0e1a02157
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/printout.json
@@ -0,0 +1,3 @@
+{
+ "type": "computercraft:printout"
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/skull_cloudy.json b/src/generated/resources/data/computercraft/recipes/skull_cloudy.json
new file mode 100644
index 000000000..97c489734
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/skull_cloudy.json
@@ -0,0 +1,15 @@
+{
+ "type": "minecraft:crafting_shapeless",
+ "ingredients": [
+ {
+ "tag": "c:skulls"
+ },
+ {
+ "item": "computercraft:monitor_normal"
+ }
+ ],
+ "result": {
+ "item": "minecraft:player_head",
+ "nbt": "{SkullOwner:{Id:\"6d074736-b1e9-4378-a99b-bd8777821c9c\",Name:\"Cloudhunter\"}}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/skull_dan200.json b/src/generated/resources/data/computercraft/recipes/skull_dan200.json
new file mode 100644
index 000000000..8519822bf
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/skull_dan200.json
@@ -0,0 +1,15 @@
+{
+ "type": "minecraft:crafting_shapeless",
+ "ingredients": [
+ {
+ "tag": "c:skulls"
+ },
+ {
+ "item": "computercraft:computer_normal"
+ }
+ ],
+ "result": {
+ "item": "minecraft:player_head",
+ "nbt": "{SkullOwner:{Id:\"f3c8d69b-0776-4512-8434-d1b2165909eb\",Name:\"dan200\"}}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/speaker.json b/src/generated/resources/data/computercraft/recipes/speaker.json
new file mode 100644
index 000000000..12259140d
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/speaker.json
@@ -0,0 +1,22 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#N#",
+ "#R#"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "N": {
+ "item": "minecraft:note_block"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ }
+ },
+ "result": {
+ "item": "computercraft:speaker"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/speaker.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/speaker.json
new file mode 100644
index 000000000..2cf9964a2
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/speaker.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "computercraft:speaker"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"computercraft:speaker\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..c6cc910fe
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_advanced"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"computercraft:wireless_modem_advanced\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..7451f115f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_normal.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_normal"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"computercraft:wireless_modem_normal\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/crafting_table.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/crafting_table.json
new file mode 100644
index 000000000..15d1993bf
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/crafting_table.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:crafting_table"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:crafting_table\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_axe.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_axe.json
new file mode 100644
index 000000000..774438beb
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_axe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:diamond_axe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_axe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_hoe.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_hoe.json
new file mode 100644
index 000000000..cf00c7587
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_hoe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:diamond_hoe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_hoe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_pickaxe.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_pickaxe.json
new file mode 100644
index 000000000..58560f66c
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_pickaxe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:diamond_pickaxe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_pickaxe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_shovel.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_shovel.json
new file mode 100644
index 000000000..614e6178f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_shovel.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:diamond_shovel"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_shovel\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_sword.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_sword.json
new file mode 100644
index 000000000..846b6bc8e
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_sword.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:diamond_sword"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_sword\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/netherite_pickaxe.json b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/netherite_pickaxe.json
new file mode 100644
index 000000000..fcfe66888
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_advanced/minecraft/netherite_pickaxe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_advanced",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_advanced"
+ },
+ "#": {
+ "item": "minecraft:netherite_pickaxe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_advanced",
+ "nbt": "{RightUpgrade:\"minecraft:netherite_pickaxe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/speaker.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/speaker.json
new file mode 100644
index 000000000..39e3fa7e8
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/speaker.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "computercraft:speaker"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"computercraft:speaker\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_advanced.json
new file mode 100644
index 000000000..7b6da28e4
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_advanced.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_advanced"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"computercraft:wireless_modem_advanced\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_normal.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_normal.json
new file mode 100644
index 000000000..66d096584
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_normal.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "computercraft:wireless_modem_normal"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"computercraft:wireless_modem_normal\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/crafting_table.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/crafting_table.json
new file mode 100644
index 000000000..31647e842
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/crafting_table.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:crafting_table"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:crafting_table\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_axe.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_axe.json
new file mode 100644
index 000000000..a79534db6
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_axe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:diamond_axe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_axe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_hoe.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_hoe.json
new file mode 100644
index 000000000..0c0fc1c39
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_hoe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:diamond_hoe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_hoe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_pickaxe.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_pickaxe.json
new file mode 100644
index 000000000..0490a1267
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_pickaxe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:diamond_pickaxe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_pickaxe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_shovel.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_shovel.json
new file mode 100644
index 000000000..7bbe87284
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_shovel.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:diamond_shovel"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_shovel\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_sword.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_sword.json
new file mode 100644
index 000000000..ba893dc67
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_sword.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:diamond_sword"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:diamond_sword\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/netherite_pickaxe.json b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/netherite_pickaxe.json
new file mode 100644
index 000000000..750320508
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_normal/minecraft/netherite_pickaxe.json
@@ -0,0 +1,19 @@
+{
+ "type": "computercraft:impostor_shaped",
+ "group": "computercraft:turtle_normal",
+ "pattern": [
+ "#T"
+ ],
+ "key": {
+ "T": {
+ "item": "computercraft:turtle_normal"
+ },
+ "#": {
+ "item": "minecraft:netherite_pickaxe"
+ }
+ },
+ "result": {
+ "item": "computercraft:turtle_normal",
+ "nbt": "{RightUpgrade:\"minecraft:netherite_pickaxe\"}"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/turtle_upgrade.json b/src/generated/resources/data/computercraft/recipes/turtle_upgrade.json
new file mode 100644
index 000000000..62e103b60
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/turtle_upgrade.json
@@ -0,0 +1,3 @@
+{
+ "type": "computercraft:turtle_upgrade"
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/wired_modem.json b/src/generated/resources/data/computercraft/recipes/wired_modem.json
new file mode 100644
index 000000000..08290b43a
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/wired_modem.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#R#",
+ "###"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "R": {
+ "tag": "c:redstone_dusts"
+ }
+ },
+ "result": {
+ "item": "computercraft:wired_modem"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/wired_modem_full_from.json b/src/generated/resources/data/computercraft/recipes/wired_modem_full_from.json
new file mode 100644
index 000000000..7865f45de
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/wired_modem_full_from.json
@@ -0,0 +1,11 @@
+{
+ "type": "minecraft:crafting_shapeless",
+ "ingredients": [
+ {
+ "item": "computercraft:wired_modem"
+ }
+ ],
+ "result": {
+ "item": "computercraft:wired_modem_full"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/wired_modem_full_to.json b/src/generated/resources/data/computercraft/recipes/wired_modem_full_to.json
new file mode 100644
index 000000000..8c303ebb4
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/wired_modem_full_to.json
@@ -0,0 +1,11 @@
+{
+ "type": "minecraft:crafting_shapeless",
+ "ingredients": [
+ {
+ "item": "computercraft:wired_modem_full"
+ }
+ ],
+ "result": {
+ "item": "computercraft:wired_modem"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/wireless_modem_advanced.json b/src/generated/resources/data/computercraft/recipes/wireless_modem_advanced.json
new file mode 100644
index 000000000..1682913f8
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/wireless_modem_advanced.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#E#",
+ "###"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:gold_ingots"
+ },
+ "E": {
+ "item": "minecraft:ender_eye"
+ }
+ },
+ "result": {
+ "item": "computercraft:wireless_modem_advanced"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/recipes/wireless_modem_normal.json b/src/generated/resources/data/computercraft/recipes/wireless_modem_normal.json
new file mode 100644
index 000000000..4e5539a8f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/recipes/wireless_modem_normal.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:crafting_shaped",
+ "pattern": [
+ "###",
+ "#E#",
+ "###"
+ ],
+ "key": {
+ "#": {
+ "tag": "c:stones"
+ },
+ "E": {
+ "tag": "c:ender_pearls"
+ }
+ },
+ "result": {
+ "item": "computercraft:wireless_modem_normal"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/blocks/computer.json b/src/generated/resources/data/computercraft/tags/blocks/computer.json
new file mode 100644
index 000000000..bcd0e8037
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/blocks/computer.json
@@ -0,0 +1,8 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:computer_normal",
+ "computercraft:computer_advanced",
+ "computercraft:computer_command"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/blocks/monitor.json b/src/generated/resources/data/computercraft/tags/blocks/monitor.json
new file mode 100644
index 000000000..babaefa8b
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/blocks/monitor.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:monitor_normal",
+ "computercraft:monitor_advanced"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/blocks/turtle.json b/src/generated/resources/data/computercraft/tags/blocks/turtle.json
new file mode 100644
index 000000000..e4277edfe
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/blocks/turtle.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:turtle_normal",
+ "computercraft:turtle_advanced"
+ ]
+}
\ No newline at end of file
diff --git a/src/main/resources/data/computercraft/tags/blocks/turtle_always_breakable.json b/src/generated/resources/data/computercraft/tags/blocks/turtle_always_breakable.json
similarity index 98%
rename from src/main/resources/data/computercraft/tags/blocks/turtle_always_breakable.json
rename to src/generated/resources/data/computercraft/tags/blocks/turtle_always_breakable.json
index c4225f023..1d795dcef 100644
--- a/src/main/resources/data/computercraft/tags/blocks/turtle_always_breakable.json
+++ b/src/generated/resources/data/computercraft/tags/blocks/turtle_always_breakable.json
@@ -5,4 +5,4 @@
"minecraft:bamboo",
"minecraft:bamboo_sapling"
]
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/data/computercraft/tags/blocks/turtle_hoe_harvestable.json b/src/generated/resources/data/computercraft/tags/blocks/turtle_hoe_harvestable.json
similarity index 99%
rename from src/main/resources/data/computercraft/tags/blocks/turtle_hoe_harvestable.json
rename to src/generated/resources/data/computercraft/tags/blocks/turtle_hoe_harvestable.json
index 6f687aeb6..10e786424 100644
--- a/src/main/resources/data/computercraft/tags/blocks/turtle_hoe_harvestable.json
+++ b/src/generated/resources/data/computercraft/tags/blocks/turtle_hoe_harvestable.json
@@ -9,4 +9,4 @@
"minecraft:carved_pumpkin",
"minecraft:jack_o_lantern"
]
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/data/computercraft/tags/blocks/turtle_shovel_harvestable.json b/src/generated/resources/data/computercraft/tags/blocks/turtle_shovel_harvestable.json
similarity index 98%
rename from src/main/resources/data/computercraft/tags/blocks/turtle_shovel_harvestable.json
rename to src/generated/resources/data/computercraft/tags/blocks/turtle_shovel_harvestable.json
index 06ba799d8..f6a10bb7f 100644
--- a/src/main/resources/data/computercraft/tags/blocks/turtle_shovel_harvestable.json
+++ b/src/generated/resources/data/computercraft/tags/blocks/turtle_shovel_harvestable.json
@@ -7,4 +7,4 @@
"minecraft:carved_pumpkin",
"minecraft:jack_o_lantern"
]
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/data/computercraft/tags/blocks/turtle_sword_harvestable.json b/src/generated/resources/data/computercraft/tags/blocks/turtle_sword_harvestable.json
similarity index 97%
rename from src/main/resources/data/computercraft/tags/blocks/turtle_sword_harvestable.json
rename to src/generated/resources/data/computercraft/tags/blocks/turtle_sword_harvestable.json
index 931423b91..c38f1efd8 100644
--- a/src/main/resources/data/computercraft/tags/blocks/turtle_sword_harvestable.json
+++ b/src/generated/resources/data/computercraft/tags/blocks/turtle_sword_harvestable.json
@@ -4,4 +4,4 @@
"#minecraft:wool",
"minecraft:cobweb"
]
-}
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/blocks/wired_modem.json b/src/generated/resources/data/computercraft/tags/blocks/wired_modem.json
new file mode 100644
index 000000000..10ade37b3
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/blocks/wired_modem.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:cable",
+ "computercraft:wired_modem_full"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/items/computer.json b/src/generated/resources/data/computercraft/tags/items/computer.json
new file mode 100644
index 000000000..bcd0e8037
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/items/computer.json
@@ -0,0 +1,8 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:computer_normal",
+ "computercraft:computer_advanced",
+ "computercraft:computer_command"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/items/monitor.json b/src/generated/resources/data/computercraft/tags/items/monitor.json
new file mode 100644
index 000000000..babaefa8b
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/items/monitor.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:monitor_normal",
+ "computercraft:monitor_advanced"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/items/turtle.json b/src/generated/resources/data/computercraft/tags/items/turtle.json
new file mode 100644
index 000000000..e4277edfe
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/items/turtle.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:turtle_normal",
+ "computercraft:turtle_advanced"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/computercraft/tags/items/wired_modem.json b/src/generated/resources/data/computercraft/tags/items/wired_modem.json
new file mode 100644
index 000000000..57db1557f
--- /dev/null
+++ b/src/generated/resources/data/computercraft/tags/items/wired_modem.json
@@ -0,0 +1,7 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:wired_modem",
+ "computercraft:wired_modem_full"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
new file mode 100644
index 000000000..bb1e60dbc
--- /dev/null
+++ b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
@@ -0,0 +1,18 @@
+{
+ "replace": false,
+ "values": [
+ "computercraft:computer_normal",
+ "computercraft:computer_advanced",
+ "computercraft:turtle_normal",
+ "computercraft:turtle_advanced",
+ "computercraft:speaker",
+ "computercraft:disk_drive",
+ "computercraft:printer",
+ "computercraft:monitor_normal",
+ "computercraft:monitor_advanced",
+ "computercraft:wireless_modem_normal",
+ "computercraft:wireless_modem_advanced",
+ "computercraft:wired_modem_full",
+ "computercraft:cable"
+ ]
+}
\ No newline at end of file
diff --git a/src/main/resources/data/minecraft/tags/items/piglin_loved.json b/src/generated/resources/data/minecraft/tags/items/piglin_loved.json
similarity index 99%
rename from src/main/resources/data/minecraft/tags/items/piglin_loved.json
rename to src/generated/resources/data/minecraft/tags/items/piglin_loved.json
index 534e2a988..8eedcc427 100644
--- a/src/main/resources/data/minecraft/tags/items/piglin_loved.json
+++ b/src/generated/resources/data/minecraft/tags/items/piglin_loved.json
@@ -7,4 +7,4 @@
"computercraft:pocket_computer_advanced",
"computercraft:monitor_advanced"
]
-}
+}
\ No newline at end of file
diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java
index 93e8afacd..e977120c5 100644
--- a/src/main/java/dan200/computercraft/ComputerCraft.java
+++ b/src/main/java/dan200/computercraft/ComputerCraft.java
@@ -7,7 +7,6 @@
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRule;
-import dan200.computercraft.shared.Registry.ModBlocks;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
@@ -24,15 +23,11 @@
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
import dan200.computercraft.shared.util.ImpostorRecipe;
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
-import net.fabricmc.api.ModInitializer;
-import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.item.CreativeModeTab;
-import net.minecraft.world.item.ItemStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -41,7 +36,7 @@
import static dan200.computercraft.shared.Registry.init;
-public final class ComputerCraft implements ModInitializer
+public final class ComputerCraft
{
public static final String MOD_ID = "computercraft";
@@ -104,10 +99,7 @@ public final class ComputerCraft implements ModInitializer
// Logging
public static final Logger log = LogManager.getLogger( MOD_ID );
- public static CreativeModeTab MAIN_GROUP = FabricItemGroupBuilder.build( new ResourceLocation( MOD_ID, "main" ), () -> new ItemStack( ModBlocks.COMPUTER_NORMAL ) );
-
- @Override
- public void onInitialize()
+ public static void onInitialize()
{
ComputerCraftProxyCommon.init();
Registry.register( Registry.RECIPE_SERIALIZER, new ResourceLocation( ComputerCraft.MOD_ID, "colour" ), ColourableRecipe.SERIALIZER );
diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
index 11791d4cf..3c4e0e75b 100644
--- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
+++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
@@ -283,6 +283,7 @@ public interface IComputerCraftAPI
int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side );
void registerMediaProvider( @Nonnull IMediaProvider provider );
+
@Nonnull
IPacketNetwork getWirelessNetwork();
diff --git a/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java b/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java
index 460ace58f..c9dcfeda9 100644
--- a/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java
+++ b/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java
@@ -6,8 +6,8 @@
package dan200.computercraft.api.pocket;
import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.world.level.Level;
import javax.annotation.Nonnull;
diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java
index 260a2aff7..51c3f2dad 100644
--- a/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java
+++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java
@@ -6,9 +6,9 @@
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.core.Direction;
diff --git a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java
deleted file mode 100644
index 5e82c7091..000000000
--- a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * This file is part of ComputerCraft - http://www.computercraft.info
- * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
- * Send enquiries to dratcliffe@gmail.com
- */
-package dan200.computercraft.client.gui;
-
-import com.mojang.blaze3d.vertex.PoseStack;
-import com.mojang.blaze3d.vertex.Tesselator;
-import com.mojang.blaze3d.vertex.VertexConsumer;
-import com.mojang.math.Matrix3f;
-import com.mojang.math.Matrix4f;
-import dan200.computercraft.client.FrameInfo;
-import dan200.computercraft.client.render.RenderTypes;
-import dan200.computercraft.core.terminal.Terminal;
-import dan200.computercraft.core.terminal.TextBuffer;
-import dan200.computercraft.shared.util.Colour;
-import dan200.computercraft.shared.util.Palette;
-import net.minecraft.client.renderer.MultiBufferSource;
-import net.minecraft.client.renderer.texture.OverlayTexture;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
-
-/**
- * Handles rendering fixed width text and computer terminals.
- *
- * This class has several modes of usage:
- *
- * {@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods,
- * this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.
- * {@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor
- * separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically.
- *
- * {@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the
- * whole term.
- *
- */
-public final class FixedWidthFontRenderer
-{
- public static final int FONT_HEIGHT = 9;
- public static final int FONT_WIDTH = 6;
- public static final float WIDTH = 256.0f;
-
- public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
- public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
-
- public static final float Z_EPSILON = 0.001f;
-
- private FixedWidthFontRenderer()
- {
- }
-
- public static float toGreyscale( double[] rgb )
- {
- return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
- }
-
- public static int getColour( char c, Colour def )
- {
- return 15 - Terminal.getColour( c, def );
- }
-
- private static void drawChar( PoseStack transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b, int light )
- {
- // Short circuit to avoid the common case - the texture should be blank here after all.
- if( index == '\0' || index == ' ' ) return;
-
- int column = index % 16;
- int row = index / 16;
-
- int xStart = 1 + column * (FONT_WIDTH + 2);
- int yStart = 1 + row * (FONT_HEIGHT + 2);
-
- Matrix4f matrix = transform.last().pose();
- Matrix3f normalMatrix = transform.last().normal();
- vertex( matrix, normalMatrix, buffer, x, y, Z_EPSILON, r, g, b, xStart / WIDTH, yStart / WIDTH, light );
- vertex( matrix, normalMatrix, buffer, x, y + FONT_HEIGHT, Z_EPSILON, r, g, b, xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light );
- vertex( matrix, normalMatrix, buffer, x + FONT_WIDTH, y + FONT_HEIGHT, Z_EPSILON, r, g, b, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light );
- vertex( matrix, normalMatrix, buffer, x + FONT_WIDTH, y, Z_EPSILON, r, g, b, (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH, light );
- }
-
- private static void drawQuad( PoseStack transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b, int light )
- {
- Matrix4f matrix = transform.last().pose();
- Matrix3f normalMatrix = transform.last().normal();
- vertex( matrix, normalMatrix, buffer, x, y, 0, r, g, b, BACKGROUND_START, BACKGROUND_START, light );
- vertex( matrix, normalMatrix, buffer, x, y + height, 0, r, g, b, BACKGROUND_START, BACKGROUND_END, light );
- vertex( matrix, normalMatrix, buffer, x + width, y + height, 0, r, g, b, BACKGROUND_END, BACKGROUND_END, light );
- vertex( matrix, normalMatrix, buffer, x + width, y, 0, r, g, b, BACKGROUND_END, BACKGROUND_START, light );
- }
-
- private static void drawQuad( PoseStack transform, VertexConsumer buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light )
- {
- double[] colour = palette.getColour( getColour( colourIndex, Colour.BLACK ) );
- float r, g, b;
- if( greyscale )
- {
- r = g = b = toGreyscale( colour );
- }
- else
- {
- r = (float) colour[0];
- g = (float) colour[1];
- b = (float) colour[2];
- }
-
- drawQuad( transform, buffer, x, y, width, height, r, g, b, light );
- }
-
- private static void drawBackground(
- @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, float x, float y,
- @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
- float leftMarginSize, float rightMarginSize, float height, int light
- )
- {
- if( leftMarginSize > 0 )
- {
- drawQuad( transform, buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light );
- }
-
- if( rightMarginSize > 0 )
- {
- drawQuad( transform, buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light );
- }
-
- // Batch together runs of identical background cells.
- int blockStart = 0;
- char blockColour = '\0';
- for( int i = 0; i < backgroundColour.length(); i++ )
- {
- char colourIndex = backgroundColour.charAt( i );
- if( colourIndex == blockColour ) continue;
-
- if( blockColour != '\0' )
- {
- drawQuad( transform, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light );
- }
-
- blockColour = colourIndex;
- blockStart = i;
- }
-
- if( blockColour != '\0' )
- {
- drawQuad( transform, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light );
- }
- }
-
- public static void drawString(
- @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, float x, float y,
- @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
- @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize, int light
- )
- {
- if( backgroundColour != null )
- {
- drawBackground( transform, buffer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT, light );
- }
-
- for( int i = 0; i < text.length(); i++ )
- {
- double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.BLACK ) );
- float r, g, b;
- if( greyscale )
- {
- r = g = b = toGreyscale( colour );
- }
- else
- {
- r = (float) colour[0];
- g = (float) colour[1];
- b = (float) colour[2];
- }
-
- // Draw char
- int index = text.charAt( i );
- if( index > 255 ) index = '?';
- drawChar( transform, buffer, x + i * FONT_WIDTH, y, index, r, g, b, light );
- }
-
- }
-
- public static void drawTerminalWithoutCursor(
- @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, float x, float y,
- @Nonnull Terminal terminal, boolean greyscale,
- float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize, int light
- )
- {
- Palette palette = terminal.getPalette();
- int height = terminal.getHeight();
-
- // Top and bottom margins
- drawBackground(
- transform, buffer, x, y - topMarginSize,
- terminal.getBackgroundColourLine( 0 ), palette, greyscale,
- leftMarginSize, rightMarginSize, topMarginSize, light
- );
-
- drawBackground(
- transform, buffer, x, y + height * FONT_HEIGHT,
- terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
- leftMarginSize, rightMarginSize, bottomMarginSize, light
- );
-
- // The main text
- for( int i = 0; i < height; i++ )
- {
- drawString(
- transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
- terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ),
- palette, greyscale, leftMarginSize, rightMarginSize, FULL_BRIGHT_LIGHTMAP
- );
- }
- }
-
- public static void drawCursor(
- @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, float x, float y,
- @Nonnull Terminal terminal, boolean greyscale
- )
- {
- Palette palette = terminal.getPalette();
- int width = terminal.getWidth();
- int height = terminal.getHeight();
-
- int cursorX = terminal.getCursorX();
- int cursorY = terminal.getCursorY();
- if( terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink() )
- {
- double[] colour = palette.getColour( 15 - terminal.getTextColour() );
- float r, g, b;
- if( greyscale )
- {
- r = g = b = toGreyscale( colour );
- }
- else
- {
- r = (float) colour[0];
- g = (float) colour[1];
- b = (float) colour[2];
- }
-
- drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b, FULL_BRIGHT_LIGHTMAP );
- }
- }
-
- public static void drawTerminal(
- @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, float x, float y,
- @Nonnull Terminal terminal, boolean greyscale,
- float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize, int light
- )
- {
- drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize, light );
- drawCursor( transform, buffer, x, y, terminal, greyscale );
- }
-
- // Called by WidgetTerminal
- public static void drawTerminalImmediate(
- @Nonnull PoseStack transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
- float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
- )
- {
- MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
- VertexConsumer buffer = renderer.getBuffer( RenderTypes.GUI_TERMINAL );
- drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize, FULL_BRIGHT_LIGHTMAP );
- renderer.endBatch();
- }
-
- public static void drawEmptyTerminal( @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, float x, float y, float width, float height, int light )
- {
- Colour colour = Colour.BLACK;
- drawQuad( transform, buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB(), light );
- }
-
- public static void drawEmptyTerminalImmediate( @Nonnull PoseStack transform, float x, float y, float width, float height )
- {
- MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
- VertexConsumer buffer = renderer.getBuffer( RenderTypes.GUI_TERMINAL );
- drawEmptyTerminal( transform, buffer, x, y, width, height, FULL_BRIGHT_LIGHTMAP );
- renderer.endBatch();
- }
-
- private static void vertex( Matrix4f poseMatrix, Matrix3f normalMatrix, VertexConsumer buffer, float x, float y, float z, float r, float g, float b, float u, float v, int light )
- {
- buffer.vertex( poseMatrix, x, y, z ).color( r, g, b, 1.0f ).uv( u, v ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( light ).normal( normalMatrix, 0f, 0f, 1f ).endVertex();
- }
-
-}
diff --git a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java
index 5eb8cbc8f..14c24a9e1 100644
--- a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java
+++ b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java
@@ -10,7 +10,6 @@
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.render.ComputerBorderRenderer;
-import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import net.minecraft.network.chat.Component;
@@ -19,6 +18,7 @@
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
+import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
public final class GuiComputer extends ComputerScreenBase
{
@@ -78,7 +78,7 @@ public void renderBg( @Nonnull PoseStack stack, float partialTicks, int mouseX,
// Draw a border around the terminal
ComputerBorderRenderer.renderFromGui(
ComputerBorderRenderer.getTexture( family ), terminal.x, terminal.y, getBlitOffset(),
- RenderTypes.FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
+ FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
);
ComputerSidebar.renderBackground( stack, leftPos, topPos + sidebarYOffset );
}
diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java
index bae7fd8fd..4a5895ec8 100644
--- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java
+++ b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java
@@ -6,22 +6,25 @@
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.vertex.PoseStack;
-import dan200.computercraft.client.gui.FixedWidthFontRenderer;
+import com.mojang.blaze3d.vertex.Tesselator;
+import dan200.computercraft.client.render.RenderTypes;
+import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
+import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.TextComponent;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.util.BitSet;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
public class WidgetTerminal extends AbstractWidget
{
@@ -313,14 +316,24 @@ public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float
{
if( !visible ) return;
Terminal terminal = computer.getTerminal();
+
+ var bufferSource = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
+ var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.GUI_TERMINAL ) );
+
if( terminal != null )
{
- FixedWidthFontRenderer.drawTerminalImmediate( transform, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
+ boolean greyscale = !computer.isColour();
+ FixedWidthFontRenderer.drawTerminal(
+ emitter,
+ (float) innerX, (float) innerY, terminal, greyscale, (float) MARGIN, (float) MARGIN, (float) MARGIN, (float) MARGIN
+ );
}
else
{
- FixedWidthFontRenderer.drawEmptyTerminalImmediate( transform, x, y, width, height );
+ FixedWidthFontRenderer.drawEmptyTerminal( emitter, (float) x, (float) y, (float) width, (float) height );
}
+
+ bufferSource.endBatch();
}
@Override
diff --git a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java
index c55dbcb8b..11c702413 100644
--- a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java
+++ b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java
@@ -12,7 +12,6 @@
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
-import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.fabric.events.CustomClientEvents;
import dan200.computercraft.shared.Registry;
@@ -36,14 +35,13 @@
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry;
-import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
-import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry;
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
-import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry;
+import net.minecraft.client.gui.screens.MenuScreens;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
+import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.inventory.InventoryMenu;
@@ -106,8 +104,6 @@ public void onInitializeClient()
TurtleModelLoader.INSTANCE.loadModel(
name ) : null );
- EntityRendererRegistry.register( Registry.ModEntities.TURTLE_PLAYER, TurtlePlayerRenderer::new );
-
registerItemProperty( "state",
( stack, world, player, integer ) -> ItemPocketComputer.getState( stack )
.ordinal(),
@@ -125,18 +121,16 @@ public void onInitializeClient()
// My IDE doesn't think so, but we do actually need these generics.
private static void registerContainers()
{
- ScreenRegistry.>register( Registry.ModContainers.COMPUTER, GuiComputer::create );
- ScreenRegistry.>register( Registry.ModContainers.POCKET_COMPUTER,
- GuiComputer::createPocket );
- ScreenRegistry.>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM,
- NoTermComputerScreen::new );
- ScreenRegistry.register( Registry.ModContainers.TURTLE, GuiTurtle::new );
-
- ScreenRegistry.register( Registry.ModContainers.PRINTER, GuiPrinter::new );
- ScreenRegistry.register( Registry.ModContainers.DISK_DRIVE, GuiDiskDrive::new );
- ScreenRegistry.register( Registry.ModContainers.PRINTOUT, GuiPrintout::new );
-
- ScreenRegistry.>register( Registry.ModContainers.VIEW_COMPUTER,
+ MenuScreens.>register( Registry.ModContainers.COMPUTER, GuiComputer::create );
+ MenuScreens.>register( Registry.ModContainers.POCKET_COMPUTER, GuiComputer::createPocket );
+ MenuScreens.>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM, NoTermComputerScreen::new );
+ MenuScreens.register( Registry.ModContainers.TURTLE, GuiTurtle::new );
+
+ MenuScreens.register( Registry.ModContainers.PRINTER, GuiPrinter::new );
+ MenuScreens.register( Registry.ModContainers.DISK_DRIVE, GuiDiskDrive::new );
+ MenuScreens.register( Registry.ModContainers.PRINTOUT, GuiPrintout::new );
+
+ MenuScreens.>register( Registry.ModContainers.VIEW_COMPUTER,
GuiComputer::createView );
}
@@ -162,7 +156,7 @@ public float unclampedCall( ItemStack itemStack, @Nullable ClientLevel clientLev
};
for( Supplier extends Item> item : items )
{
- FabricModelPredicateProviderRegistry.register( item.get(), id, unclampedGetter );
+ ItemProperties.register( item.get(), id, unclampedGetter );
}
}
}
diff --git a/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java b/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java
index a17f50bc0..a807934cc 100644
--- a/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java
+++ b/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java
@@ -72,7 +72,6 @@ public ComputerBorderRenderer( PoseStack transform, VertexConsumer builder, int
this.b = b;
}
-
@Nonnull
public static ResourceLocation getTexture( @Nonnull ComputerFamily family )
{
diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java
index 9ab59b94b..02847e27f 100644
--- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java
+++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java
@@ -5,25 +5,24 @@
*/
package dan200.computercraft.client.render;
-import com.mojang.blaze3d.vertex.*;
-import com.mojang.math.Matrix3f;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
-import dan200.computercraft.client.gui.FixedWidthFontRenderer;
+import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.renderer.MultiBufferSource;
-import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
-import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
/**
* Emulates map rendering for pocket computers.
@@ -37,7 +36,7 @@ private ItemPocketRenderer()
}
@Override
- protected void renderItem( PoseStack transform, MultiBufferSource renderer, ItemStack stack, int light )
+ protected void renderItem( PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal();
@@ -75,32 +74,32 @@ protected void renderItem( PoseStack transform, MultiBufferSource renderer, Item
ComputerFamily family = item.getFamily();
int frameColour = item.getColour( stack );
- renderFrame( transform, renderer, family, frameColour, light, width, height );
+ renderFrame( transform, bufferSource, family, frameColour, light, width, height );
// Render the light
int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.BLACK.getHex();
- renderLight( transform, renderer, lightColour, width, height );
- VertexConsumer buffer = renderer.getBuffer( RenderTypes.ITEM_POCKET_TERMINAL );
+ renderLight( transform, bufferSource, lightColour, width, height );
if( computer != null && terminal != null )
{
FixedWidthFontRenderer.drawTerminal(
- transform, buffer,
- MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN, FULL_BRIGHT_LIGHTMAP
+ FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.ITEM_POCKET_TERMINAL ) ),
+ MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN
);
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal(
- transform, buffer,
- 0, 0, width, height, 0 );
+ FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.ITEM_POCKET_TERMINAL ) ),
+ 0, 0, width, height
+ );
}
transform.popPose();
}
- private static void renderFrame( PoseStack transform, MultiBufferSource renderer, ComputerFamily family, int colour, int light, int width, int height )
+ private static void renderFrame( PoseStack transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height )
{
ResourceLocation texture = colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family );
@@ -108,23 +107,22 @@ private static void renderFrame( PoseStack transform, MultiBufferSource renderer
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
- VertexConsumer buffer = renderer.getBuffer( RenderTypes.itemPocketBorder( texture ) );
+ VertexConsumer buffer = render.getBuffer( RenderTypes.itemPocketBorder( texture ) );
ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, light, width, height, true, r, g, b );
}
- private static void renderLight( PoseStack transform, MultiBufferSource renderer, int colour, int width, int height )
+ private static void renderLight( PoseStack transform, MultiBufferSource render, int colour, int width, int height )
{
- float r = ((colour >>> 16) & 0xFF) / 255.0f;
- float g = ((colour >>> 8) & 0xFF) / 255.0f;
- float b = (colour & 0xFF) / 255.0f;
- float z = 0.001f;
-
- VertexConsumer buffer = renderer.getBuffer( RenderTypes.ITEM_POCKET_LIGHT );
- Matrix4f poseMatrix = transform.last().pose();
- Matrix3f normalMatrix = transform.last().normal();
- buffer.vertex( poseMatrix, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_START ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( FULL_BRIGHT_LIGHTMAP ).normal( normalMatrix, 0f, 0f, 1f ).endVertex();
- buffer.vertex( poseMatrix, width, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( FULL_BRIGHT_LIGHTMAP ).normal( normalMatrix, 0f, 0f, 1f ).endVertex();
- buffer.vertex( poseMatrix, width, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_END ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( FULL_BRIGHT_LIGHTMAP ).normal( normalMatrix, 0f, 0f, 1f ).endVertex();
- buffer.vertex( poseMatrix, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( FULL_BRIGHT_LIGHTMAP ).normal( normalMatrix, 0f, 0f, 1f ).endVertex();
+ byte r = (byte) ((colour >>> 16) & 0xFF);
+ byte g = (byte) ((colour >>> 8) & 0xFF);
+ byte b = (byte) (colour & 0xFF);
+ byte[] c = new byte[] { r, g, b, (byte) 255 };
+
+ VertexConsumer buffer = render.getBuffer( RenderTypes.ITEM_POCKET_LIGHT );
+ FixedWidthFontRenderer.drawQuad(
+ FixedWidthFontRenderer.toVertexConsumer( transform, buffer ),
+ width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
+ c, RenderTypes.FULL_BRIGHT_LIGHTMAP
+ );
}
}
diff --git a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java
index 422370065..da43dd393 100644
--- a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java
+++ b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java
@@ -12,9 +12,9 @@
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemStack;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
diff --git a/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java b/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java
index 8552ad921..e54908690 100644
--- a/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java
+++ b/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java
@@ -7,105 +7,125 @@
import com.mojang.blaze3d.shaders.Uniform;
import com.mojang.blaze3d.vertex.VertexFormat;
-import dan200.computercraft.client.gui.FixedWidthFontRenderer;
-import dan200.computercraft.shared.util.Palette;
+import dan200.computercraft.client.FrameInfo;
+import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
+import dan200.computercraft.core.terminal.Terminal;
+import dan200.computercraft.core.terminal.TextBuffer;
+import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.server.packs.resources.ResourceProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL13;
+import org.lwjgl.opengl.GL31;
import javax.annotation.Nullable;
import java.io.IOException;
-import java.nio.FloatBuffer;
+import java.nio.ByteBuffer;
+
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour;
public class MonitorTextureBufferShader extends ShaderInstance
{
+ public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
+
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
private static final Logger LOGGER = LogManager.getLogger();
- private final Uniform palette;
- private final Uniform width;
- private final Uniform height;
+ private final int monitorData;
+ private int uniformBuffer = 0;
+
+ private final Uniform cursorBlink;
public MonitorTextureBufferShader( ResourceProvider provider, String name, VertexFormat format ) throws IOException
{
super( provider, name, format );
+ monitorData = GL31.glGetUniformBlockIndex( getId(), "MonitorData" );
+ if( monitorData == -1 ) throw new IllegalStateException( "Could not find MonitorData uniform." );
- width = getUniformChecked( "Width" );
- height = getUniformChecked( "Height" );
- palette = new Uniform( "Palette", Uniform.UT_FLOAT3, 16 * 3, this );
- updateUniformLocation( palette );
+ cursorBlink = getUniformChecked( "CursorBlink" );
Uniform tbo = getUniformChecked( "Tbo" );
if( tbo != null ) tbo.set( TEXTURE_INDEX - GL13.GL_TEXTURE0 );
}
- void setupUniform( int width, int height, Palette palette, boolean greyscale )
- {
- if( this.width != null ) this.width.set( width );
- if( this.height != null ) this.height.set( height );
- setupPalette( palette, greyscale );
- }
-
- private void setupPalette( Palette palette, boolean greyscale )
+ public void setupUniform( int buffer )
{
- if( this.palette == null ) return;
+ uniformBuffer = buffer;
- FloatBuffer paletteBuffer = this.palette.getFloatBuffer();
- paletteBuffer.rewind();
- for( int i = 0; i < 16; i++ )
- {
- double[] colour = palette.getColour( i );
- if( greyscale )
- {
- float f = FixedWidthFontRenderer.toGreyscale( colour );
- paletteBuffer.put( f ).put( f ).put( f );
- }
- else
- {
- paletteBuffer.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] );
- }
- }
+ int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0;
+ if( cursorBlink != null && cursorBlink.getIntBuffer().get( 0 ) != cursorAlpha ) cursorBlink.set( cursorAlpha );
}
@Override
public void apply()
{
super.apply();
- palette.upload();
+ GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, monitorData, uniformBuffer );
}
- @Override
- public void close()
+ @Nullable
+ private Uniform getUniformChecked( String name )
{
- palette.close();
- super.close();
+ Uniform uniform = getUniform( name );
+ if( uniform == null )
+ {
+ LOGGER.warn( "Monitor shader {} should have uniform {}, but it was not present.", getName(), name );
+ }
+
+ return uniform;
}
- private void updateUniformLocation( Uniform uniform )
+ public static void setTerminalData( ByteBuffer buffer, Terminal terminal )
{
- int id = Uniform.glGetUniformLocation( getId(), uniform.getName() );
- if( id == -1 )
- {
- LOGGER.warn( "Shader {} could not find uniform named {} in the specified shader program.", getName(), uniform.getName() );
- }
- else
+ int width = terminal.getWidth(), height = terminal.getHeight();
+
+ int pos = 0;
+ for( int y = 0; y < height; y++ )
{
- uniform.setLocation( id );
+ TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y );
+ for( int x = 0; x < width; x++ )
+ {
+ buffer.put( pos, (byte) (text.charAt( x ) & 0xFF) );
+ buffer.put( pos + 1, (byte) getColour( textColour.charAt( x ), Colour.WHITE ) );
+ buffer.put( pos + 2, (byte) getColour( background.charAt( x ), Colour.BLACK ) );
+ pos += 3;
+ }
}
+
+ buffer.limit( pos );
}
- @Nullable
- private Uniform getUniformChecked( String name )
+ public static void setUniformData( ByteBuffer buffer, Terminal terminal, boolean greyscale )
{
- Uniform uniform = getUniform( name );
- if( uniform == null )
+ int pos = 0;
+ var palette = terminal.getPalette();
+ for( int i = 0; i < 16; i++ )
{
- LOGGER.warn( "Monitor shader {} should have uniform {}, but it was not present.", getName(), name );
+ {
+ double[] colour = palette.getColour( i );
+ if( greyscale )
+ {
+ float f = FixedWidthFontRenderer.toGreyscale( colour );
+ buffer.putFloat( pos, f ).putFloat( pos + 4, f ).putFloat( pos + 8, f );
+ }
+ else
+ {
+ buffer.putFloat( pos, (float) colour[0] ).putFloat( pos + 4, (float) colour[1] ).putFloat( pos + 8, (float) colour[2] );
+ }
+ }
+
+ pos += 4 * 4; // std140 requires these are 4-wide
}
- return uniform;
+ boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal );
+ buffer
+ .putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() )
+ .putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 )
+ .putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 )
+ .putInt( pos + 16, 15 - terminal.getTextColour() );
+
+ buffer.limit( UNIFORM_SIZE );
}
}
diff --git a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java
index af35f2253..7561b0a50 100644
--- a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java
+++ b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java
@@ -9,12 +9,12 @@
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
-import dan200.computercraft.client.gui.FixedWidthFontRenderer;
+import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.texture.OverlayTexture;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
public final class PrintoutRenderer
@@ -58,25 +58,25 @@ private PrintoutRenderer() {}
public static void drawText( PoseStack transform, VertexConsumer buffer, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours )
{
+ var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
- FixedWidthFontRenderer.drawString( transform, buffer,
- x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT,
- false, 0, 0,
- light
+ FixedWidthFontRenderer.drawString( emitter,
+ x, y + line * FONT_HEIGHT, text[start + line], colours[start + line],
+ Palette.DEFAULT, false, light
);
}
}
public static void drawText( PoseStack transform, VertexConsumer buffer, int x, int y, int start, int light, String[] text, String[] colours )
{
+ var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
- FixedWidthFontRenderer.drawString( transform, buffer,
+ FixedWidthFontRenderer.drawString( emitter,
x, y + line * FONT_HEIGHT,
new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ),
- null, Palette.DEFAULT, false, 0, 0,
- light
+ Palette.DEFAULT, false, light
);
}
}
diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java
index 5cc3c97a5..c0dfc4986 100644
--- a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java
+++ b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java
@@ -8,21 +8,23 @@
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.*;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.FrameInfo;
-import dan200.computercraft.client.gui.FixedWidthFontRenderer;
+import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
+import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
+import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.core.terminal.Terminal;
-import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
-import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.BlockPos;
@@ -34,8 +36,8 @@
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
-import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
-import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
public class TileEntityMonitorRenderer implements BlockEntityRenderer
{
@@ -43,8 +45,8 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer
-{
- public TurtlePlayerRenderer( EntityRendererProvider.Context renderManager )
- {
- super( renderManager );
- }
-
- @Nonnull
- @Override
- public ResourceLocation getTextureLocation( @Nonnull TurtlePlayer entity )
- {
- return ComputerBorderRenderer.BACKGROUND_NORMAL;
- }
-
- @Override
- public void render( @Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource buffer, int packedLightIn )
- {
- }
-}
diff --git a/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java
new file mode 100644
index 000000000..59c4e6737
--- /dev/null
+++ b/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java
@@ -0,0 +1,238 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.client.render.text;
+
+import com.mojang.blaze3d.platform.MemoryTracker;
+import com.mojang.blaze3d.vertex.DefaultVertexFormat;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import dan200.computercraft.core.terminal.Terminal;
+import dan200.computercraft.core.terminal.TextBuffer;
+import dan200.computercraft.shared.util.Colour;
+import dan200.computercraft.shared.util.Palette;
+import org.lwjgl.system.MemoryUtil;
+
+import javax.annotation.Nonnull;
+import java.nio.ByteBuffer;
+
+import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
+import static org.lwjgl.system.MemoryUtil.*;
+
+/**
+ * An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link ByteBuffer} rather than
+ * emitting to {@link VertexConsumer}. This allows us to emit vertices very quickly, when using the VBO renderer.
+ *
+ * There are some limitations here:
+ *
+ * No transformation matrix (not needed for VBOs).
+ * Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX_LIGHTMAP}.
+ * The buffer MUST be allocated with {@link MemoryTracker}, and not through any other means.
+ *
+ *
+ * Note this is almost an exact copy of {@link FixedWidthFontRenderer}. While the code duplication is unfortunate,
+ * it is measurably faster than introducing polymorphism into {@link FixedWidthFontRenderer}.
+ *
+ * IMPORTANT: When making changes to this class, please check if you need to make the same changes to
+ * {@link FixedWidthFontRenderer}.
+ */
+public final class DirectFixedWidthFontRenderer
+{
+ private DirectFixedWidthFontRenderer()
+ {
+ }
+
+ private static void drawChar( ByteBuffer buffer, float x, float y, int index, byte[] colour )
+ {
+ // Short circuit to avoid the common case - the texture should be blank here after all.
+ if( index == '\0' || index == ' ' ) return;
+
+ int column = index % 16;
+ int row = index / 16;
+
+ int xStart = 1 + column * (FONT_WIDTH + 2);
+ int yStart = 1 + row * (FONT_HEIGHT + 2);
+
+ quad(
+ buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, Z_EPSILON, colour,
+ xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH
+ );
+ }
+
+ private static void drawQuad( ByteBuffer emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
+ {
+ byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
+ quad( emitter, x, y, x + width, y + height, 0f, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END );
+ }
+
+ private static void drawBackground(
+ @Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
+ float leftMarginSize, float rightMarginSize, float height
+ )
+ {
+ if( leftMarginSize > 0 )
+ {
+ drawQuad( buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
+ }
+
+ if( rightMarginSize > 0 )
+ {
+ drawQuad( buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
+ }
+
+ // Batch together runs of identical background cells.
+ int blockStart = 0;
+ char blockColour = '\0';
+ for( int i = 0; i < backgroundColour.length(); i++ )
+ {
+ char colourIndex = backgroundColour.charAt( i );
+ if( colourIndex == blockColour ) continue;
+
+ if( blockColour != '\0' )
+ {
+ drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
+ }
+
+ blockColour = colourIndex;
+ blockStart = i;
+ }
+
+ if( blockColour != '\0' )
+ {
+ drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
+ }
+ }
+
+ private static void drawString( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale )
+ {
+ for( int i = 0; i < text.length(); i++ )
+ {
+ byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
+
+ int index = text.charAt( i );
+ if( index > 255 ) index = '?';
+ drawChar( buffer, x + i * FONT_WIDTH, y, index, colour );
+ }
+ }
+
+ public static void drawTerminalWithoutCursor(
+ @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
+ float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
+ )
+ {
+ Palette palette = terminal.getPalette();
+ int height = terminal.getHeight();
+
+ // Top and bottom margins
+ drawBackground(
+ buffer, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale,
+ leftMarginSize, rightMarginSize, topMarginSize
+ );
+
+ drawBackground(
+ buffer, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
+ leftMarginSize, rightMarginSize, bottomMarginSize
+ );
+
+ // The main text
+ for( int i = 0; i < height; i++ )
+ {
+ float rowY = y + FONT_HEIGHT * i;
+ drawBackground(
+ buffer, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale,
+ leftMarginSize, rightMarginSize, FONT_HEIGHT
+ );
+ drawString(
+ buffer, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ),
+ palette, greyscale
+ );
+ }
+ }
+
+ public static void drawCursor( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale )
+ {
+ if( isCursorVisible( terminal ) )
+ {
+ byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
+ drawChar( buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour );
+ }
+ }
+
+ public static int getVertexCount( Terminal terminal )
+ {
+ return (1 + (terminal.getHeight() + 2) * terminal.getWidth() * 2) * 4;
+ }
+
+ private static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2 )
+ {
+ // Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
+ // underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
+ // This provides significant performance gains, at the cost of well, using Unsafe.
+ // Each vertex is 28 bytes, giving 112 bytes in total. Vertices are of the form (xyz:FFF)(rgba:BBBB)(uv1:FF)(uv2:SS),
+ // which matches the POSITION_COLOR_TEX_LIGHTMAP vertex format.
+
+ int position = buffer.position();
+ long addr = MemoryUtil.memAddress( buffer );
+
+ // We're doing terrible unsafe hacks below, so let's be really sure that what we're doing is reasonable.
+ if( position < 0 || 112 > buffer.limit() - position ) throw new IndexOutOfBoundsException();
+ // Require the pointer to be aligned to a 32-bit boundary.
+ if( (addr & 3) != 0 ) throw new IllegalStateException( "Memory is not aligned" );
+ // Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
+ if( rgba.length != 4 ) throw new IllegalStateException();
+
+ memPutFloat( addr + 0, x1 );
+ memPutFloat( addr + 4, y1 );
+ memPutFloat( addr + 8, z );
+ memPutByte( addr + 12, rgba[0] );
+ memPutByte( addr + 13, rgba[1] );
+ memPutByte( addr + 14, rgba[2] );
+ memPutByte( addr + 15, (byte) 255 );
+ memPutFloat( addr + 16, u1 );
+ memPutFloat( addr + 20, v1 );
+ memPutShort( addr + 24, (short) 0xF0 );
+ memPutShort( addr + 26, (short) 0xF0 );
+
+ memPutFloat( addr + 28, x1 );
+ memPutFloat( addr + 32, y2 );
+ memPutFloat( addr + 36, z );
+ memPutByte( addr + 40, rgba[0] );
+ memPutByte( addr + 41, rgba[1] );
+ memPutByte( addr + 42, rgba[2] );
+ memPutByte( addr + 43, (byte) 255 );
+ memPutFloat( addr + 44, u1 );
+ memPutFloat( addr + 48, v2 );
+ memPutShort( addr + 52, (short) 0xF0 );
+ memPutShort( addr + 54, (short) 0xF0 );
+
+ memPutFloat( addr + 56, x2 );
+ memPutFloat( addr + 60, y2 );
+ memPutFloat( addr + 64, z );
+ memPutByte( addr + 68, rgba[0] );
+ memPutByte( addr + 69, rgba[1] );
+ memPutByte( addr + 70, rgba[2] );
+ memPutByte( addr + 71, (byte) 255 );
+ memPutFloat( addr + 72, u2 );
+ memPutFloat( addr + 76, v2 );
+ memPutShort( addr + 80, (short) 0xF0 );
+ memPutShort( addr + 82, (short) 0xF0 );
+
+ memPutFloat( addr + 84, x2 );
+ memPutFloat( addr + 88, y1 );
+ memPutFloat( addr + 92, z );
+ memPutByte( addr + 96, rgba[0] );
+ memPutByte( addr + 97, rgba[1] );
+ memPutByte( addr + 98, rgba[2] );
+ memPutByte( addr + 99, (byte) 255 );
+ memPutFloat( addr + 100, u2 );
+ memPutFloat( addr + 104, v1 );
+ memPutShort( addr + 108, (short) 0xF0 );
+ memPutShort( addr + 110, (short) 0xF0 );
+
+ // Finally increment the position.
+ buffer.position( position + 112 );
+
+ // Well done for getting to the end of this method. I recommend you take a break and go look at cute puppies.
+ }
+}
diff --git a/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java
new file mode 100644
index 000000000..574fb86b0
--- /dev/null
+++ b/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java
@@ -0,0 +1,243 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.client.render.text;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import com.mojang.math.Matrix4f;
+import com.mojang.math.Vector3f;
+import dan200.computercraft.client.FrameInfo;
+import dan200.computercraft.core.terminal.Terminal;
+import dan200.computercraft.core.terminal.TextBuffer;
+import dan200.computercraft.shared.util.Colour;
+import dan200.computercraft.shared.util.Palette;
+import net.minecraft.client.renderer.texture.OverlayTexture;
+import net.minecraft.resources.ResourceLocation;
+
+import javax.annotation.Nonnull;
+
+import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
+
+/**
+ * Handles rendering fixed width text and computer terminals.
+ *
+ * This class has several modes of usage:
+ *
+ * {@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods,
+ * this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.
+ * {@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor
+ * separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically.
+ *
+ * {@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the
+ * whole term.
+ *
+ *
+ * IMPORTANT: When making changes to this class, please check if you need to make the same changes to
+ * {@link DirectFixedWidthFontRenderer}.
+ */
+public final class FixedWidthFontRenderer
+{
+ public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
+
+ public static final int FONT_HEIGHT = 9;
+ public static final int FONT_WIDTH = 6;
+ static final float WIDTH = 256.0f;
+
+ static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
+ static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
+
+ private static final byte[] BLACK = new byte[] { byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), (byte) 255 };
+
+ public static final float Z_EPSILON = 0.001f;
+
+ private FixedWidthFontRenderer()
+ {
+ }
+
+ private static byte byteColour( float c )
+ {
+ return (byte) (int) (c * 255);
+ }
+
+ public static float toGreyscale( double[] rgb )
+ {
+ return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
+ }
+
+ public static int getColour( char c, Colour def )
+ {
+ return 15 - Terminal.getColour( c, def );
+ }
+
+ private static void drawChar( QuadEmitter emitter, float x, float y, int index, byte[] colour, int light )
+ {
+ // Short circuit to avoid the common case - the texture should be blank here after all.
+ if( index == '\0' || index == ' ' ) return;
+
+ int column = index % 16;
+ int row = index / 16;
+
+ int xStart = 1 + column * (FONT_WIDTH + 2);
+ int yStart = 1 + row * (FONT_HEIGHT + 2);
+
+ quad(
+ emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, Z_EPSILON, colour,
+ xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light
+ );
+ }
+
+ public static void drawQuad( QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light )
+ {
+ quad( emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light );
+ }
+
+ private static void drawQuad( QuadEmitter emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light )
+ {
+ byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
+ drawQuad( emitter, x, y, 0, width, height, colour, light );
+ }
+
+ private static void drawBackground(
+ @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
+ float leftMarginSize, float rightMarginSize, float height, int light
+ )
+ {
+ if( leftMarginSize > 0 )
+ {
+ drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light );
+ }
+
+ if( rightMarginSize > 0 )
+ {
+ drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light );
+ }
+
+ // Batch together runs of identical background cells.
+ int blockStart = 0;
+ char blockColour = '\0';
+ for( int i = 0; i < backgroundColour.length(); i++ )
+ {
+ char colourIndex = backgroundColour.charAt( i );
+ if( colourIndex == blockColour ) continue;
+
+ if( blockColour != '\0' )
+ {
+ drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light );
+ }
+
+ blockColour = colourIndex;
+ blockStart = i;
+ }
+
+ if( blockColour != '\0' )
+ {
+ drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light );
+ }
+ }
+
+ public static void drawString( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale, int light )
+ {
+ for( int i = 0; i < text.length(); i++ )
+ {
+ byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
+
+ int index = text.charAt( i );
+ if( index > 255 ) index = '?';
+ drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, light );
+ }
+
+ }
+
+ public static void drawTerminalWithoutCursor(
+ @Nonnull QuadEmitter emitter, float x, float y,
+ @Nonnull Terminal terminal, boolean greyscale,
+ float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
+ )
+ {
+ Palette palette = terminal.getPalette();
+ int height = terminal.getHeight();
+
+ // Top and bottom margins
+ drawBackground(
+ emitter, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale,
+ leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP
+ );
+
+ drawBackground(
+ emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
+ leftMarginSize, rightMarginSize, bottomMarginSize, FULL_BRIGHT_LIGHTMAP
+ );
+
+ // The main text
+ for( int i = 0; i < height; i++ )
+ {
+ float rowY = y + FixedWidthFontRenderer.FONT_HEIGHT * i;
+ drawBackground(
+ emitter, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale,
+ leftMarginSize, rightMarginSize, FONT_HEIGHT, FULL_BRIGHT_LIGHTMAP
+ );
+ drawString(
+ emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ),
+ palette, greyscale, FULL_BRIGHT_LIGHTMAP
+ );
+ }
+ }
+
+ public static boolean isCursorVisible( Terminal terminal )
+ {
+ if( !terminal.getCursorBlink() ) return false;
+
+ int cursorX = terminal.getCursorX();
+ int cursorY = terminal.getCursorY();
+ return cursorX >= 0 && cursorX < terminal.getWidth() && cursorY >= 0 && cursorY < terminal.getHeight();
+ }
+
+ public static void drawCursor( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale )
+ {
+ if( isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() )
+ {
+ byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
+ drawChar( emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP );
+ }
+ }
+
+ public static void drawTerminal(
+ @Nonnull QuadEmitter emitter, float x, float y,
+ @Nonnull Terminal terminal, boolean greyscale,
+ float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
+ )
+ {
+ drawTerminalWithoutCursor( emitter, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
+ drawCursor( emitter, x, y, terminal, greyscale );
+ }
+
+ public static void drawEmptyTerminal( @Nonnull QuadEmitter emitter, float x, float y, float width, float height )
+ {
+ drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
+ }
+
+ public record QuadEmitter(Matrix4f poseMatrix, Vector3f normal, VertexConsumer consumer ) {}
+
+ public static QuadEmitter toVertexConsumer( PoseStack transform, VertexConsumer consumer )
+ {
+ var normal = new Vector3f( 0f, 0f, 1f );
+ normal.transform( transform.last().normal() );
+ return new QuadEmitter( transform.last().pose(), normal, consumer );
+ }
+
+ private static void quad( QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light )
+ {
+ var poseMatrix = c.poseMatrix();
+ var consumer = c.consumer();
+ var normal = c.normal();
+ byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
+
+ consumer.vertex( poseMatrix, x1, y1, z ).color( rgba[0], rgba[1], rgba[2], rgba[3] ).uv( u1, v1 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( light ).normal( normal.x(), normal.y(), normal.z() ).endVertex();
+ consumer.vertex( poseMatrix, x1, y2, z ).color( rgba[0], rgba[1], rgba[2], rgba[3] ).uv( u1, v2 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( light ).normal( normal.x(), normal.y(), normal.z() ).endVertex();
+ consumer.vertex( poseMatrix, x2, y2, z ).color( rgba[0], rgba[1], rgba[2], rgba[3] ).uv( u2, v2 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( light ).normal( normal.x(), normal.y(), normal.z() ).endVertex();
+ consumer.vertex( poseMatrix, x2, y1, z ).color( rgba[0], rgba[1], rgba[2], rgba[3] ).uv( u2, v1 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( light ).normal( normal.x(), normal.y(), normal.z() ).endVertex();
+ }
+}
diff --git a/src/main/java/dan200/computercraft/client/sound/SpeakerInstance.java b/src/main/java/dan200/computercraft/client/sound/SpeakerInstance.java
index 3ac5a6092..e52988174 100644
--- a/src/main/java/dan200/computercraft/client/sound/SpeakerInstance.java
+++ b/src/main/java/dan200/computercraft/client/sound/SpeakerInstance.java
@@ -6,10 +6,10 @@
package dan200.computercraft.client.sound;
import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.phys.Vec3;
/**
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
@@ -43,7 +43,7 @@ public synchronized void pushAudio( ByteBuf buffer )
}
}
- public void playAudio( Vec3 position, float volume )
+ public void playAudio( SpeakerPosition position, float volume )
{
var soundManager = Minecraft.getInstance().getSoundManager();
@@ -62,7 +62,7 @@ public void playAudio( Vec3 position, float volume )
}
}
- public void playSound( Vec3 position, ResourceLocation location, float volume, float pitch )
+ public void playSound( SpeakerPosition position, ResourceLocation location, float volume, float pitch )
{
var soundManager = Minecraft.getInstance().getSoundManager();
currentStream = null;
@@ -77,7 +77,7 @@ public void playSound( Vec3 position, ResourceLocation location, float volume, f
soundManager.play( sound );
}
- void setPosition( Vec3 position )
+ void setPosition( SpeakerPosition position )
{
if( sound != null ) sound.setPosition( position );
}
diff --git a/src/main/java/dan200/computercraft/client/sound/SpeakerManager.java b/src/main/java/dan200/computercraft/client/sound/SpeakerManager.java
index f8c738461..4953cd763 100644
--- a/src/main/java/dan200/computercraft/client/sound/SpeakerManager.java
+++ b/src/main/java/dan200/computercraft/client/sound/SpeakerManager.java
@@ -5,10 +5,10 @@
*/
package dan200.computercraft.client.sound;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import com.mojang.blaze3d.audio.Channel;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.sounds.SoundEngine;
-import net.minecraft.world.phys.Vec3;
import java.util.Map;
import java.util.UUID;
@@ -24,8 +24,7 @@ public class SpeakerManager
// A return value of true cancels the event
public static boolean playStreaming( SoundEngine engine, SoundInstance soundInstance, Channel channel )
{
- if( !(soundInstance instanceof SpeakerSound sound) ) return false;
- if( sound.stream == null ) return false;
+ if( !(soundInstance instanceof SpeakerSound sound) || sound.stream == null ) return false;
channel.attachBufferStream( sound.stream );
channel.play();
@@ -46,7 +45,7 @@ public static void stopSound( UUID source )
if( sound != null ) sound.stop();
}
- public static void moveSound( UUID source, Vec3 position )
+ public static void moveSound( UUID source, SpeakerPosition position )
{
SpeakerInstance sound = sounds.get( source );
if( sound != null ) sound.setPosition( position );
diff --git a/src/main/java/dan200/computercraft/client/sound/SpeakerSound.java b/src/main/java/dan200/computercraft/client/sound/SpeakerSound.java
index 636ef307c..cacc83d07 100644
--- a/src/main/java/dan200/computercraft/client/sound/SpeakerSound.java
+++ b/src/main/java/dan200/computercraft/client/sound/SpeakerSound.java
@@ -6,14 +6,13 @@
package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
import net.minecraft.client.resources.sounds.TickableSoundInstance;
-import net.minecraft.client.sounds.AudioStream;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundSource;
-import net.minecraft.world.phys.Vec3;
+import net.minecraft.world.entity.Entity;
-import javax.annotation.Nullable;
import java.util.concurrent.Executor;
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance
@@ -22,7 +21,11 @@ public class SpeakerSound extends AbstractSoundInstance implements TickableSound
Executor executor;
DfpwmStream stream;
- SpeakerSound( ResourceLocation sound, DfpwmStream stream, Vec3 position, float volume, float pitch )
+ private Entity entity;
+
+ private boolean stopped = false;
+
+ SpeakerSound( ResourceLocation sound, DfpwmStream stream, SpeakerPosition position, float volume, float pitch )
{
super( sound, SoundSource.RECORDS );
setPosition( position );
@@ -32,27 +35,34 @@ public class SpeakerSound extends AbstractSoundInstance implements TickableSound
attenuation = Attenuation.LINEAR;
}
- void setPosition( Vec3 position )
+ void setPosition( SpeakerPosition position )
{
- x = (float) position.x();
- y = (float) position.y();
- z = (float) position.z();
+ x = position.position().x;
+ y = position.position().y;
+ z = position.position().z;
+ entity = position.entity();
}
@Override
public boolean isStopped()
{
- return false;
+ return stopped;
}
@Override
public void tick()
{
- }
-
- @Nullable
- public AudioStream getStream()
- {
- return stream;
+ if( entity == null ) return;
+ if( !entity.isAlive() )
+ {
+ stopped = true;
+ looping = false;
+ }
+ else
+ {
+ x = entity.getX();
+ y = entity.getY();
+ z = entity.getZ();
+ }
}
}
diff --git a/src/main/java/dan200/computercraft/client/util/DirectBuffers.java b/src/main/java/dan200/computercraft/client/util/DirectBuffers.java
new file mode 100644
index 000000000..1c83fba3b
--- /dev/null
+++ b/src/main/java/dan200/computercraft/client/util/DirectBuffers.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.client.util;
+
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferUploader;
+import net.minecraft.Util;
+import org.lwjgl.opengl.GL;
+import org.lwjgl.opengl.GL15C;
+import org.lwjgl.opengl.GL45C;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Provides utilities to interact with OpenGL's buffer objects, either using direct state access or binding/unbinding
+ * it.
+ */
+public class DirectBuffers
+{
+ public static final boolean HAS_DSA;
+ static final boolean ON_LINUX = Util.getPlatform() == Util.OS.LINUX;
+
+ static
+ {
+ var capabilities = GL.getCapabilities();
+ HAS_DSA = capabilities.OpenGL45 || capabilities.GL_ARB_direct_state_access;
+ }
+
+ public static int createBuffer()
+ {
+ return HAS_DSA ? GL45C.glCreateBuffers() : GL15C.glGenBuffers();
+ }
+
+ /**
+ * Delete a previously created buffer.
+ *
+ * On Linux, {@link GlStateManager#_glDeleteBuffers(int)} clears a buffer before deleting it. However, this involves
+ * binding and unbinding the buffer, conflicting with {@link BufferUploader}'s cache. This deletion method uses
+ * our existing {@link #setEmptyBufferData(int, int, int)}, which correctly handles clearing the buffer.
+ *
+ * @param type The buffer's type.
+ * @param id The buffer's ID.
+ */
+ public static void deleteBuffer( int type, int id )
+ {
+ RenderSystem.assertOnRenderThread();
+ if( ON_LINUX ) DirectBuffers.setEmptyBufferData( type, id, GL15C.GL_DYNAMIC_DRAW );
+ GL15C.glDeleteBuffers( id );
+ }
+
+ public static void setBufferData( int type, int id, ByteBuffer buffer, int flags )
+ {
+ if( HAS_DSA )
+ {
+ GL45C.glNamedBufferData( id, buffer, flags );
+ }
+ else
+ {
+ if( type == GL15C.GL_ARRAY_BUFFER ) BufferUploader.reset();
+ GlStateManager._glBindBuffer( type, id );
+ GlStateManager._glBufferData( type, buffer, flags );
+ GlStateManager._glBindBuffer( type, 0 );
+ }
+ }
+
+ public static void setEmptyBufferData( int type, int id, int flags )
+ {
+ if( HAS_DSA )
+ {
+ GL45C.glNamedBufferData( id, 0, flags );
+ }
+ else
+ {
+ if( type == GL15C.GL_ARRAY_BUFFER ) BufferUploader.reset();
+ GlStateManager._glBindBuffer( type, id );
+ GlStateManager._glBufferData( type, 0, flags );
+ GlStateManager._glBindBuffer( type, 0 );
+ }
+ }
+}
diff --git a/src/main/java/dan200/computercraft/client/util/DirectVertexBuffer.java b/src/main/java/dan200/computercraft/client/util/DirectVertexBuffer.java
new file mode 100644
index 000000000..47ad69263
--- /dev/null
+++ b/src/main/java/dan200/computercraft/client/util/DirectVertexBuffer.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.client.util;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.BufferUploader;
+import com.mojang.blaze3d.vertex.VertexBuffer;
+import com.mojang.blaze3d.vertex.VertexFormat;
+import com.mojang.math.Matrix4f;
+import net.minecraft.client.renderer.ShaderInstance;
+import org.lwjgl.opengl.GL15;
+import org.lwjgl.opengl.GL45C;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A version of {@link VertexBuffer} which allows uploading {@link ByteBuffer}s directly.
+ *
+ * This should probably be its own class (rather than subclassing), but I need access to {@link VertexBuffer#drawWithShader}.
+ */
+public class DirectVertexBuffer extends VertexBuffer
+{
+ private int actualIndexCount;
+
+ public DirectVertexBuffer()
+ {
+ if( DirectBuffers.HAS_DSA )
+ {
+ RenderSystem.glDeleteBuffers( vertextBufferId );
+ if( DirectBuffers.ON_LINUX ) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
+ vertextBufferId = GL45C.glCreateBuffers();
+ }
+ }
+
+ public void upload( int vertexCount, VertexFormat.Mode mode, VertexFormat format, ByteBuffer buffer )
+ {
+ RenderSystem.assertOnRenderThread();
+
+ DirectBuffers.setBufferData( GL15.GL_ARRAY_BUFFER, vertextBufferId, buffer, GL15.GL_STATIC_DRAW );
+
+ this.format = format;
+ this.mode = mode;
+ actualIndexCount = indexCount = mode.indexCount( vertexCount );
+ indexType = VertexFormat.IndexType.SHORT;
+ sequentialIndices = true;
+ }
+
+ public void drawWithShader( Matrix4f modelView, Matrix4f projection, ShaderInstance shader, int indexCount )
+ {
+ this.indexCount = indexCount;
+ drawWithShader( modelView, projection, shader );
+ this.indexCount = actualIndexCount;
+ }
+
+ public int getIndexCount()
+ {
+ return actualIndexCount;
+ }
+
+ @Override
+ public void close()
+ {
+ super.close();
+ if( DirectBuffers.ON_LINUX ) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.
+ }
+}
diff --git a/src/main/java/dan200/computercraft/core/tracking/ComputerMBean.java b/src/main/java/dan200/computercraft/core/tracking/ComputerMBean.java
index 965285beb..5d345f114 100644
--- a/src/main/java/dan200/computercraft/core/tracking/ComputerMBean.java
+++ b/src/main/java/dan200/computercraft/core/tracking/ComputerMBean.java
@@ -57,7 +57,8 @@ public static void register()
{
ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) );
}
- catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | MalformedObjectNameException e )
+ catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException |
+ MalformedObjectNameException e )
{
ComputerCraft.log.warn( "Failed to register JMX bean", e );
}
diff --git a/src/main/java/dan200/computercraft/data/BlockModelProvider.java b/src/main/java/dan200/computercraft/data/BlockModelProvider.java
new file mode 100644
index 000000000..1ffc05762
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/BlockModelProvider.java
@@ -0,0 +1,250 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.shared.Registry;
+import dan200.computercraft.shared.computer.blocks.BlockComputer;
+import dan200.computercraft.shared.computer.core.ComputerState;
+import dan200.computercraft.shared.peripheral.modem.wired.BlockWiredModemFull;
+import dan200.computercraft.shared.peripheral.modem.wireless.BlockWirelessModem;
+import dan200.computercraft.shared.peripheral.monitor.BlockMonitor;
+import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState;
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
+import net.minecraft.core.Direction;
+import net.minecraft.data.models.BlockModelGenerators;
+import net.minecraft.data.models.ItemModelGenerators;
+import net.minecraft.data.models.blockstates.MultiVariantGenerator;
+import net.minecraft.data.models.blockstates.PropertyDispatch;
+import net.minecraft.data.models.blockstates.Variant;
+import net.minecraft.data.models.blockstates.VariantProperties;
+import net.minecraft.data.models.model.*;
+import net.minecraft.resources.ResourceLocation;
+
+import java.util.Optional;
+
+import static net.minecraft.data.models.model.ModelLocationUtils.getModelLocation;
+import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
+
+public class BlockModelProvider extends FabricModelProvider
+{
+ private static final ModelTemplate MONITOR_BASE = new ModelTemplate(
+ Optional.of( new ResourceLocation( ComputerCraft.MOD_ID, "block/monitor_base" ) ),
+ Optional.empty(),
+ TextureSlot.FRONT, TextureSlot.SIDE, TextureSlot.TOP, TextureSlot.BACK
+ );
+ private static final ModelTemplate MODEM = new ModelTemplate(
+ Optional.of( new ResourceLocation( ComputerCraft.MOD_ID, "block/modem" ) ),
+ Optional.empty(),
+ TextureSlot.FRONT, TextureSlot.BACK
+ );
+
+ public BlockModelProvider( FabricDataGenerator dataGenerator )
+ {
+ super( dataGenerator );
+ }
+
+ @Override
+ public void generateBlockStateModels( BlockModelGenerators generators )
+ {
+ registerComputer( generators, Registry.ModBlocks.COMPUTER_NORMAL );
+ registerComputer( generators, Registry.ModBlocks.COMPUTER_ADVANCED );
+ registerComputer( generators, Registry.ModBlocks.COMPUTER_COMMAND );
+
+ registerWirelessModem( generators, Registry.ModBlocks.WIRELESS_MODEM_NORMAL );
+ registerWirelessModem( generators, Registry.ModBlocks.WIRELESS_MODEM_ADVANCED );
+
+ registerWiredModems( generators );
+
+ registerMonitors( generators, Registry.ModBlocks.MONITOR_NORMAL );
+ registerMonitors( generators, Registry.ModBlocks.MONITOR_ADVANCED );
+
+ generators.createHorizontallyRotatedBlock( Registry.ModBlocks.SPEAKER, TexturedModel.ORIENTABLE_ONLY_TOP );
+ generators.delegateItemModel( Registry.ModBlocks.SPEAKER, getModelLocation( Registry.ModBlocks.SPEAKER ) );
+ }
+
+ @Override
+ public void generateItemModels( ItemModelGenerators itemModelGenerator )
+ {
+
+ }
+
+ private void registerComputer( BlockModelGenerators generators, BlockComputer> block )
+ {
+ var generator = PropertyDispatch.properties( BlockComputer.STATE, BlockComputer.FACING );
+ for( ComputerState state : BlockComputer.STATE.getPossibleValues() )
+ {
+ var model = ModelTemplates.CUBE_ORIENTABLE.create(
+ getModelLocation( block, "_" + state ),
+ new TextureMapping()
+ .put( TextureSlot.SIDE, getBlockTexture( block, "_side" ) )
+ .put( TextureSlot.FRONT, getBlockTexture( block, "_front" + state.getTexture() ) )
+ .put( TextureSlot.TOP, getBlockTexture( block, "_top" ) ),
+ generators.modelOutput
+ );
+
+ for( Direction facing : BlockComputer.FACING.getPossibleValues() )
+ {
+ generator.select( state, facing, Variant.variant()
+ .with( VariantProperties.Y_ROT, toYAngle( facing ) )
+ .with( VariantProperties.MODEL, model )
+ );
+ }
+ }
+
+ generators.blockStateOutput.accept( MultiVariantGenerator.multiVariant( block ).with( generator ) );
+ generators.delegateItemModel( block, getModelLocation( block, "_blinking" ) );
+ }
+
+ private void registerWirelessModem( BlockModelGenerators generators, BlockWirelessModem block )
+ {
+ var generator = PropertyDispatch.properties( BlockWirelessModem.FACING, BlockWirelessModem.ON );
+
+ for( boolean on : BlockWirelessModem.ON.getPossibleValues() )
+ {
+ var model = modemModel( generators, getModelLocation( block, on ? "_on" : "_off" ), getBlockTexture( block, "_face" + (on ? "_on" : "") ) );
+
+ for( Direction facing : BlockWirelessModem.FACING.getPossibleValues() )
+ {
+ generator.select( facing, on, Variant.variant()
+ .with( VariantProperties.X_ROT, toXAngle( facing ) )
+ .with( VariantProperties.Y_ROT, toYAngle( facing ) )
+ .with( VariantProperties.MODEL, model )
+ );
+ }
+ }
+
+ generators.blockStateOutput.accept( MultiVariantGenerator.multiVariant( block ).with( generator ) );
+ generators.delegateItemModel( block, getModelLocation( block, "_off" ) );
+ }
+
+ private void registerWiredModems( BlockModelGenerators generators )
+ {
+ BlockWiredModemFull fullBlock = Registry.ModBlocks.WIRED_MODEM_FULL;
+ var fullBlockGenerator = PropertyDispatch.properties( BlockWiredModemFull.MODEM_ON, BlockWiredModemFull.PERIPHERAL_ON );
+ for( boolean on : BlockWiredModemFull.MODEM_ON.getPossibleValues() )
+ {
+ for( boolean peripheral : BlockWiredModemFull.PERIPHERAL_ON.getPossibleValues() )
+ {
+ String suffix = (on ? "_on" : "_off") + (peripheral ? "_peripheral" : "");
+ ResourceLocation faceTexture = new ResourceLocation(
+ ComputerCraft.MOD_ID,
+ "block/wired_modem_face" + (peripheral ? "_peripheral" : "") + (on ? "_on" : "")
+ );
+ var fullBlockModel = ModelTemplates.CUBE_ALL.create(
+ getModelLocation( fullBlock, suffix ),
+ new TextureMapping().put( TextureSlot.ALL, faceTexture ),
+ generators.modelOutput
+ );
+
+ fullBlockGenerator.select( on, peripheral, Variant.variant().with( VariantProperties.MODEL, fullBlockModel ) );
+
+ modemModel( generators, new ResourceLocation( ComputerCraft.MOD_ID, "block/wired_modem" + suffix ), faceTexture );
+ }
+ }
+
+
+ generators.blockStateOutput.accept( MultiVariantGenerator.multiVariant( fullBlock ).with( fullBlockGenerator ) );
+ generators.delegateItemModel( fullBlock, getModelLocation( fullBlock, "_off" ) );
+ generators.delegateItemModel( Registry.ModItems.WIRED_MODEM, new ResourceLocation( ComputerCraft.MOD_ID, "block/wired_modem_off" ) );
+ }
+
+ private ResourceLocation modemModel( BlockModelGenerators generators, ResourceLocation name, ResourceLocation texture )
+ {
+ return MODEM.create(
+ name,
+ new TextureMapping()
+ .put( TextureSlot.FRONT, texture )
+ .put( TextureSlot.BACK, new ResourceLocation( ComputerCraft.MOD_ID, "block/modem_back" ) ),
+ generators.modelOutput
+ );
+ }
+
+ private void registerMonitors( BlockModelGenerators generators, BlockMonitor block )
+ {
+ monitorModel( generators, block, "", 16, 4, 0, 32 );
+ monitorModel( generators, block, "_d", 20, 7, 0, 36 );
+ monitorModel( generators, block, "_l", 19, 4, 1, 33 );
+ monitorModel( generators, block, "_ld", 31, 7, 1, 45 );
+ monitorModel( generators, block, "_lr", 18, 4, 2, 34 );
+ monitorModel( generators, block, "_lrd", 30, 7, 2, 46 );
+ monitorModel( generators, block, "_lru", 24, 5, 2, 40 );
+ monitorModel( generators, block, "_lrud", 27, 6, 2, 43 );
+ monitorModel( generators, block, "_lu", 25, 5, 1, 39 );
+ monitorModel( generators, block, "_lud", 28, 6, 1, 42 );
+ monitorModel( generators, block, "_r", 17, 4, 3, 35 );
+ monitorModel( generators, block, "_rd", 29, 7, 3, 47 );
+ monitorModel( generators, block, "_ru", 23, 5, 3, 41 );
+ monitorModel( generators, block, "_rud", 26, 6, 3, 44 );
+ monitorModel( generators, block, "_u", 22, 5, 0, 38 );
+ monitorModel( generators, block, "_ud", 21, 6, 0, 37 );
+
+ var generator = PropertyDispatch.properties( BlockMonitor.STATE, BlockMonitor.FACING, BlockMonitor.ORIENTATION );
+ for( MonitorEdgeState edge : BlockMonitor.STATE.getPossibleValues() )
+ {
+ String suffix = edge == MonitorEdgeState.NONE ? "" : "_" + edge.getSerializedName();
+ var model = getModelLocation( block, suffix );
+
+ for( Direction facing : BlockMonitor.FACING.getPossibleValues() )
+ {
+ for( Direction orientation : BlockMonitor.ORIENTATION.getPossibleValues() )
+ {
+ generator.select( edge, facing, orientation, Variant.variant()
+ .with( VariantProperties.MODEL, model )
+ .with( VariantProperties.X_ROT, toXAngle( orientation ) )
+ .with( VariantProperties.Y_ROT, toYAngle( facing ) )
+ );
+ }
+ }
+ }
+
+ generators.blockStateOutput.accept( MultiVariantGenerator.multiVariant( block ).with( generator ) );
+ generators.delegateItemModel( block, monitorModel( generators, block, "_item", 15, 4, 0, 32 ) );
+ }
+
+ private ResourceLocation monitorModel( BlockModelGenerators generators, BlockMonitor block, String corners, int front, int side, int top, int back )
+ {
+ return MONITOR_BASE.create(
+ getModelLocation( block, corners ),
+ new TextureMapping()
+ .put( TextureSlot.FRONT, getBlockTexture( block, "_" + front ) )
+ .put( TextureSlot.SIDE, getBlockTexture( block, "_" + side ) )
+ .put( TextureSlot.TOP, getBlockTexture( block, "_" + top ) )
+ .put( TextureSlot.BACK, getBlockTexture( block, "_" + back ) ),
+ generators.modelOutput
+ );
+ }
+
+ private static VariantProperties.Rotation toXAngle( Direction direction )
+ {
+ switch( direction )
+ {
+ default:
+ return VariantProperties.Rotation.R0;
+ case UP:
+ return VariantProperties.Rotation.R270;
+ case DOWN:
+ return VariantProperties.Rotation.R90;
+ }
+ }
+
+ private static VariantProperties.Rotation toYAngle( Direction direction )
+ {
+ switch( direction )
+ {
+ default:
+ case NORTH:
+ return VariantProperties.Rotation.R0;
+ case SOUTH:
+ return VariantProperties.Rotation.R180;
+ case EAST:
+ return VariantProperties.Rotation.R90;
+ case WEST:
+ return VariantProperties.Rotation.R270;
+ }
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/BlockTagsGenerator.java b/src/main/java/dan200/computercraft/data/BlockTagsGenerator.java
new file mode 100644
index 000000000..5905987d2
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/BlockTagsGenerator.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import dan200.computercraft.shared.Registry;
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.tags.TagKey;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import org.jetbrains.annotations.NotNull;
+
+import static dan200.computercraft.api.ComputerCraftTags.Blocks.*;
+
+class BlockTagsGenerator extends FabricTagProvider.BlockTagProvider
+{
+ BlockTagsGenerator( FabricDataGenerator generator )
+ {
+ super( generator );
+ }
+
+ @Override
+ protected void generateTags()
+ {
+ // Items
+ tag( COMPUTER ).add(
+ Registry.ModBlocks.COMPUTER_NORMAL,
+ Registry.ModBlocks.COMPUTER_ADVANCED,
+ Registry.ModBlocks.COMPUTER_COMMAND
+ );
+ tag( TURTLE ).add( Registry.ModBlocks.TURTLE_NORMAL, Registry.ModBlocks.TURTLE_ADVANCED );
+ tag( WIRED_MODEM ).add( Registry.ModBlocks.CABLE, Registry.ModBlocks.WIRED_MODEM_FULL );
+ tag( MONITOR ).add( Registry.ModBlocks.MONITOR_NORMAL, Registry.ModBlocks.MONITOR_ADVANCED );
+
+ tag( TURTLE_ALWAYS_BREAKABLE ).forceAddTag( BlockTags.LEAVES ).add(
+ Blocks.BAMBOO, Blocks.BAMBOO_SAPLING // Bamboo isn't instabreak for some odd reason.
+ );
+
+ tag( TURTLE_SHOVEL_BREAKABLE ).forceAddTag( BlockTags.MINEABLE_WITH_SHOVEL ).add(
+ Blocks.MELON,
+ Blocks.PUMPKIN,
+ Blocks.CARVED_PUMPKIN,
+ Blocks.JACK_O_LANTERN
+ );
+
+ tag( TURTLE_HOE_BREAKABLE ).forceAddTag( BlockTags.CROPS ).forceAddTag( BlockTags.MINEABLE_WITH_HOE ).add(
+ Blocks.CACTUS,
+ Blocks.MELON,
+ Blocks.PUMPKIN,
+ Blocks.CARVED_PUMPKIN,
+ Blocks.JACK_O_LANTERN
+ );
+
+ tag( TURTLE_SWORD_BREAKABLE ).forceAddTag( BlockTags.WOOL ).add( Blocks.COBWEB );
+
+ // Make all blocks aside from command computer mineable.
+ tag( BlockTags.MINEABLE_WITH_PICKAXE ).add(
+ Registry.ModBlocks.COMPUTER_NORMAL,
+ Registry.ModBlocks.COMPUTER_ADVANCED,
+ Registry.ModBlocks.TURTLE_NORMAL,
+ Registry.ModBlocks.TURTLE_ADVANCED,
+ Registry.ModBlocks.SPEAKER,
+ Registry.ModBlocks.DISK_DRIVE,
+ Registry.ModBlocks.PRINTER,
+ Registry.ModBlocks.MONITOR_NORMAL,
+ Registry.ModBlocks.MONITOR_ADVANCED,
+ Registry.ModBlocks.WIRELESS_MODEM_NORMAL,
+ Registry.ModBlocks.WIRELESS_MODEM_ADVANCED,
+ Registry.ModBlocks.WIRED_MODEM_FULL,
+ Registry.ModBlocks.CABLE
+ );
+ }
+
+ @Override
+ protected FabricTagProvider.FabricTagBuilder tag( @NotNull TagKey tagKey )
+ {
+ // Fun mapping weirdness here!
+ return super.getOrCreateTagBuilder( tagKey );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/ExtraConventionalItemTags.java b/src/main/java/dan200/computercraft/data/ExtraConventionalItemTags.java
new file mode 100644
index 000000000..c7e204821
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/ExtraConventionalItemTags.java
@@ -0,0 +1,30 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import net.fabricmc.fabric.api.tag.convention.v1.ConventionalItemTags;
+import net.fabricmc.fabric.impl.tag.convention.TagRegistration;
+import net.minecraft.tags.TagKey;
+import net.minecraft.world.item.Item;
+
+/**
+ * Additional conventional item tags not built in to Fabric.
+ *
+ * @see ConventionalItemTags
+ */
+public class ExtraConventionalItemTags
+{
+ public static final TagKey- ENDER_PEARLS = register( "ender_pearls" );
+ public static final TagKey
- GOLD_BLOCKS = register( "gold_blocks" );
+ public static final TagKey
- SKULLS = register( "skulls" );
+ public static final TagKey
- STONES = register( "stones" );
+ public static final TagKey
- WOODEN_CHESTS = register( "wooden_chests" );
+
+ private static TagKey
- register( String tagID )
+ {
+ return TagRegistration.ITEM_TAG_REGISTRATION.registerCommon( tagID );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/Generators.java b/src/main/java/dan200/computercraft/data/Generators.java
new file mode 100644
index 000000000..d933ffeb9
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/Generators.java
@@ -0,0 +1,24 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
+
+public class Generators implements DataGeneratorEntrypoint
+{
+ @Override
+ public void onInitializeDataGenerator( FabricDataGenerator generator )
+ {
+ generator.addProvider( new RecipeGenerator( generator ) );
+ generator.addProvider( new LootTableGenerator( generator ) );
+ generator.addProvider( new BlockModelProvider( generator ) );
+
+ BlockTagsGenerator blockTags = new BlockTagsGenerator( generator );
+ generator.addProvider( blockTags );
+ generator.addProvider( new ItemTagsGenerator( generator, blockTags ) );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/ItemTagsGenerator.java b/src/main/java/dan200/computercraft/data/ItemTagsGenerator.java
new file mode 100644
index 000000000..a1c63534c
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/ItemTagsGenerator.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import dan200.computercraft.api.ComputerCraftTags.Blocks;
+import dan200.computercraft.shared.Registry;
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
+import net.minecraft.tags.ItemTags;
+import net.minecraft.world.item.Items;
+
+import static dan200.computercraft.api.ComputerCraftTags.Items.*;
+
+class ItemTagsGenerator extends FabricTagProvider.ItemTagProvider
+{
+ ItemTagsGenerator( FabricDataGenerator generator, BlockTagsGenerator blockTags )
+ {
+ super( generator, blockTags );
+ }
+
+ @Override
+ protected void generateTags()
+ {
+ copy( Blocks.COMPUTER, COMPUTER );
+ copy( Blocks.TURTLE, TURTLE );
+ tag( WIRED_MODEM ).add( Registry.ModItems.WIRED_MODEM, Registry.ModItems.WIRED_MODEM_FULL );
+ copy( Blocks.MONITOR, MONITOR );
+
+ tag( ItemTags.PIGLIN_LOVED ).add(
+ Registry.ModItems.COMPUTER_ADVANCED, Registry.ModItems.TURTLE_ADVANCED,
+ Registry.ModItems.WIRELESS_MODEM_ADVANCED, Registry.ModItems.POCKET_COMPUTER_ADVANCED,
+ Registry.ModItems.MONITOR_ADVANCED
+ );
+
+ tag( ExtraConventionalItemTags.ENDER_PEARLS ).add( Items.ENDER_PEARL );
+ tag( ExtraConventionalItemTags.GOLD_BLOCKS ).add( Items.GOLD_BLOCK );
+ tag( ExtraConventionalItemTags.SKULLS ).add(
+ Items.CREEPER_HEAD,
+ Items.DRAGON_HEAD,
+ Items.PLAYER_HEAD,
+ Items.SKELETON_SKULL,
+ Items.WITHER_SKELETON_SKULL,
+ Items.ZOMBIE_HEAD
+ );
+ tag( ExtraConventionalItemTags.STONES ).add(
+ Items.ANDESITE,
+ Items.DIORITE,
+ Items.GRANITE,
+ Items.INFESTED_STONE,
+ Items.STONE,
+ Items.POLISHED_ANDESITE,
+ Items.POLISHED_DIORITE,
+ Items.POLISHED_GRANITE,
+ Items.DEEPSLATE,
+ Items.POLISHED_DEEPSLATE,
+ Items.INFESTED_DEEPSLATE,
+ Items.TUFF
+ );
+ tag( ExtraConventionalItemTags.WOODEN_CHESTS ).add(
+ Items.CHEST,
+ Items.TRAPPED_CHEST
+ );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/LootTableGenerator.java b/src/main/java/dan200/computercraft/data/LootTableGenerator.java
new file mode 100644
index 000000000..1ac4c1dab
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/LootTableGenerator.java
@@ -0,0 +1,112 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.shared.Registry;
+import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
+import dan200.computercraft.shared.data.HasComputerIdLootCondition;
+import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
+import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
+import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
+import net.minecraft.advancements.critereon.StatePropertiesPredicate;
+import net.minecraft.data.DataGenerator;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.storage.loot.LootPool;
+import net.minecraft.world.level.storage.loot.LootTable;
+import net.minecraft.world.level.storage.loot.entries.DynamicLoot;
+import net.minecraft.world.level.storage.loot.entries.LootItem;
+import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
+import net.minecraft.world.level.storage.loot.predicates.AlternativeLootItemCondition;
+import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
+import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
+import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
+
+import java.util.function.BiConsumer;
+
+class LootTableGenerator extends LootTableProvider
+{
+ LootTableGenerator( DataGenerator generator )
+ {
+ super( generator );
+ }
+
+ @Override
+ protected void registerLoot( BiConsumer
add )
+ {
+ basicDrop( add, Registry.ModBlocks.DISK_DRIVE );
+ basicDrop( add, Registry.ModBlocks.MONITOR_NORMAL );
+ basicDrop( add, Registry.ModBlocks.MONITOR_ADVANCED );
+ basicDrop( add, Registry.ModBlocks.PRINTER );
+ basicDrop( add, Registry.ModBlocks.SPEAKER );
+ basicDrop( add, Registry.ModBlocks.WIRED_MODEM_FULL );
+ basicDrop( add, Registry.ModBlocks.WIRELESS_MODEM_NORMAL );
+ basicDrop( add, Registry.ModBlocks.WIRELESS_MODEM_ADVANCED );
+
+ computerDrop( add, Registry.ModBlocks.COMPUTER_NORMAL );
+ computerDrop( add, Registry.ModBlocks.COMPUTER_ADVANCED );
+ computerDrop( add, Registry.ModBlocks.COMPUTER_COMMAND );
+ computerDrop( add, Registry.ModBlocks.TURTLE_NORMAL );
+ computerDrop( add, Registry.ModBlocks.TURTLE_ADVANCED );
+
+ add.accept( Registry.ModBlocks.CABLE.getLootTable(), LootTable
+ .lootTable()
+ .setParamSet( LootContextParamSets.BLOCK )
+ .withPool( LootPool.lootPool()
+ .setRolls( ConstantValue.exactly( 1 ) )
+ .add( LootItem.lootTableItem( Registry.ModItems.CABLE ) )
+ .when( ExplosionCondition.survivesExplosion() )
+ .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties( Registry.ModBlocks.CABLE )
+ .setProperties( StatePropertiesPredicate.Builder.properties().hasProperty( BlockCable.CABLE, true ) )
+ )
+ )
+ .withPool( LootPool.lootPool()
+ .setRolls( ConstantValue.exactly( 1 ) )
+ .add( LootItem.lootTableItem( Registry.ModItems.WIRED_MODEM ) )
+ .when( ExplosionCondition.survivesExplosion() )
+ .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties( Registry.ModBlocks.CABLE )
+ .setProperties( StatePropertiesPredicate.Builder.properties().hasProperty( BlockCable.MODEM, CableModemVariant.None ) )
+ .invert()
+ )
+ )
+ .build() );
+
+ // TODO: LOOT_TREASURE_DISK
+ /*add.accept( CommonHooks.LOOT_TREASURE_DISK, LootTable
+ .lootTable()
+ .setParamSet( LootContextParamSets.ALL_PARAMS )
+ .build() );*/
+ }
+
+ private static void basicDrop( BiConsumer add, Block block )
+ {
+ add.accept( block.getLootTable(), LootTable
+ .lootTable()
+ .setParamSet( LootContextParamSets.BLOCK )
+ .withPool( LootPool.lootPool()
+ .setRolls( ConstantValue.exactly( 1 ) )
+ .add( LootItem.lootTableItem( block ) )
+ .when( ExplosionCondition.survivesExplosion() )
+ ).build() );
+ }
+
+ private static void computerDrop( BiConsumer add, Block block )
+ {
+ add.accept( block.getLootTable(), LootTable
+ .lootTable()
+ .setParamSet( LootContextParamSets.BLOCK )
+ .withPool( LootPool.lootPool()
+ .setRolls( ConstantValue.exactly( 1 ) )
+ .add( DynamicLoot.dynamicEntry( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ) ) )
+ .when( AlternativeLootItemCondition.alternative(
+ BlockNamedEntityLootCondition.BUILDER,
+ HasComputerIdLootCondition.BUILDER,
+ PlayerCreativeLootCondition.BUILDER.invert()
+ ) )
+ ).build() );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/LootTableProvider.java b/src/main/java/dan200/computercraft/data/LootTableProvider.java
new file mode 100644
index 000000000..0d28f77b8
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/LootTableProvider.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import com.google.common.collect.Multimap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import dan200.computercraft.ComputerCraft;
+import net.minecraft.data.DataGenerator;
+import net.minecraft.data.DataProvider;
+import net.minecraft.data.HashCache;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.level.storage.loot.LootTable;
+import net.minecraft.world.level.storage.loot.LootTables;
+import net.minecraft.world.level.storage.loot.ValidationContext;
+import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+/**
+ * An alternative to {@link net.minecraft.data.loot.LootTableProvider}, with a more flexible interface.
+ */
+abstract class LootTableProvider implements DataProvider
+{
+ private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
+
+ private final DataGenerator generator;
+
+ LootTableProvider( DataGenerator generator )
+ {
+ this.generator = generator;
+ }
+
+ @Override
+ public void run( @Nonnull HashCache cache )
+ {
+ Map tables = new HashMap<>();
+ ValidationContext validation = new ValidationContext( LootContextParamSets.ALL_PARAMS, x -> null, tables::get );
+
+ registerLoot( ( id, table ) -> {
+ if( tables.containsKey( id ) ) validation.reportProblem( "Duplicate loot tables for " + id );
+ tables.put( id, table );
+ } );
+
+ tables.forEach( ( key, value ) -> LootTables.validate( validation, key, value ) );
+
+ Multimap problems = validation.getProblems();
+ if( !problems.isEmpty() )
+ {
+ problems.forEach( ( child, problem ) ->
+ ComputerCraft.log.warn( "Found validation problem in " + child + ": " + problem ) );
+ throw new IllegalStateException( "Failed to validate loot tables, see logs" );
+ }
+
+ tables.forEach( ( key, value ) -> {
+ Path path = getPath( key );
+ try
+ {
+ DataProvider.save( GSON, cache, LootTables.serialize( value ), path );
+ }
+ catch( IOException e )
+ {
+ ComputerCraft.log.error( "Couldn't save loot table {}", path, e );
+ }
+ } );
+ }
+
+ protected abstract void registerLoot( BiConsumer add );
+
+ @Nonnull
+ @Override
+ public String getName()
+ {
+ return "LootTables";
+ }
+
+ private Path getPath( ResourceLocation id )
+ {
+ return generator.getOutputFolder()
+ .resolve( "data" ).resolve( id.getNamespace() ).resolve( "loot_tables" )
+ .resolve( id.getPath() + ".json" );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/RecipeGenerator.java b/src/main/java/dan200/computercraft/data/RecipeGenerator.java
new file mode 100644
index 000000000..adba89d4e
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/RecipeGenerator.java
@@ -0,0 +1,395 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.shared.PocketUpgrades;
+import dan200.computercraft.shared.Registry;
+import dan200.computercraft.shared.TurtleUpgrades;
+import dan200.computercraft.shared.common.ColourableRecipe;
+import dan200.computercraft.shared.common.IColouredItem;
+import dan200.computercraft.shared.computer.core.ComputerFamily;
+import dan200.computercraft.shared.media.recipes.DiskRecipe;
+import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
+import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory;
+import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
+import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
+import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
+import dan200.computercraft.shared.util.Colour;
+import dan200.computercraft.shared.util.ImpostorRecipe;
+import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
+import net.fabricmc.fabric.api.tag.convention.v1.ConventionalItemTags;
+import net.minecraft.advancements.critereon.InventoryChangeTrigger;
+import net.minecraft.advancements.critereon.ItemPredicate;
+import net.minecraft.data.recipes.FinishedRecipe;
+import net.minecraft.data.recipes.ShapedRecipeBuilder;
+import net.minecraft.data.recipes.ShapelessRecipeBuilder;
+import net.minecraft.data.recipes.SpecialRecipeBuilder;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.TagKey;
+import net.minecraft.world.item.*;
+import net.minecraft.world.item.crafting.RecipeSerializer;
+import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
+import net.minecraft.world.level.ItemLike;
+import net.minecraft.world.level.block.Blocks;
+
+import javax.annotation.Nonnull;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
+import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
+
+class RecipeGenerator extends FabricRecipeProvider
+{
+ RecipeGenerator( FabricDataGenerator generator )
+ {
+ super( generator );
+ }
+
+ @Override
+ protected void generateRecipes( @Nonnull Consumer add )
+ {
+ basicRecipes( add );
+ diskColours( add );
+ pocketUpgrades( add );
+ turtleUpgrades( add );
+
+ addSpecial( add, PrintoutRecipe.SERIALIZER );
+ addSpecial( add, DiskRecipe.SERIALIZER );
+ addSpecial( add, ColourableRecipe.SERIALIZER );
+ addSpecial( add, TurtleUpgradeRecipe.SERIALIZER );
+ addSpecial( add, PocketComputerUpgradeRecipe.SERIALIZER );
+ }
+
+ /**
+ * Register a crafting recipe for a disk of every dye colour.
+ *
+ * @param add The callback to add recipes.
+ */
+ private void diskColours( @Nonnull Consumer add )
+ {
+ for( Colour colour : Colour.VALUES )
+ {
+ ShapelessRecipeBuilder
+ .shapeless( Registry.ModItems.DISK )
+ .requires( ConventionalItemTags.REDSTONE_DUSTS )
+ .requires( Items.PAPER )
+ .requires( DyeItem.byColor( ofColour( colour ) ) )
+ .group( "computercraft:disk" )
+ .unlockedBy( "has_drive", inventoryChange( Registry.ModBlocks.DISK_DRIVE ) )
+ .save( RecipeWrapper.wrap(
+ ImpostorShapelessRecipe.SERIALIZER, add,
+ x -> x.putInt( IColouredItem.NBT_COLOUR, colour.getHex() )
+ ), new ResourceLocation( ComputerCraft.MOD_ID, "disk_" + (colour.ordinal() + 1) ) );
+ }
+ }
+
+ /**
+ * Register a crafting recipe for each turtle upgrade.
+ *
+ * @param add The callback to add recipes.
+ */
+ private void turtleUpgrades( @Nonnull Consumer add )
+ {
+ for( ComputerFamily family : ComputerFamily.values() )
+ {
+ ItemStack base = TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null );
+ if( base.isEmpty() ) continue;
+
+ String nameId = family.name().toLowerCase( Locale.ROOT );
+
+ TurtleUpgrades.getVanillaUpgrades().forEach( upgrade -> {
+ ItemStack result = TurtleItemFactory.create( -1, null, -1, family, null, upgrade, -1, null );
+ ShapedRecipeBuilder
+ .shaped( result.getItem() )
+ .group( String.format( "%s:turtle_%s", ComputerCraft.MOD_ID, nameId ) )
+ .pattern( "#T" )
+ .define( 'T', base.getItem() )
+ .define( '#', upgrade.getCraftingItem().getItem() )
+ .unlockedBy( "has_items",
+ inventoryChange( base.getItem(), upgrade.getCraftingItem().getItem() ) )
+ .save(
+ RecipeWrapper.wrap( ImpostorRecipe.SERIALIZER, add, result.getTag() ),
+ new ResourceLocation( ComputerCraft.MOD_ID, String.format( "turtle_%s/%s/%s",
+ nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
+ ) )
+ );
+ } );
+ }
+ }
+
+ /**
+ * Register a crafting recipe for each pocket upgrade.
+ *
+ * @param add The callback to add recipes.
+ */
+ private void pocketUpgrades( @Nonnull Consumer add )
+ {
+ for( ComputerFamily family : ComputerFamily.values() )
+ {
+ ItemStack base = PocketComputerItemFactory.create( -1, null, -1, family, null );
+ if( base.isEmpty() ) continue;
+
+ String nameId = family.name().toLowerCase( Locale.ROOT );
+
+ PocketUpgrades.getVanillaUpgrades().forEach( upgrade -> {
+ ItemStack result = PocketComputerItemFactory.create( -1, null, -1, family, upgrade );
+ ShapedRecipeBuilder
+ .shaped( result.getItem() )
+ .group( String.format( "%s:pocket_%s", ComputerCraft.MOD_ID, nameId ) )
+ .pattern( "#" )
+ .pattern( "P" )
+ .define( 'P', base.getItem() )
+ .define( '#', upgrade.getCraftingItem().getItem() )
+ .unlockedBy( "has_items",
+ inventoryChange( base.getItem(), upgrade.getCraftingItem().getItem() ) )
+ .save(
+ RecipeWrapper.wrap( ImpostorRecipe.SERIALIZER, add, result.getTag() ),
+ new ResourceLocation( ComputerCraft.MOD_ID, String.format( "pocket_%s/%s/%s",
+ nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
+ ) )
+ );
+ } );
+ }
+ }
+
+ private void basicRecipes( @Nonnull Consumer add )
+ {
+ ShapedRecipeBuilder
+ .shaped( Registry.ModItems.CABLE, 6 )
+ .pattern( " # " )
+ .pattern( "#R#" )
+ .pattern( " # " )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .unlockedBy( "has_modem", inventoryChange( WIRED_MODEM ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.COMPUTER_NORMAL )
+ .pattern( "###" )
+ .pattern( "#R#" )
+ .pattern( "#G#" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_redstone", inventoryChange( ConventionalItemTags.REDSTONE_DUSTS ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.COMPUTER_ADVANCED )
+ .pattern( "###" )
+ .pattern( "#R#" )
+ .pattern( "#G#" )
+ .define( '#', ConventionalItemTags.GOLD_INGOTS )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_components", inventoryChange( Items.REDSTONE, Items.GOLD_INGOT ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.COMPUTER_COMMAND )
+ .pattern( "###" )
+ .pattern( "#R#" )
+ .pattern( "#G#" )
+ .define( '#', ConventionalItemTags.GOLD_INGOTS )
+ .define( 'R', Blocks.COMMAND_BLOCK )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_components", inventoryChange( Blocks.COMMAND_BLOCK ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.DISK_DRIVE )
+ .pattern( "###" )
+ .pattern( "#R#" )
+ .pattern( "#R#" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.MONITOR_NORMAL )
+ .pattern( "###" )
+ .pattern( "#G#" )
+ .pattern( "###" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.MONITOR_ADVANCED, 4 )
+ .pattern( "###" )
+ .pattern( "#G#" )
+ .pattern( "###" )
+ .define( '#', ConventionalItemTags.GOLD_INGOTS )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModItems.POCKET_COMPUTER_NORMAL )
+ .pattern( "###" )
+ .pattern( "#A#" )
+ .pattern( "#G#" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'A', Items.GOLDEN_APPLE )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .unlockedBy( "has_apple", inventoryChange( Items.GOLDEN_APPLE ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModItems.POCKET_COMPUTER_ADVANCED )
+ .pattern( "###" )
+ .pattern( "#A#" )
+ .pattern( "#G#" )
+ .define( '#', ConventionalItemTags.GOLD_INGOTS )
+ .define( 'A', Items.GOLDEN_APPLE )
+ .define( 'G', ConventionalItemTags.GLASS_PANES )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .unlockedBy( "has_apple", inventoryChange( Items.GOLDEN_APPLE ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.PRINTER )
+ .pattern( "###" )
+ .pattern( "#R#" )
+ .pattern( "#D#" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .define( 'D', ConventionalItemTags.DYES )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.SPEAKER )
+ .pattern( "###" )
+ .pattern( "#N#" )
+ .pattern( "#R#" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'N', Blocks.NOTE_BLOCK )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModItems.WIRED_MODEM )
+ .pattern( "###" )
+ .pattern( "#R#" )
+ .pattern( "###" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'R', ConventionalItemTags.REDSTONE_DUSTS )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .unlockedBy( "has_cable", inventoryChange( Registry.ModItems.CABLE ) )
+ .save( add );
+
+ ShapelessRecipeBuilder
+ .shapeless( Registry.ModBlocks.WIRED_MODEM_FULL )
+ .requires( Registry.ModItems.WIRED_MODEM )
+ .unlockedBy( "has_modem", inventoryChange( WIRED_MODEM ) )
+ .save( add, new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full_from" ) );
+ ShapelessRecipeBuilder
+ .shapeless( Registry.ModItems.WIRED_MODEM )
+ .requires( Registry.ModBlocks.WIRED_MODEM_FULL )
+ .unlockedBy( "has_modem", inventoryChange( WIRED_MODEM ) )
+ .save( add, new ResourceLocation( ComputerCraft.MOD_ID, "wired_modem_full_to" ) );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.WIRELESS_MODEM_NORMAL )
+ .pattern( "###" )
+ .pattern( "#E#" )
+ .pattern( "###" )
+ .define( '#', ExtraConventionalItemTags.STONES )
+ .define( 'E', ExtraConventionalItemTags.ENDER_PEARLS )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .save( add );
+
+ ShapedRecipeBuilder
+ .shaped( Registry.ModBlocks.WIRELESS_MODEM_ADVANCED )
+ .pattern( "###" )
+ .pattern( "#E#" )
+ .pattern( "###" )
+ .define( '#', ConventionalItemTags.GOLD_INGOTS )
+ .define( 'E', Items.ENDER_EYE )
+ .unlockedBy( "has_computer", inventoryChange( COMPUTER ) )
+ .unlockedBy( "has_wireless", inventoryChange( Registry.ModBlocks.WIRELESS_MODEM_NORMAL ) )
+ .save( add );
+
+ ShapelessRecipeBuilder
+ .shapeless( Items.PLAYER_HEAD )
+ .requires( ExtraConventionalItemTags.SKULLS )
+ .requires( Registry.ModItems.MONITOR_NORMAL )
+ .unlockedBy( "has_monitor", inventoryChange( Registry.ModItems.MONITOR_NORMAL ) )
+ .save(
+ RecipeWrapper.wrap( RecipeSerializer.SHAPELESS_RECIPE, add, playerHead( "Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c" ) ),
+ new ResourceLocation( ComputerCraft.MOD_ID, "skull_cloudy" )
+ );
+
+ ShapelessRecipeBuilder
+ .shapeless( Items.PLAYER_HEAD )
+ .requires( ExtraConventionalItemTags.SKULLS )
+ .requires( Registry.ModItems.COMPUTER_NORMAL )
+ .unlockedBy( "has_computer", inventoryChange( Registry.ModItems.COMPUTER_NORMAL ) )
+ .save(
+ RecipeWrapper.wrap( RecipeSerializer.SHAPELESS_RECIPE, add, playerHead( "dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb" ) ),
+ new ResourceLocation( ComputerCraft.MOD_ID, "skull_dan200" )
+ );
+
+ ShapelessRecipeBuilder
+ .shapeless( Registry.ModItems.PRINTED_PAGES )
+ .requires( Registry.ModItems.PRINTED_PAGE, 2 )
+ .requires( Items.STRING )
+ .unlockedBy( "has_printer", inventoryChange( Registry.ModBlocks.PRINTER ) )
+ .save( RecipeWrapper.wrap( ImpostorShapelessRecipe.SERIALIZER, add ) );
+
+ ShapelessRecipeBuilder
+ .shapeless( Registry.ModItems.PRINTED_BOOK )
+ .requires( Items.LEATHER )
+ .requires( Registry.ModItems.PRINTED_PAGE, 1 )
+ .requires( Items.STRING )
+ .unlockedBy( "has_printer", inventoryChange( Registry.ModBlocks.PRINTER ) )
+ .save( RecipeWrapper.wrap( ImpostorShapelessRecipe.SERIALIZER, add ) );
+ }
+
+ private static DyeColor ofColour( Colour colour )
+ {
+ return DyeColor.byId( 15 - colour.ordinal() );
+ }
+
+ private static InventoryChangeTrigger.TriggerInstance inventoryChange( TagKey- stack )
+ {
+ return InventoryChangeTrigger.TriggerInstance.hasItems( ItemPredicate.Builder.item().of( stack ).build() );
+ }
+
+ private static InventoryChangeTrigger.TriggerInstance inventoryChange( ItemLike... stack )
+ {
+ return InventoryChangeTrigger.TriggerInstance.hasItems( stack );
+ }
+
+ private static CompoundTag playerHead( String name, String uuid )
+ {
+ CompoundTag owner = new CompoundTag();
+ owner.putString( "Name", name );
+ owner.putString( "Id", uuid );
+
+ CompoundTag tag = new CompoundTag();
+ tag.put( "SkullOwner", owner );
+ return tag;
+ }
+
+ private static void addSpecial( Consumer
add, SimpleRecipeSerializer> special )
+ {
+ var key = net.minecraft.core.Registry.RECIPE_SERIALIZER.getKey( special );
+ SpecialRecipeBuilder.special( special ).save( add, key.toString() );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/data/RecipeWrapper.java b/src/main/java/dan200/computercraft/data/RecipeWrapper.java
new file mode 100644
index 000000000..bc1b166d8
--- /dev/null
+++ b/src/main/java/dan200/computercraft/data/RecipeWrapper.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.data;
+
+import com.google.gson.JsonObject;
+import net.minecraft.data.recipes.FinishedRecipe;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.util.GsonHelper;
+import net.minecraft.world.item.crafting.RecipeSerializer;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.function.Consumer;
+
+/**
+ * Adapter for recipes which overrides the serializer and adds custom item NBT.
+ */
+final class RecipeWrapper implements FinishedRecipe
+{
+ private final FinishedRecipe recipe;
+ private final CompoundTag resultData;
+ private final RecipeSerializer> serializer;
+
+ private RecipeWrapper( FinishedRecipe recipe, CompoundTag resultData, RecipeSerializer> serializer )
+ {
+ this.resultData = resultData;
+ this.recipe = recipe;
+ this.serializer = serializer;
+ }
+
+ public static Consumer wrap( RecipeSerializer> serializer, Consumer original )
+ {
+ return x -> original.accept( new RecipeWrapper( x, null, serializer ) );
+ }
+
+ public static Consumer wrap( RecipeSerializer> serializer, Consumer original, CompoundTag resultData )
+ {
+ return x -> original.accept( new RecipeWrapper( x, resultData, serializer ) );
+ }
+
+ public static Consumer wrap( RecipeSerializer> serializer, Consumer original, Consumer resultData )
+ {
+ CompoundTag tag = new CompoundTag();
+ resultData.accept( tag );
+ return x -> original.accept( new RecipeWrapper( x, tag, serializer ) );
+ }
+
+ @Override
+ public void serializeRecipeData( @Nonnull JsonObject jsonObject )
+ {
+ recipe.serializeRecipeData( jsonObject );
+
+ if( resultData != null )
+ {
+ JsonObject object = GsonHelper.getAsJsonObject( jsonObject, "result" );
+ object.addProperty( "nbt", resultData.toString() );
+ }
+ }
+
+ @Nonnull
+ @Override
+ public ResourceLocation getId()
+ {
+ return recipe.getId();
+ }
+
+ @Nonnull
+ @Override
+ public RecipeSerializer> getType()
+ {
+ return serializer;
+ }
+
+ @Nullable
+ @Override
+ public JsonObject serializeAdvancement()
+ {
+ return recipe.serializeAdvancement();
+ }
+
+ @Nullable
+ @Override
+ public ResourceLocation getAdvancementId()
+ {
+ return recipe.getAdvancementId();
+ }
+}
diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinChunkMap.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinChunkMap.java
index dfa3fde3f..a69f0550b 100644
--- a/src/main/java/dan200/computercraft/fabric/mixin/MixinChunkMap.java
+++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinChunkMap.java
@@ -27,11 +27,11 @@ public class MixinChunkMap
ServerLevel level;
/*
- * This mixin mimics the logic of Forge's ChunkWatchEvent.Watch but I don't believe it behaves as expected. Instead
- * of firing once when the player initially come in server view distance of a chunk, it fires every time
- * the chunk is checked against the player's view distance. This continually happens every tick that the player
- * moves (or even rotates in place).
- */
+ * This mixin mimics the logic of Forge's ChunkWatchEvent.Watch but I don't believe it behaves as expected. Instead
+ * of firing once when the player initially come in server view distance of a chunk, it fires every time
+ * the chunk is checked against the player's view distance. This continually happens every tick that the player
+ * moves (or even rotates in place).
+ */
// @Inject( method = "updateChunkTracking", at = @At( value = "HEAD" ) )
// public void updateChunkTracking( ServerPlayer serverPlayer, ChunkPos chunkPos, MutableObject mutableObject, boolean bl, boolean bl2, CallbackInfo ci )
diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinLanguage.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinLanguage.java
index 9296a91c7..274b7d845 100644
--- a/src/main/java/dan200/computercraft/fabric/mixin/MixinLanguage.java
+++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinLanguage.java
@@ -5,16 +5,16 @@
*/
package dan200.computercraft.fabric.mixin;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonParseException;
import dan200.computercraft.fabric.util.ServerTranslationEntry;
-import net.minecraft.util.StringDecomposer;
-import net.minecraft.util.FormattedCharSequence;
-import net.minecraft.network.chat.FormattedText;
-import net.minecraft.network.chat.Style;
-import net.minecraft.locale.Language;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
-import com.google.common.collect.ImmutableMap;
-import com.google.gson.JsonParseException;
+import net.minecraft.locale.Language;
+import net.minecraft.network.chat.FormattedText;
+import net.minecraft.network.chat.Style;
+import net.minecraft.util.FormattedCharSequence;
+import net.minecraft.util.StringDecomposer;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final;
@@ -28,14 +28,7 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@@ -60,14 +53,14 @@ private static void loadFromJson( InputStream inputStream, BiConsumer biConsumer )
{
- Path path = modContainer.getPath( "assets/" + modContainer.getMetadata().getId() + "/lang/" + DEFAULT + ".json" );
- if ( !Files.exists( path ) ) return;
+ Optional path = modContainer.findPath( "assets/" + modContainer.getMetadata().getId() + "/lang/" + DEFAULT + ".json" );
+ if( path.isEmpty() ) return;
- try ( InputStream inputStream = Files.newInputStream( path ) )
+ try( InputStream inputStream = Files.newInputStream( path.get() ) )
{
loadFromJson( inputStream, biConsumer );
}
- catch ( JsonParseException | IOException e )
+ catch( JsonParseException | IOException e )
{
LOGGER.error( "Couldn't read strings from " + path, e );
}
@@ -78,19 +71,19 @@ private static void loadDefault( CallbackInfoReturnable cir )
{
Map> translations = new HashMap<>();
- for ( ModContainer mod : FabricLoader.getInstance().getAllMods() )
+ for( ModContainer mod : FabricLoader.getInstance().getAllMods() )
{
loadModLangFile( mod, ( k, v ) -> {
- if ( !translations.containsKey( k ) ) translations.put( k, new ArrayList<>() );
+ if( !translations.containsKey( k ) ) translations.put( k, new ArrayList<>() );
translations.get( k ).add( new ServerTranslationEntry( mod.getMetadata(), k, v ) );
} );
}
ImmutableMap.Builder builder = ImmutableMap.builder();
- for ( Map.Entry> keyEntry : translations.entrySet() )
+ for( Map.Entry> keyEntry : translations.entrySet() )
{
- if ( keyEntry.getValue().size() == 1 )
+ if( keyEntry.getValue().size() == 1 )
{
// Only one value provided for this key
builder.put( keyEntry.getKey(), keyEntry.getValue().get( 0 ).value() );
diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinSoundEngine.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinSoundEngine.java
index 6add67f91..1c627afc4 100644
--- a/src/main/java/dan200/computercraft/fabric/mixin/MixinSoundEngine.java
+++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinSoundEngine.java
@@ -21,11 +21,13 @@ public class MixinSoundEngine
{
// Used to capture the SoundInstance argument passed to SoundEngine#play and the SoundEngine instance.
// Not a thread-safe way to do it but this code is only called from the render thread as far as I can tell.
- @Unique private static SoundInstance soundInstanceCapture;
- @Unique private static SoundEngine thisCapture;
+ @Unique
+ private static SoundInstance soundInstanceCapture;
+ @Unique
+ private static SoundEngine thisCapture;
@Inject(
- method = "lambda$play$8(Lnet/minecraft/client/sounds/AudioStream;Lcom/mojang/blaze3d/audio/Channel;)V",
+ method = "method_19755(Lnet/minecraft/client/sounds/AudioStream;Lcom/mojang/blaze3d/audio/Channel;)V",
at = @At( "HEAD" ),
cancellable = true
)
diff --git a/src/main/java/dan200/computercraft/fabric/util/ServerTranslationEntry.java b/src/main/java/dan200/computercraft/fabric/util/ServerTranslationEntry.java
index 0b55baae3..3d6dc759c 100644
--- a/src/main/java/dan200/computercraft/fabric/util/ServerTranslationEntry.java
+++ b/src/main/java/dan200/computercraft/fabric/util/ServerTranslationEntry.java
@@ -12,7 +12,7 @@
import java.util.stream.Collectors;
// A utility class for holding translation mappings prior collision resolution.
-public record ServerTranslationEntry(ModMetadata providingModMetadata, String key, String value )
+public record ServerTranslationEntry(ModMetadata providingModMetadata, String key, String value)
{
public String getModId()
{
@@ -21,9 +21,9 @@ public String getModId()
public Set getDependencyIds()
{
- Set deps = providingModMetadata.getDependencies().stream().map( ModDependency::getModId ) .collect( Collectors.toSet() );
+ Set deps = providingModMetadata.getDependencies().stream().map( ModDependency::getModId ).collect( Collectors.toSet() );
// For the purposes of handling key collisions, all mods should depend on minecraft
- if ( !getModId().equals( "minecraft" ) ) deps.add( "minecraft" );
+ if( !getModId().equals( "minecraft" ) ) deps.add( "minecraft" );
return deps;
}
diff --git a/src/main/java/dan200/computercraft/shared/Registry.java b/src/main/java/dan200/computercraft/shared/Registry.java
index 509902dbc..5aeaa9e3c 100644
--- a/src/main/java/dan200/computercraft/shared/Registry.java
+++ b/src/main/java/dan200/computercraft/shared/Registry.java
@@ -42,20 +42,17 @@
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
-import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
import dan200.computercraft.shared.turtle.upgrades.TurtleTool;
+import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
-import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
import net.minecraft.core.BlockPos;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.*;
@@ -83,7 +80,6 @@ public static void init()
ModBlockEntities.CABLE,
ModBlocks.CABLE,
ModItems.CABLE,
- ModEntities.TURTLE_PLAYER,
ModContainers.COMPUTER,
};
@@ -114,7 +110,7 @@ public static T register( String id, T value )
register( "computer_advanced", new BlockComputer<>( properties(), ComputerFamily.ADVANCED, () -> ModBlockEntities.COMPUTER_ADVANCED ) );
public static final BlockComputer COMPUTER_COMMAND =
- register( "computer_command", new BlockComputer<>( properties().strength( -1, 6000000.0F ).requiresCorrectToolForDrops().noDrops(), ComputerFamily.COMMAND, () -> ModBlockEntities.COMPUTER_COMMAND ) );
+ register( "computer_command", new BlockComputer<>( properties().strength( -1, 6000000.0F ), ComputerFamily.COMMAND, () -> ModBlockEntities.COMPUTER_COMMAND ) );
public static final BlockTurtle TURTLE_NORMAL =
register( "turtle_normal", new BlockTurtle( turtleProperties(), ComputerFamily.NORMAL, () -> ModBlockEntities.TURTLE_NORMAL ) );
@@ -212,7 +208,10 @@ private static BlockEntityType ofBlock( Block block,
public static final class ModItems
{
- private static final CreativeModeTab mainItemGroup = ComputerCraft.MAIN_GROUP;
+ private static final CreativeModeTab mainItemGroup = FabricItemGroupBuilder.build(
+ new ResourceLocation( MOD_ID, "main" ),
+ () -> new ItemStack( ModBlocks.COMPUTER_NORMAL )
+ ).setRecipeFolderName( MOD_ID );
public static final ItemComputer COMPUTER_NORMAL =
ofBlock( ModBlocks.COMPUTER_NORMAL, ItemComputer::new );
@@ -297,13 +296,6 @@ private static T register( String id, T item )
}
}
- public static class ModEntities
- {
- public static final EntityType TURTLE_PLAYER =
- net.minecraft.core.Registry.register( net.minecraft.core.Registry.ENTITY_TYPE, new ResourceLocation( MOD_ID, "turtle_player" ),
- EntityType.Builder.createNothing( MobCategory.MISC ).noSave().noSummon().sized( 0, 0 ).build( ComputerCraft.MOD_ID + ":turtle_player" ) );
- }
-
public static class ModContainers
{
public static final MenuType COMPUTER =
@@ -330,9 +322,9 @@ public static class ModContainers
public static final MenuType VIEW_COMPUTER =
ContainerData.toType( new ResourceLocation( MOD_ID, "view_computer" ), ViewComputerContainerData::new, ContainerViewComputer::new );
- private static MenuType registerSimple( String id, ScreenHandlerRegistry.SimpleClientHandlerFactory function )
+ private static MenuType registerSimple( String id, MenuType.MenuSupplier function )
{
- return ScreenHandlerRegistry.registerSimple( new ResourceLocation( MOD_ID, id ), function );
+ return net.minecraft.core.Registry.register( net.minecraft.core.Registry.MENU, new ResourceLocation( MOD_ID, id ), new MenuType( function ) );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java b/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java
index f271fbd88..792738da9 100644
--- a/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java
+++ b/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java
@@ -104,6 +104,7 @@ public static Stream getVanillaUpgrades()
// Vanilla Minecraft upgrades
Registry.TurtleUpgrades.diamondPickaxe,
+ Registry.TurtleUpgrades.netheritePickaxe,
Registry.TurtleUpgrades.diamondAxe,
Registry.TurtleUpgrades.diamondSword,
Registry.TurtleUpgrades.diamondShovel,
diff --git a/src/main/java/dan200/computercraft/shared/command/CommandUtils.java b/src/main/java/dan200/computercraft/shared/command/CommandUtils.java
index f7f5a90a6..7d4415a83 100644
--- a/src/main/java/dan200/computercraft/shared/command/CommandUtils.java
+++ b/src/main/java/dan200/computercraft/shared/command/CommandUtils.java
@@ -32,7 +32,7 @@ public static boolean isPlayer( CommandSourceStack output )
}
@SuppressWarnings( "unchecked" )
- public static CompletableFuture suggestOnServer( CommandContext> context, SuggestionsBuilder builder, Function, CompletableFuture> supplier )
+ public static CompletableFuture suggestOnServer( CommandContext> context, Function, CompletableFuture> supplier )
{
Object source = context.getSource();
if( !(source instanceof SharedSuggestionProvider) )
diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java
index 50f895847..c756c77e3 100644
--- a/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java
+++ b/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java
@@ -121,7 +121,7 @@ public CompletableFuture listSuggestions( CommandContext con
}
// Verify we've a command source and we're running on the server
- return suggestOnServer( context, builder, s -> {
+ return suggestOnServer( context, s -> {
if( remaining.startsWith( "@" ) )
{
suggestComputers( builder, remaining, x -> {
diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java
index d193f2e6b..09825a162 100644
--- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java
+++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java
@@ -99,7 +99,7 @@ public CommandSourceStack getSource()
}
return new CommandSourceStack( receiver,
- new Vec3( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vec2.ZERO,
+ Vec3.atCenterOf( worldPosition ), Vec2.ZERO,
(ServerLevel) getLevel(), 2,
name, new TextComponent( name ),
getLevel().getServer(), null
diff --git a/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java b/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java
index 8c227fbde..1f6526644 100644
--- a/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java
+++ b/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java
@@ -9,6 +9,7 @@
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.ColourTracker;
import dan200.computercraft.shared.util.ColourUtils;
+import net.fabricmc.fabric.api.tag.convention.v1.ConventionalItemTags;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.DyeColor;
@@ -25,7 +26,7 @@
public class DiskRecipe extends CustomRecipe
{
private final Ingredient paper = Ingredient.of( Items.PAPER );
- private final Ingredient redstone = Ingredient.of( Items.REDSTONE );
+ private final Ingredient redstone = Ingredient.of( ConventionalItemTags.REDSTONE_DUSTS );
public DiskRecipe( ResourceLocation id )
{
diff --git a/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java
index 04c9163d7..569477d2a 100644
--- a/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java
+++ b/src/main/java/dan200/computercraft/shared/network/client/SpeakerAudioClientMessage.java
@@ -7,11 +7,11 @@
import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.network.NetworkMessage;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import dan200.computercraft.shared.network.PacketContext;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.FriendlyByteBuf;
-import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
@@ -27,14 +27,14 @@
public class SpeakerAudioClientMessage implements NetworkMessage
{
private final UUID source;
- private final Vec3 pos;
+ private final SpeakerPosition.Message pos;
private final ByteBuffer content;
private final float volume;
- public SpeakerAudioClientMessage( UUID source, Vec3 pos, float volume, ByteBuffer content )
+ public SpeakerAudioClientMessage( UUID source, SpeakerPosition pos, float volume, ByteBuffer content )
{
this.source = source;
- this.pos = pos;
+ this.pos = pos.asMessage();
this.content = content;
this.volume = volume;
}
@@ -42,7 +42,7 @@ public SpeakerAudioClientMessage( UUID source, Vec3 pos, float volume, ByteBuffe
public SpeakerAudioClientMessage( FriendlyByteBuf buf )
{
source = buf.readUUID();
- pos = new Vec3( buf.readDouble(), buf.readDouble(), buf.readDouble() );
+ pos = SpeakerPosition.Message.read( buf );
volume = buf.readFloat();
SpeakerManager.getSound( source ).pushAudio( buf );
@@ -53,9 +53,7 @@ public SpeakerAudioClientMessage( FriendlyByteBuf buf )
public void toBytes( @Nonnull FriendlyByteBuf buf )
{
buf.writeUUID( source );
- buf.writeDouble( pos.x() );
- buf.writeDouble( pos.y() );
- buf.writeDouble( pos.z() );
+ pos.write( buf );
buf.writeFloat( volume );
buf.writeBytes( content.duplicate() );
}
@@ -64,6 +62,6 @@ public void toBytes( @Nonnull FriendlyByteBuf buf )
@Environment( EnvType.CLIENT )
public void handle( PacketContext context )
{
- SpeakerManager.getSound( source ).playAudio( pos, volume );
+ SpeakerManager.getSound( source ).playAudio( pos.reify(), volume );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.java
index 95f705fe4..bd98ab097 100644
--- a/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.java
+++ b/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.java
@@ -7,11 +7,11 @@
import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.network.NetworkMessage;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import dan200.computercraft.shared.network.PacketContext;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.FriendlyByteBuf;
-import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import java.util.UUID;
@@ -26,33 +26,31 @@
public class SpeakerMoveClientMessage implements NetworkMessage
{
private final UUID source;
- private final Vec3 pos;
+ private final SpeakerPosition.Message pos;
- public SpeakerMoveClientMessage( UUID source, Vec3 pos )
+ public SpeakerMoveClientMessage( UUID source, SpeakerPosition pos )
{
this.source = source;
- this.pos = pos;
+ this.pos = pos.asMessage();
}
public SpeakerMoveClientMessage( FriendlyByteBuf buf )
{
source = buf.readUUID();
- pos = new Vec3( buf.readDouble(), buf.readDouble(), buf.readDouble() );
+ pos = SpeakerPosition.Message.read( buf );
}
@Override
public void toBytes( @Nonnull FriendlyByteBuf buf )
{
buf.writeUUID( source );
- buf.writeDouble( pos.x() );
- buf.writeDouble( pos.y() );
- buf.writeDouble( pos.z() );
+ pos.write( buf );
}
@Override
@Environment( EnvType.CLIENT )
public void handle( PacketContext context )
{
- SpeakerManager.moveSound( source, pos );
+ SpeakerManager.moveSound( source, pos.reify() );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java
index e759d4ec1..71ea44485 100644
--- a/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java
+++ b/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java
@@ -10,9 +10,9 @@
import dan200.computercraft.shared.network.PacketContext;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
import java.util.UUID;
@@ -27,15 +27,15 @@
public class SpeakerPlayClientMessage implements NetworkMessage
{
private final UUID source;
- private final Vec3 pos;
+ private final SpeakerPosition.Message pos;
private final ResourceLocation sound;
private final float volume;
private final float pitch;
- public SpeakerPlayClientMessage( UUID source, Vec3 pos, ResourceLocation event, float volume, float pitch )
+ public SpeakerPlayClientMessage( UUID source, SpeakerPosition pos, ResourceLocation event, float volume, float pitch )
{
this.source = source;
- this.pos = pos;
+ this.pos = pos.asMessage();
sound = event;
this.volume = volume;
this.pitch = pitch;
@@ -44,7 +44,7 @@ public SpeakerPlayClientMessage( UUID source, Vec3 pos, ResourceLocation event,
public SpeakerPlayClientMessage( FriendlyByteBuf buf )
{
source = buf.readUUID();
- pos = new Vec3( buf.readDouble(), buf.readDouble(), buf.readDouble() );
+ pos = SpeakerPosition.Message.read( buf );
sound = buf.readResourceLocation();
volume = buf.readFloat();
pitch = buf.readFloat();
@@ -54,9 +54,7 @@ public SpeakerPlayClientMessage( FriendlyByteBuf buf )
public void toBytes( @Nonnull FriendlyByteBuf buf )
{
buf.writeUUID( source );
- buf.writeDouble( pos.x() );
- buf.writeDouble( pos.y() );
- buf.writeDouble( pos.z() );
+ pos.write( buf );
buf.writeResourceLocation( sound );
buf.writeFloat( volume );
buf.writeFloat( pitch );
@@ -66,6 +64,6 @@ public void toBytes( @Nonnull FriendlyByteBuf buf )
@Environment( EnvType.CLIENT )
public void handle( PacketContext context )
{
- SpeakerManager.getSound( source ).playSound( pos, sound, volume, pitch );
+ SpeakerManager.getSound( source ).playSound( pos.reify(), sound, volume, pitch );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java
index 82dddab27..a529cc89c 100644
--- a/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java
+++ b/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java
@@ -5,7 +5,8 @@
*/
package dan200.computercraft.shared.network.container;
-import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
+import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
+import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.MenuProvider;
@@ -40,21 +41,21 @@ interface FixedFactory
C create( MenuType type, int id, @Nonnull Inventory inventory, T data );
}
- static MenuType toType( ResourceLocation identifier, Function reader,
- Factory factory )
+ static MenuType toType(
+ ResourceLocation identifier, Function reader, Factory factory
+ )
{
- return ScreenHandlerRegistry.registerExtended( identifier,
- ( id, playerInventory, packetByteBuf ) -> factory.create( id,
- playerInventory,
- reader.apply( packetByteBuf ) ) );
+ return Registry.register( Registry.MENU, identifier, new ExtendedScreenHandlerType<>( ( id, playerInventory, packetByteBuf ) ->
+ factory.create( id, playerInventory, reader.apply( packetByteBuf ) )
+ ) );
}
- static MenuType toType( ResourceLocation identifier, MenuType type, Function reader,
- FixedFactory factory )
+ static MenuType toType(
+ ResourceLocation identifier, MenuType type, Function reader, FixedFactory factory
+ )
{
- return ScreenHandlerRegistry.registerExtended( identifier,
- ( id, playerInventory, packetByteBuf ) -> factory.create( type, id,
- playerInventory,
- reader.apply( packetByteBuf ) ) );
+ return Registry.register( Registry.MENU, identifier, new ExtendedScreenHandlerType<>( ( id, playerInventory, packetByteBuf ) ->
+ factory.create( type, id, playerInventory, reader.apply( packetByteBuf ) )
+ ) );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java b/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java
index 04edcd2f8..27e557760 100644
--- a/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java
+++ b/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java
@@ -160,6 +160,7 @@ public static void send( int instanceId, List files )
contents.position( currentOffset ).limit( currentOffset + canWrite );
slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) );
currentOffset += canWrite;
+ remaining -= canWrite;
}
contents.position( 0 ).limit( capacity );
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java
index 54a57e494..8a630f9f2 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java
@@ -12,9 +12,9 @@
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
import dan200.computercraft.api.peripheral.IPeripheral;
+import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BlockEntity;
-import net.minecraft.core.Registry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java
index 63f2bdf88..ee8389c58 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java
@@ -26,6 +26,7 @@ public static > T fill( @Nonnull T data, @
stateTable.put( property.getName(), getPropertyValue( property, entry.getValue() ) );
}
data.put( "state", stateTable );
+ data.put( "tags", DataHelpers.getTags( state.getTags() ) );
return data;
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java
index 2c280fce6..bb0a51c59 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java
@@ -5,44 +5,34 @@
*/
package dan200.computercraft.shared.peripheral.generic.data;
+import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.level.block.Block;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
public final class DataHelpers
{
private DataHelpers()
{}
- @Nonnull
- public static Map getTags( @Nonnull Collection tags )
- {
- Map result = new HashMap<>( tags.size() );
- for( ResourceLocation location : tags ) result.put( location.toString(), true );
- return result;
- }
-
- @Nonnull
- static Map getTags( @Nonnull Block block )
+ public static Map getTags( Holder.Reference object )
{
- Collection tags = block.builtInRegistryHolder().tags().map( tag -> tag.location() ).collect( Collectors.toList() );
- return getTags( tags );
+ return getTags( object.tags() );
}
@Nonnull
- static Map getTags( @Nonnull Item item )
+ public static Map getTags( @Nonnull Stream> tags )
{
- Collection tags = item.builtInRegistryHolder().tags().map( tag -> tag.location() ).collect( Collectors.toList() );
- return getTags( tags );
+ return tags.collect( Collectors.toMap( x -> x.location().toString(), x -> true ) );
}
@Nullable
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java
index 604521165..85ae8fade 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java
@@ -65,7 +65,7 @@ public static > T fill( @Nonnull T data, @
data.put( "durability", stack.getItem().getBarWidth( stack ) / 13.0 );
}
- data.put( "tags", DataHelpers.getTags( stack.getItem() ) );
+ data.put( "tags", DataHelpers.getTags( stack.getTags() ) );
CompoundTag tag = stack.getTag();
if( tag != null && tag.contains( "display", Tag.TAG_COMPOUND ) )
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
index 9ea45aba5..295e3776d 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
@@ -166,7 +166,8 @@ public static int size( Container inventory )
@LuaFunction( mainThread = true )
public static int getItemLimit( Container inventory, int slot ) throws LuaException
{
- assertBetween( slot, 1, inventory.getContainerSize(), "Slot out of range (%s)" );
+ ItemStorage itemStorage = extractHandler( inventory );
+ assertBetween( slot, 1, itemStorage.size(), "Slot out of range (%s)" );
return inventory.getMaxStackSize();
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
index 11d6e88a3..068e56bd2 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
@@ -54,8 +54,7 @@ public Level getLevel()
@Override
public Vec3 getPosition()
{
- BlockPos pos = getBlockPos();
- return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
+ return Vec3.atCenterOf( getBlockPos() );
}
@Override
@@ -97,8 +96,7 @@ protected WiredModemLocalPeripheral getLocalPeripheral()
@Override
public Vec3 getPosition()
{
- BlockPos pos = getBlockPos().relative( getDirection() );
- return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
+ return Vec3.atCenterOf( getBlockPos().relative( getDirection() ) );
}
@Nonnull
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
index 8c157a17a..b5dea9367 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
@@ -81,8 +81,7 @@ public Level getLevel()
@Override
public Vec3 getPosition()
{
- BlockPos pos = entity.getBlockPos();
- return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
+ return Vec3.atCenterOf( entity.getBlockPos() );
}
}
@@ -388,8 +387,7 @@ protected WiredModemLocalPeripheral getLocalPeripheral()
@Override
public Vec3 getPosition()
{
- BlockPos pos = getBlockPos().relative( side );
- return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
+ return Vec3.atCenterOf( getBlockPos().relative( side ) );
}
@Nonnull
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java
index eeabbf315..6db6a44f6 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java
@@ -44,8 +44,7 @@ public Level getLevel()
@Override
public Vec3 getPosition()
{
- BlockPos pos = entity.getBlockPos().relative( entity.getDirection() );
- return new Vec3( pos.getX(), pos.getY(), pos.getZ() );
+ return Vec3.atLowerCornerOf( entity.getBlockPos().relative( entity.getDirection() ) );
}
@Override
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java
index 456bc56cc..8dcf92c12 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java
@@ -6,8 +6,8 @@
package dan200.computercraft.shared.peripheral.monitor;
import com.mojang.blaze3d.platform.GlStateManager;
-import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.VertexBuffer;
+import dan200.computercraft.client.util.DirectBuffers;
+import dan200.computercraft.client.util.DirectVertexBuffer;
import dan200.computercraft.shared.common.ClientTerminal;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@@ -32,7 +32,8 @@ public final class ClientMonitor extends ClientTerminal
public int tboBuffer;
public int tboTexture;
- public VertexBuffer buffer;
+ public int tboUniform;
+ public DirectVertexBuffer buffer;
public ClientMonitor( boolean colour, TileMonitor origin )
{
@@ -63,15 +64,15 @@ public boolean createBuffer( MonitorRenderer renderer )
deleteBuffers();
- tboBuffer = GlStateManager._glGenBuffers();
- GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, tboBuffer );
- GL15.glBufferData( GL31.GL_TEXTURE_BUFFER, 0, GL15.GL_STATIC_DRAW );
+ tboBuffer = DirectBuffers.createBuffer();
+ DirectBuffers.setEmptyBufferData( GL31.GL_TEXTURE_BUFFER, tboBuffer, GL15.GL_STATIC_DRAW );
tboTexture = GlStateManager._genTexture();
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, tboTexture );
GL31.glTexBuffer( GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer );
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, 0 );
- GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 );
+ tboUniform = DirectBuffers.createBuffer();
+ DirectBuffers.setEmptyBufferData( GL31.GL_UNIFORM_BUFFER, tboUniform, GL15.GL_STATIC_DRAW );
addMonitor();
return true;
@@ -81,7 +82,7 @@ public boolean createBuffer( MonitorRenderer renderer )
if( buffer != null ) return false;
deleteBuffers();
- buffer = new VertexBuffer();
+ buffer = new DirectVertexBuffer();
addMonitor();
return true;
@@ -103,7 +104,7 @@ private void deleteBuffers()
if( tboBuffer != 0 )
{
- RenderSystem.glDeleteBuffers( tboBuffer );
+ DirectBuffers.deleteBuffer( GL31.GL_TEXTURE_BUFFER, tboBuffer );
tboBuffer = 0;
}
@@ -113,6 +114,12 @@ private void deleteBuffers()
tboTexture = 0;
}
+ if( tboUniform != 0 )
+ {
+ DirectBuffers.deleteBuffer( GL31.GL_UNIFORM_BUFFER, tboUniform );
+ tboUniform = 0;
+ }
+
if( buffer != null )
{
buffer.close();
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java
index 3eac19b1d..518ba2f54 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java
@@ -8,6 +8,7 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import net.fabricmc.loader.api.FabricLoader;
+import org.lwjgl.opengl.GL;
import javax.annotation.Nonnull;
import java.util.Arrays;
@@ -48,33 +49,32 @@ public enum MonitorRenderer
@Nonnull
public static MonitorRenderer current()
{
- if( !initialised ) initialise();
-
MonitorRenderer current = ComputerCraft.monitorRenderer;
- if( current == BEST ) return best();
+ if( current == BEST ) current = ComputerCraft.monitorRenderer = best();
return current;
}
private static MonitorRenderer best()
{
+ if( !GL.getCapabilities().OpenGL31 )
+ {
+ ComputerCraft.log.warn( "Texture buffers are not supported on your graphics card. Falling back to VBO monitor renderer." );
+ return VBO;
+ }
+
if( shaderMod )
{
- ComputerCraft.log.warn( "Shader mod detected. Enabling VBO monitor renderer for compatibility." );
- return ComputerCraft.monitorRenderer = VBO;
+ ComputerCraft.log.warn( "Shader mod detected. Falling back to VBO monitor renderer." );
+ return VBO;
}
- return ComputerCraft.monitorRenderer = TBO;
+
+ return TBO;
}
- private static boolean initialised = false;
- private static boolean shaderMod;
private static final List shaderModIds = Arrays.asList( "iris", "canvas", "optifabric" );
+ private static boolean shaderMod = FabricLoader.getInstance().getAllMods().stream()
+ .map( modContainer -> modContainer.getMetadata().getId() )
+ .anyMatch( shaderModIds::contains );
- private static void initialise()
- {
- shaderMod = FabricLoader.getInstance().getAllMods().stream()
- .map( modContainer -> modContainer.getMetadata().getId() )
- .anyMatch( shaderModIds::contains );
-
- initialised = true;
- }
+ public static final boolean canvasModPresent = FabricLoader.getInstance().isModLoaded( "canvas" );
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java
index bcb90642c..c3ee14fde 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java
@@ -25,8 +25,8 @@
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
-import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -65,6 +65,11 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
private int xIndex = 0;
private int yIndex = 0;
+ private BlockPos bbPos;
+ private BlockState bbState;
+ private int bbX, bbY, bbWidth, bbHeight;
+ private AABB boundingBox;
+
public TileMonitor( BlockEntityType extends TileMonitor> type, BlockPos pos, BlockState state, boolean advanced )
{
super( type, pos, state );
@@ -188,7 +193,7 @@ public void blockTick()
@Nonnull
@Override
- public IPeripheral getPeripheral( @NotNull Direction side )
+ public IPeripheral getPeripheral( @Nonnull Direction side )
{
createServerMonitor(); // Ensure the monitor is created before doing anything else.
if( peripheral == null ) peripheral = new MonitorPeripheral( this );
@@ -591,19 +596,34 @@ void removeComputer( IComputerAccess computer )
computers.remove( computer );
}
- // @Nonnull
- // @Override
- // public AABB getRenderBoundingBox()
- // {
- // BlockPos startPos = toWorldPos( 0, 0 );
- // BlockPos endPos = toWorldPos( width, height );
- // return new AABB(
- // Math.min( startPos.getX(), endPos.getX() ),
- // Math.min( startPos.getY(), endPos.getY() ),
- // Math.min( startPos.getZ(), endPos.getZ() ),
- // Math.max( startPos.getX(), endPos.getX() ) + 1,
- // Math.max( startPos.getY(), endPos.getY() ) + 1,
- // Math.max( startPos.getZ(), endPos.getZ() ) + 1
- // );
- // }
+ @Nonnull
+ public AABB getRenderBoundingBox()
+ {
+ // We attempt to cache the bounding box to save having to do property lookups (and allocations!) on every frame.
+ // Unfortunately the AABB does depend on quite a lot of state, so we need to add a bunch of extra fields -
+ // ideally these'd be a single object, but I don't think worth doing until Java has value types.
+ if( boundingBox != null && getBlockState().equals( bbState ) && getBlockPos().equals( bbPos ) &&
+ xIndex == bbX && yIndex == bbY && width == bbWidth && height == bbHeight )
+ {
+ return boundingBox;
+ }
+
+ bbState = getBlockState();
+ bbPos = getBlockPos();
+ bbX = xIndex;
+ bbY = yIndex;
+ bbWidth = width;
+ bbHeight = height;
+
+ BlockPos startPos = toWorldPos( 0, 0 );
+ BlockPos endPos = toWorldPos( width, height );
+ return boundingBox = new AABB(
+ Math.min( startPos.getX(), endPos.getX() ),
+ Math.min( startPos.getY(), endPos.getY() ),
+ Math.min( startPos.getZ(), endPos.getZ() ),
+ Math.max( startPos.getX(), endPos.getX() ) + 1,
+ Math.max( startPos.getY(), endPos.getY() ) + 1,
+ Math.max( startPos.getZ(), endPos.getZ() ) + 1
+ );
+ }
}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java
index 41d8a27a1..42b912776 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java
@@ -10,7 +10,9 @@
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.media.items.ItemPrintout;
-import dan200.computercraft.shared.util.*;
+import dan200.computercraft.shared.util.ColourUtils;
+import dan200.computercraft.shared.util.DefaultSidedInventory;
+import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java
index b2a032464..cf6bb6aa9 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java
@@ -29,7 +29,6 @@
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import java.util.*;
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
@@ -57,7 +56,7 @@ public abstract class SpeakerPeripheral implements IPeripheral
private long clock = 0;
private long lastPositionTime;
- private Vec3 lastPosition;
+ private SpeakerPosition lastPosition;
private long lastPlayTime;
@@ -72,8 +71,9 @@ public void update()
{
clock++;
- Vec3 pos = getPosition();
- Level level = getLevel();
+ SpeakerPosition position = getPosition();
+ Level level = position.level();
+ Vec3 pos = position.position();
if( level == null ) return;
MinecraftServer server = level.getServer();
@@ -125,20 +125,20 @@ public void update()
{
lastPlayTime = clock;
NetworkHandler.sendToAllAround(
- new SpeakerPlayClientMessage( getSource(), pos, sound.location, sound.volume, sound.pitch ),
+ new SpeakerPlayClientMessage( getSource(), position, sound.location, sound.volume, sound.pitch ),
level, pos, sound.volume * 16
);
- syncedPosition( pos );
+ syncedPosition( position );
}
else if( dfpwmState != null && dfpwmState.shouldSendPending( now ) )
{
// If clients need to receive another batch of audio, send it and then notify computers our internal buffer is
// free again.
NetworkHandler.sendToAllTracking(
- new SpeakerAudioClientMessage( getSource(), pos, dfpwmState.getVolume(), dfpwmState.pullPending( now ) ),
- getLevel().getChunkAt( new BlockPos( pos ) )
+ new SpeakerAudioClientMessage( getSource(), position, dfpwmState.getVolume(), dfpwmState.pullPending( now ) ),
+ level.getChunkAt( new BlockPos( pos ) )
);
- syncedPosition( pos );
+ syncedPosition( position );
// And notify computers that we have space for more audio.
synchronized( computers )
@@ -153,25 +153,19 @@ else if( dfpwmState != null && dfpwmState.shouldSendPending( now ) )
// Push position updates to any speakers which have ever played a note,
// have moved by a non-trivial amount and haven't had a position update
// in the last second.
- if( lastPosition != null && (clock - lastPositionTime) >= 20 )
+ if( lastPosition != null && (clock - lastPositionTime) >= 20 && !lastPosition.withinDistance( position, 0.1 ) )
{
- Vec3 position = getPosition();
- if( lastPosition.distanceToSqr( position ) >= 0.1 )
- {
- NetworkHandler.sendToAllTracking(
- new SpeakerMoveClientMessage( getSource(), position ),
- getLevel().getChunkAt( new BlockPos( position ) )
- );
- syncedPosition( position );
- }
+ // TODO: What to do when entities move away? How do we notify people left behind that they're gone.
+ NetworkHandler.sendToAllTracking(
+ new SpeakerMoveClientMessage( getSource(), position ),
+ level.getChunkAt( new BlockPos( pos ) )
+ );
+ syncedPosition( position );
}
}
- @Nullable
- public abstract Level getLevel();
-
@Nonnull
- public abstract Vec3 getPosition();
+ public abstract SpeakerPosition getPosition();
@Nonnull
public UUID getSource()
@@ -373,7 +367,7 @@ public final void stop()
shouldStop = true;
}
- private void syncedPosition( Vec3 position )
+ private void syncedPosition( SpeakerPosition position )
{
lastPosition = position;
lastPositionTime = clock;
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPosition.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPosition.java
new file mode 100644
index 000000000..207869b89
--- /dev/null
+++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPosition.java
@@ -0,0 +1,91 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.shared.peripheral.speaker;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.Minecraft;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec3;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.OptionalInt;
+
+public record SpeakerPosition(@Nullable Level level, @Nonnull Vec3 position, @Nullable Entity entity)
+{
+ public static SpeakerPosition of( @Nullable Level level, @Nonnull Vec3 position )
+ {
+ return new SpeakerPosition( level, position, null );
+ }
+
+ public static SpeakerPosition of( @Nonnull Entity entity )
+ {
+ return new SpeakerPosition( entity.level, entity.getEyePosition( 1 ), entity );
+ }
+
+ public boolean withinDistance( SpeakerPosition other, double distanceSq )
+ {
+ return level == other.level && entity == other.entity && position.distanceToSqr( other.position ) <= distanceSq;
+ }
+
+ public Message asMessage()
+ {
+ if( level == null ) throw new NullPointerException( "Cannot send a position without a level" );
+ return new Message( level.dimension().location(), position, entity == null ? OptionalInt.empty() : OptionalInt.of( entity.getId() ) );
+ }
+
+ public static final class Message
+ {
+ private final ResourceLocation level;
+ private final Vec3 position;
+ private final OptionalInt entity;
+
+ private Message( ResourceLocation level, Vec3 position, OptionalInt entity )
+ {
+ this.level = level;
+ this.position = position;
+ this.entity = entity;
+ }
+
+ public static Message read( @Nonnull FriendlyByteBuf buffer )
+ {
+ ResourceLocation level = buffer.readResourceLocation();
+ Vec3 position = new Vec3( buffer.readDouble(), buffer.readDouble(), buffer.readDouble() );
+ OptionalInt entity = buffer.readBoolean() ? OptionalInt.of( buffer.readInt() ) : OptionalInt.empty();
+ return new Message( level, position, entity );
+ }
+
+ public void write( @Nonnull FriendlyByteBuf buffer )
+ {
+ buffer.writeResourceLocation( level );
+
+ buffer.writeDouble( position.x );
+ buffer.writeDouble( position.y );
+ buffer.writeDouble( position.z );
+
+ buffer.writeBoolean( entity.isPresent() );
+ if( entity.isPresent() ) buffer.writeInt( entity.getAsInt() );
+ }
+
+ @Nonnull
+ @Environment( EnvType.CLIENT )
+ public SpeakerPosition reify()
+ {
+ Minecraft minecraft = Minecraft.getInstance();
+ Level level = minecraft.level;
+ if( level != null && !level.dimension().location().equals( this.level ) ) level = null;
+
+ return new SpeakerPosition(
+ level, position,
+ level != null && entity.isPresent() ? level.getEntity( entity.getAsInt() ) : null
+ );
+ }
+ }
+}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java
index 01f1e18a2..8460da970 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java
@@ -12,7 +12,6 @@
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
-import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
@@ -63,18 +62,11 @@ private Peripheral( TileSpeaker speaker )
this.speaker = speaker;
}
- @Override
- public Level getLevel()
- {
- return speaker.getLevel();
- }
-
@Nonnull
@Override
- public Vec3 getPosition()
+ public SpeakerPosition getPosition()
{
- BlockPos pos = speaker.getBlockPos();
- return new Vec3( pos.getX(), pos.getY(), pos.getZ() );
+ return SpeakerPosition.of( speaker.getLevel(), Vec3.atCenterOf( speaker.getBlockPos() ) );
}
@Override
diff --git a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java
index 374bb1f23..f47b2efb9 100644
--- a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java
+++ b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java
@@ -21,6 +21,7 @@
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
@@ -60,6 +61,10 @@ else if( entity instanceof LivingEntity living )
{
return living.getMainHandItem() == stack || living.getOffhandItem() == stack ? entity : null;
}
+ else if( entity instanceof ItemEntity itemEntity )
+ {
+ return itemEntity.getItem() == stack ? entity : null;
+ }
else
{
return null;
diff --git a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java
index 959cfcca6..d18630336 100644
--- a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java
+++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java
@@ -10,7 +10,6 @@
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
@@ -27,21 +26,13 @@ public PocketSpeaker( ResourceLocation id, ItemStack item )
@Override
public IPeripheral createPeripheral( @Nonnull IPocketAccess access )
{
- return new PocketSpeakerPeripheral();
+ return new PocketSpeakerPeripheral( access );
}
@Override
public void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral )
{
- if( !(peripheral instanceof PocketSpeakerPeripheral speaker) ) return;
-
- Entity entity = access.getEntity();
- if( entity != null )
- {
- speaker.setLocation( entity.getCommandSenderWorld(), entity.getEyePosition( 1 ) );
- }
-
- speaker.update();
- access.setLight( speaker.madeSound() ? 0x3320fc : -1 );
+ if( !(peripheral instanceof PocketSpeakerPeripheral) ) return;
+ ((PocketSpeakerPeripheral) peripheral).update();
}
}
diff --git a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java
index 38869fda4..ed28046ac 100644
--- a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java
+++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java
@@ -6,7 +6,10 @@
package dan200.computercraft.shared.pocket.peripherals;
import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.api.pocket.IPocketAccess;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
+import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
@@ -14,31 +17,41 @@
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral
{
- private Level world = null;
+ private final IPocketAccess access;
+ private Level level;
private Vec3 position = Vec3.ZERO;
- void setLocation( Level world, Vec3 position )
+ public PocketSpeakerPeripheral( IPocketAccess access )
{
- this.position = position;
- this.world = world;
+ this.access = access;
}
+ @Nonnull
@Override
- public Level getLevel()
+ public SpeakerPosition getPosition()
{
- return world;
+ Entity entity = access.getEntity();
+ return entity == null ? SpeakerPosition.of( level, position ) : SpeakerPosition.of( entity );
}
- @Nonnull
@Override
- public Vec3 getPosition()
+ public boolean equals( IPeripheral other )
{
- return world != null ? position : null;
+ return other instanceof PocketSpeakerPeripheral;
}
@Override
- public boolean equals( IPeripheral other )
+ public void update()
{
- return other instanceof PocketSpeakerPeripheral;
+ Entity entity = access.getEntity();
+ if( entity != null )
+ {
+ level = entity.level;
+ position = entity.position();
+ }
+
+ super.update();
+
+ access.setLight( madeSound() ? 0x3320fc : -1 );
}
}
diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java
index 481ae91c9..fd13fe0c9 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java
@@ -66,6 +66,7 @@
* accessible by accessing the `"left"` or `"right"` peripheral.
*
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
+ *
* @cc.module turtle
* @cc.since 1.3
*/
diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java
index e9bf0fa72..85feeb6ee 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java
@@ -432,10 +432,13 @@ public ItemStack removeItem( int slot, int count )
@Override
public void setItem( int i, @Nonnull ItemStack stack )
{
- if( i >= 0 && i < INVENTORY_SIZE && !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) )
+ if ( i >= 0 && i < INVENTORY_SIZE )
{
inventory.set( i, stack );
- onInventoryDefinitelyChanged();
+ if ( !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) )
+ {
+ onInventoryDefinitelyChanged();
+ }
}
}
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
index 9745f74a8..56c37f59f 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
@@ -11,7 +11,10 @@
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.shared.TurtlePermissions;
-import dan200.computercraft.shared.util.*;
+import dan200.computercraft.shared.util.DropConsumer;
+import dan200.computercraft.shared.util.InventoryUtil;
+import dan200.computercraft.shared.util.ItemStorage;
+import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.TextComponent;
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
index 269b73616..f30df1817 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
@@ -9,7 +9,6 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
-import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
@@ -23,7 +22,6 @@
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions;
-import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.animal.horse.AbstractHorse;
import net.minecraft.world.item.ItemStack;
@@ -197,13 +195,6 @@ public void unloadInventory( ITurtleAccess turtle )
getInventory().setChanged();
}
- @Nonnull
- @Override
- public EntityType> getType()
- {
- return Registry.ModEntities.TURTLE_PLAYER;
- }
-
@Override
public Vec3 position()
{
diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java
index 59f5c22d2..ccd33ca88 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java
@@ -11,14 +11,13 @@
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
+import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.resources.model.ModelResourceLocation;
-import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nonnull;
@@ -41,18 +40,11 @@ private static class Peripheral extends UpgradeSpeakerPeripheral
this.turtle = turtle;
}
- @Override
- public Level getLevel()
- {
- return turtle.getLevel();
- }
-
@Nonnull
@Override
- public Vec3 getPosition()
+ public SpeakerPosition getPosition()
{
- BlockPos pos = turtle.getPosition();
- return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
+ return SpeakerPosition.of( turtle.getLevel(), Vec3.atCenterOf( turtle.getPosition() ) );
}
@Override
diff --git a/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java b/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java
index 9a8480852..8fe407c8f 100644
--- a/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java
+++ b/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java
@@ -285,7 +285,7 @@ private static class FakeNetworkManager extends Connection
FakeNetworkManager()
{
super( PacketFlow.CLIENTBOUND );
- ((ConnectionAccess)this).setChannel( new EmbeddedChannel() );
+ ((ConnectionAccess) this).setChannel( new EmbeddedChannel() );
}
@Override
diff --git a/src/main/java/dan200/computercraft/shared/util/IDAssigner.java b/src/main/java/dan200/computercraft/shared/util/IDAssigner.java
index e1f7be027..00c780e04 100644
--- a/src/main/java/dan200/computercraft/shared/util/IDAssigner.java
+++ b/src/main/java/dan200/computercraft/shared/util/IDAssigner.java
@@ -46,21 +46,17 @@ public static File getDir()
return GameInstanceUtils.getServer().getWorldPath( FOLDER ).toFile();
}
- private static MinecraftServer getCachedServer()
+ private static boolean hasServerChanged()
{
- if( server == null ) return null;
+ if( server == null ) return true;
MinecraftServer currentServer = server.get();
- if( currentServer == null ) return null;
-
- if( currentServer != GameInstanceUtils.getServer() ) return null;
- return currentServer;
+ return currentServer == null || currentServer != GameInstanceUtils.getServer();
}
public static synchronized int getNextId( String kind )
{
- MinecraftServer currentServer = getCachedServer();
- if( currentServer == null )
+ if( hasServerChanged() )
{
// The server has changed, refetch our ID map
server = new WeakReference<>( GameInstanceUtils.getServer() );
@@ -69,23 +65,22 @@ public static synchronized int getNextId( String kind )
dir.mkdirs();
// Load our ID file from disk
+ Map newIds = null;
idFile = new File( dir, "ids.json" ).toPath();
if( Files.isRegularFile( idFile ) )
{
try( Reader reader = Files.newBufferedReader( idFile, StandardCharsets.UTF_8 ) )
{
- ids = GSON.fromJson( reader, ID_TOKEN );
+ newIds = GSON.fromJson( reader, ID_TOKEN );
}
catch( Exception e )
{
ComputerCraft.log.error( "Cannot load id file '" + idFile + "'", e );
- ids = new HashMap<>();
}
}
- else
- {
- ids = new HashMap<>();
- }
+
+ if( newIds == null ) newIds = new HashMap<>();
+ ids = newIds;
}
Integer existing = ids.get( kind );
diff --git a/src/main/java/dan200/computercraft/shared/util/Palette.java b/src/main/java/dan200/computercraft/shared/util/Palette.java
index f5343726f..6f4900300 100644
--- a/src/main/java/dan200/computercraft/shared/util/Palette.java
+++ b/src/main/java/dan200/computercraft/shared/util/Palette.java
@@ -5,13 +5,18 @@
*/
package dan200.computercraft.shared.util;
+import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
+import javax.annotation.Nonnull;
+
public class Palette
{
private static final int PALETTE_SIZE = 16;
private final double[][] colours = new double[PALETTE_SIZE][3];
+ private final byte[][] byteColours = new byte[PALETTE_SIZE][4];
+ private final byte[][] greyByteColours = new byte[PALETTE_SIZE][4];
public static final Palette DEFAULT = new Palette();
@@ -19,16 +24,23 @@ public Palette()
{
// Get the default palette
resetColours();
+
+ for( int i = 0; i < PALETTE_SIZE; i++ ) byteColours[i][3] = greyByteColours[i][3] = (byte) 255;
}
public void setColour( int i, double r, double g, double b )
{
- if( i >= 0 && i < colours.length )
- {
- colours[i][0] = r;
- colours[i][1] = g;
- colours[i][2] = b;
- }
+ if( i < 0 || i >= colours.length ) return;
+ colours[i][0] = r;
+ colours[i][1] = g;
+ colours[i][2] = b;
+
+ byteColours[i][0] = (byte) (int) (r * 255);
+ byteColours[i][1] = (byte) (int) (g * 255);
+ byteColours[i][2] = (byte) (int) (b * 255);
+
+ byte grey = (byte) (int) ((r + g + b) / 3 * 255);
+ greyByteColours[i][0] = greyByteColours[i][1] = greyByteColours[i][2] = grey;
}
public void setColour( int i, Colour colour )
@@ -38,19 +50,29 @@ public void setColour( int i, Colour colour )
public double[] getColour( int i )
{
- if( i >= 0 && i < colours.length )
- {
- return colours[i];
- }
- return null;
+ return i >= 0 && i < colours.length ? colours[i] : null;
+ }
+
+ /**
+ * Get the colour as a set of bytes rather than floats. This is called frequently by {@link FixedWidthFontRenderer},
+ * as our vertex format uses bytes.
+ *
+ * This allows us to do the conversion once (when setting the colour) rather than for every vertex, at the cost of
+ * some memory overhead.
+ *
+ * @param i The colour index.
+ * @param greyscale Whether this number should be converted to greyscale.
+ * @return The number as a tuple of bytes.
+ */
+ @Nonnull
+ public byte[] getByteColour( int i, boolean greyscale )
+ {
+ return greyscale ? greyByteColours[i] : byteColours[i];
}
public void resetColour( int i )
{
- if( i >= 0 && i < colours.length )
- {
- setColour( i, Colour.VALUES[i] );
- }
+ if( i >= 0 && i < colours.length ) setColour( i, Colour.VALUES[i] );
}
public void resetColours()
@@ -89,9 +111,12 @@ public void write( FriendlyByteBuf buffer )
public void read( FriendlyByteBuf buffer )
{
- for( double[] colour : colours )
+ for( int i = 0; i < PALETTE_SIZE; i++ )
{
- for( int i = 0; i < colour.length; i++ ) colour[i] = (buffer.readByte() & 0xFF) / 255.0;
+ double r = (buffer.readByte() & 0xFF) / 255.0;
+ double g = (buffer.readByte() & 0xFF) / 255.0;
+ double b = (buffer.readByte() & 0xFF) / 255.0;
+ setColour( i, r, g, b );
}
}
@@ -117,7 +142,8 @@ public void readFromNBT( CompoundTag nbt )
for( int i = 0; i < colours.length; i++ )
{
- colours[i] = decodeRGB8( rgb8[i] );
+ double[] colours = decodeRGB8( rgb8[i] );
+ setColour( i, colours[0], colours[1], colours[2] );
}
}
}
diff --git a/src/main/resources/assets/computercraft/blockstates/computer_advanced.json b/src/main/resources/assets/computercraft/blockstates/computer_advanced.json
deleted file mode 100644
index f438720d8..000000000
--- a/src/main/resources/assets/computercraft/blockstates/computer_advanced.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "variants": {
- "facing=north,state=off": { "model": "computercraft:block/computer_advanced_off" },
- "facing=south,state=off": { "model": "computercraft:block/computer_advanced_off", "y": 180 },
- "facing=west,state=off": { "model": "computercraft:block/computer_advanced_off", "y": 270 },
- "facing=east,state=off": { "model": "computercraft:block/computer_advanced_off", "y": 90 },
-
- "facing=north,state=on": { "model": "computercraft:block/computer_advanced_on" },
- "facing=south,state=on": { "model": "computercraft:block/computer_advanced_on", "y": 180 },
- "facing=west,state=on": { "model": "computercraft:block/computer_advanced_on", "y": 270 },
- "facing=east,state=on": { "model": "computercraft:block/computer_advanced_on", "y": 90 },
-
- "facing=north,state=blinking": { "model": "computercraft:block/computer_advanced_blinking" },
- "facing=south,state=blinking": { "model": "computercraft:block/computer_advanced_blinking", "y": 180 },
- "facing=west,state=blinking": { "model": "computercraft:block/computer_advanced_blinking", "y": 270 },
- "facing=east,state=blinking": { "model": "computercraft:block/computer_advanced_blinking", "y": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/computer_command.json b/src/main/resources/assets/computercraft/blockstates/computer_command.json
deleted file mode 100644
index cb45d46c2..000000000
--- a/src/main/resources/assets/computercraft/blockstates/computer_command.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "variants": {
-
- "facing=north,state=off": { "model": "computercraft:block/computer_command_off" },
- "facing=south,state=off": { "model": "computercraft:block/computer_command_off", "y": 180 },
- "facing=west,state=off": { "model": "computercraft:block/computer_command_off", "y": 270 },
- "facing=east,state=off": { "model": "computercraft:block/computer_command_off", "y": 90 },
-
- "facing=north,state=on": { "model": "computercraft:block/computer_command_on" },
- "facing=south,state=on": { "model": "computercraft:block/computer_command_on", "y": 180 },
- "facing=west,state=on": { "model": "computercraft:block/computer_command_on", "y": 270 },
- "facing=east,state=on": { "model": "computercraft:block/computer_command_on", "y": 90 },
-
- "facing=north,state=blinking": { "model": "computercraft:block/computer_command_blinking" },
- "facing=south,state=blinking": { "model": "computercraft:block/computer_command_blinking", "y": 180 },
- "facing=west,state=blinking": { "model": "computercraft:block/computer_command_blinking", "y": 270 },
- "facing=east,state=blinking": { "model": "computercraft:block/computer_command_blinking", "y": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/computer_normal.json b/src/main/resources/assets/computercraft/blockstates/computer_normal.json
deleted file mode 100644
index 95e521a7d..000000000
--- a/src/main/resources/assets/computercraft/blockstates/computer_normal.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "variants": {
- "facing=north,state=off": { "model": "computercraft:block/computer_normal_off" },
- "facing=south,state=off": { "model": "computercraft:block/computer_normal_off", "y": 180 },
- "facing=west,state=off": { "model": "computercraft:block/computer_normal_off", "y": 270 },
- "facing=east,state=off": { "model": "computercraft:block/computer_normal_off", "y": 90 },
-
- "facing=north,state=on": { "model": "computercraft:block/computer_normal_on" },
- "facing=south,state=on": { "model": "computercraft:block/computer_normal_on", "y": 180 },
- "facing=west,state=on": { "model": "computercraft:block/computer_normal_on", "y": 270 },
- "facing=east,state=on": { "model": "computercraft:block/computer_normal_on", "y": 90 },
-
- "facing=north,state=blinking": { "model": "computercraft:block/computer_normal_blinking" },
- "facing=south,state=blinking": { "model": "computercraft:block/computer_normal_blinking", "y": 180 },
- "facing=west,state=blinking": { "model": "computercraft:block/computer_normal_blinking", "y": 270 },
- "facing=east,state=blinking": { "model": "computercraft:block/computer_normal_blinking", "y": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/monitor_advanced.json b/src/main/resources/assets/computercraft/blockstates/monitor_advanced.json
deleted file mode 100644
index b55870f67..000000000
--- a/src/main/resources/assets/computercraft/blockstates/monitor_advanced.json
+++ /dev/null
@@ -1,364 +0,0 @@
-{
- "variants": {
- "facing=north,orientation=north,state=none": { "model": "computercraft:block/monitor_advanced" },
- "facing=south,orientation=north,state=none": { "model": "computercraft:block/monitor_advanced", "y": 180 },
- "facing=west,orientation=north,state=none": { "model": "computercraft:block/monitor_advanced", "y": 270 },
- "facing=east,orientation=north,state=none": { "model": "computercraft:block/monitor_advanced", "y": 90 },
- "facing=north,orientation=north,state=r": { "model": "computercraft:block/monitor_advanced_r" },
- "facing=south,orientation=north,state=r": { "model": "computercraft:block/monitor_advanced_r", "y": 180 },
- "facing=west,orientation=north,state=r": { "model": "computercraft:block/monitor_advanced_r", "y": 270 },
- "facing=east,orientation=north,state=r": { "model": "computercraft:block/monitor_advanced_r", "y": 90 },
- "facing=north,orientation=north,state=lr": { "model": "computercraft:block/monitor_advanced_lr" },
- "facing=south,orientation=north,state=lr": { "model": "computercraft:block/monitor_advanced_lr", "y": 180 },
- "facing=west,orientation=north,state=lr": { "model": "computercraft:block/monitor_advanced_lr", "y": 270 },
- "facing=east,orientation=north,state=lr": { "model": "computercraft:block/monitor_advanced_lr", "y": 90 },
- "facing=north,orientation=north,state=l": { "model": "computercraft:block/monitor_advanced_l" },
- "facing=south,orientation=north,state=l": { "model": "computercraft:block/monitor_advanced_l", "y": 180 },
- "facing=west,orientation=north,state=l": { "model": "computercraft:block/monitor_advanced_l", "y": 270 },
- "facing=east,orientation=north,state=l": { "model": "computercraft:block/monitor_advanced_l", "y": 90 },
- "facing=north,orientation=north,state=d": { "model": "computercraft:block/monitor_advanced_d" },
- "facing=south,orientation=north,state=d": { "model": "computercraft:block/monitor_advanced_d", "y": 180 },
- "facing=west,orientation=north,state=d": { "model": "computercraft:block/monitor_advanced_d", "y": 270 },
- "facing=east,orientation=north,state=d": { "model": "computercraft:block/monitor_advanced_d", "y": 90 },
- "facing=north,orientation=north,state=ud": { "model": "computercraft:block/monitor_advanced_ud" },
- "facing=south,orientation=north,state=ud": { "model": "computercraft:block/monitor_advanced_ud", "y": 180 },
- "facing=west,orientation=north,state=ud": { "model": "computercraft:block/monitor_advanced_ud", "y": 270 },
- "facing=east,orientation=north,state=ud": { "model": "computercraft:block/monitor_advanced_ud", "y": 90 },
- "facing=north,orientation=north,state=u": { "model": "computercraft:block/monitor_advanced_u" },
- "facing=south,orientation=north,state=u": { "model": "computercraft:block/monitor_advanced_u", "y": 180 },
- "facing=west,orientation=north,state=u": { "model": "computercraft:block/monitor_advanced_u", "y": 270 },
- "facing=east,orientation=north,state=u": { "model": "computercraft:block/monitor_advanced_u", "y": 90 },
- "facing=north,orientation=north,state=rd": { "model": "computercraft:block/monitor_advanced_rd" },
- "facing=south,orientation=north,state=rd": { "model": "computercraft:block/monitor_advanced_rd", "y": 180 },
- "facing=west,orientation=north,state=rd": { "model": "computercraft:block/monitor_advanced_rd", "y": 270 },
- "facing=east,orientation=north,state=rd": { "model": "computercraft:block/monitor_advanced_rd", "y": 90 },
- "facing=north,orientation=north,state=lrd": { "model": "computercraft:block/monitor_advanced_lrd" },
- "facing=south,orientation=north,state=lrd": { "model": "computercraft:block/monitor_advanced_lrd", "y": 180 },
- "facing=west,orientation=north,state=lrd": { "model": "computercraft:block/monitor_advanced_lrd", "y": 270 },
- "facing=east,orientation=north,state=lrd": { "model": "computercraft:block/monitor_advanced_lrd", "y": 90 },
- "facing=north,orientation=north,state=ld": { "model": "computercraft:block/monitor_advanced_ld" },
- "facing=south,orientation=north,state=ld": { "model": "computercraft:block/monitor_advanced_ld", "y": 180 },
- "facing=west,orientation=north,state=ld": { "model": "computercraft:block/monitor_advanced_ld", "y": 270 },
- "facing=east,orientation=north,state=ld": { "model": "computercraft:block/monitor_advanced_ld", "y": 90 },
- "facing=north,orientation=north,state=rud": { "model": "computercraft:block/monitor_advanced_rud" },
- "facing=south,orientation=north,state=rud": { "model": "computercraft:block/monitor_advanced_rud", "y": 180 },
- "facing=west,orientation=north,state=rud": { "model": "computercraft:block/monitor_advanced_rud", "y": 270 },
- "facing=east,orientation=north,state=rud": { "model": "computercraft:block/monitor_advanced_rud", "y": 90 },
- "facing=north,orientation=north,state=lrud": { "model": "computercraft:block/monitor_advanced_lrud" },
- "facing=south,orientation=north,state=lrud": { "model": "computercraft:block/monitor_advanced_lrud", "y": 180 },
- "facing=west,orientation=north,state=lrud": { "model": "computercraft:block/monitor_advanced_lrud", "y": 270 },
- "facing=east,orientation=north,state=lrud": { "model": "computercraft:block/monitor_advanced_lrud", "y": 90 },
- "facing=north,orientation=north,state=lud": { "model": "computercraft:block/monitor_advanced_lud" },
- "facing=south,orientation=north,state=lud": { "model": "computercraft:block/monitor_advanced_lud", "y": 180 },
- "facing=west,orientation=north,state=lud": { "model": "computercraft:block/monitor_advanced_lud", "y": 270 },
- "facing=east,orientation=north,state=lud": { "model": "computercraft:block/monitor_advanced_lud", "y": 90 },
- "facing=north,orientation=north,state=ru": { "model": "computercraft:block/monitor_advanced_ru" },
- "facing=south,orientation=north,state=ru": { "model": "computercraft:block/monitor_advanced_ru", "y": 180 },
- "facing=west,orientation=north,state=ru": { "model": "computercraft:block/monitor_advanced_ru", "y": 270 },
- "facing=east,orientation=north,state=ru": { "model": "computercraft:block/monitor_advanced_ru", "y": 90 },
- "facing=north,orientation=north,state=lru": { "model": "computercraft:block/monitor_advanced_lru" },
- "facing=south,orientation=north,state=lru": { "model": "computercraft:block/monitor_advanced_lru", "y": 180 },
- "facing=west,orientation=north,state=lru": { "model": "computercraft:block/monitor_advanced_lru", "y": 270 },
- "facing=east,orientation=north,state=lru": { "model": "computercraft:block/monitor_advanced_lru", "y": 90 },
- "facing=north,orientation=north,state=lu": { "model": "computercraft:block/monitor_advanced_lu" },
- "facing=south,orientation=north,state=lu": { "model": "computercraft:block/monitor_advanced_lu", "y": 180 },
- "facing=west,orientation=north,state=lu": { "model": "computercraft:block/monitor_advanced_lu", "y": 270 },
- "facing=east,orientation=north,state=lu": { "model": "computercraft:block/monitor_advanced_lu", "y": 90 },
-
- "facing=north,orientation=up,state=none": { "model": "computercraft:block/monitor_advanced", "x": 270 },
- "facing=south,orientation=up,state=none": {
- "model": "computercraft:block/monitor_advanced", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=none": {
- "model": "computercraft:block/monitor_advanced", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=none": { "model": "computercraft:block/monitor_advanced", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=r": { "model": "computercraft:block/monitor_advanced_r", "x": 270 },
- "facing=south,orientation=up,state=r": {
- "model": "computercraft:block/monitor_advanced_r", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=r": { "model": "computercraft:block/monitor_advanced_r", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=r": { "model": "computercraft:block/monitor_advanced_r", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=lr": { "model": "computercraft:block/monitor_advanced_lr", "x": 270 },
- "facing=south,orientation=up,state=lr": {
- "model": "computercraft:block/monitor_advanced_lr", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lr": {
- "model": "computercraft:block/monitor_advanced_lr", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lr": {
- "model": "computercraft:block/monitor_advanced_lr", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=l": { "model": "computercraft:block/monitor_advanced_l", "x": 270 },
- "facing=south,orientation=up,state=l": {
- "model": "computercraft:block/monitor_advanced_l", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=l": { "model": "computercraft:block/monitor_advanced_l", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=l": { "model": "computercraft:block/monitor_advanced_l", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=d": { "model": "computercraft:block/monitor_advanced_d", "x": 270 },
- "facing=south,orientation=up,state=d": {
- "model": "computercraft:block/monitor_advanced_d", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=d": { "model": "computercraft:block/monitor_advanced_d", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=d": { "model": "computercraft:block/monitor_advanced_d", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=ud": { "model": "computercraft:block/monitor_advanced_ud", "x": 270 },
- "facing=south,orientation=up,state=ud": {
- "model": "computercraft:block/monitor_advanced_ud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=ud": {
- "model": "computercraft:block/monitor_advanced_ud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=ud": {
- "model": "computercraft:block/monitor_advanced_ud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=u": { "model": "computercraft:block/monitor_advanced_u", "x": 270 },
- "facing=south,orientation=up,state=u": {
- "model": "computercraft:block/monitor_advanced_u", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=u": { "model": "computercraft:block/monitor_advanced_u", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=u": { "model": "computercraft:block/monitor_advanced_u", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=rd": { "model": "computercraft:block/monitor_advanced_rd", "x": 270 },
- "facing=south,orientation=up,state=rd": {
- "model": "computercraft:block/monitor_advanced_rd", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=rd": {
- "model": "computercraft:block/monitor_advanced_rd", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=rd": {
- "model": "computercraft:block/monitor_advanced_rd", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lrd": { "model": "computercraft:block/monitor_advanced_lrd", "x": 270 },
- "facing=south,orientation=up,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=ld": { "model": "computercraft:block/monitor_advanced_ld", "x": 270 },
- "facing=south,orientation=up,state=ld": {
- "model": "computercraft:block/monitor_advanced_ld", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=ld": {
- "model": "computercraft:block/monitor_advanced_ld", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=ld": {
- "model": "computercraft:block/monitor_advanced_ld", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=rud": { "model": "computercraft:block/monitor_advanced_rud", "x": 270 },
- "facing=south,orientation=up,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lrud": { "model": "computercraft:block/monitor_advanced_lrud", "x": 270 },
- "facing=south,orientation=up,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lud": { "model": "computercraft:block/monitor_advanced_lud", "x": 270 },
- "facing=south,orientation=up,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=ru": { "model": "computercraft:block/monitor_advanced_ru", "x": 270 },
- "facing=south,orientation=up,state=ru": {
- "model": "computercraft:block/monitor_advanced_ru", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=ru": {
- "model": "computercraft:block/monitor_advanced_ru", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=ru": {
- "model": "computercraft:block/monitor_advanced_ru", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lru": { "model": "computercraft:block/monitor_advanced_lru", "x": 270 },
- "facing=south,orientation=up,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lu": { "model": "computercraft:block/monitor_advanced_lu", "x": 270 },
- "facing=south,orientation=up,state=lu": {
- "model": "computercraft:block/monitor_advanced_lu", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lu": {
- "model": "computercraft:block/monitor_advanced_lu", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lu": {
- "model": "computercraft:block/monitor_advanced_lu", "y": 90, "x": 270
- },
-
- "facing=north,orientation=down,state=none": { "model": "computercraft:block/monitor_advanced", "x": 90 },
- "facing=south,orientation=down,state=none": {
- "model": "computercraft:block/monitor_advanced", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=none": {
- "model": "computercraft:block/monitor_advanced", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=none": {
- "model": "computercraft:block/monitor_advanced", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=r": { "model": "computercraft:block/monitor_advanced_r", "x": 90 },
- "facing=south,orientation=down,state=r": {
- "model": "computercraft:block/monitor_advanced_r", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=r": {
- "model": "computercraft:block/monitor_advanced_r", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=r": { "model": "computercraft:block/monitor_advanced_r", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=lr": { "model": "computercraft:block/monitor_advanced_lr", "x": 90 },
- "facing=south,orientation=down,state=lr": {
- "model": "computercraft:block/monitor_advanced_lr", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lr": {
- "model": "computercraft:block/monitor_advanced_lr", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lr": {
- "model": "computercraft:block/monitor_advanced_lr", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=l": { "model": "computercraft:block/monitor_advanced_l", "x": 90 },
- "facing=south,orientation=down,state=l": {
- "model": "computercraft:block/monitor_advanced_l", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=l": {
- "model": "computercraft:block/monitor_advanced_l", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=l": { "model": "computercraft:block/monitor_advanced_l", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=d": { "model": "computercraft:block/monitor_advanced_d", "x": 90 },
- "facing=south,orientation=down,state=d": {
- "model": "computercraft:block/monitor_advanced_d", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=d": {
- "model": "computercraft:block/monitor_advanced_d", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=d": { "model": "computercraft:block/monitor_advanced_d", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=ud": { "model": "computercraft:block/monitor_advanced_ud", "x": 90 },
- "facing=south,orientation=down,state=ud": {
- "model": "computercraft:block/monitor_advanced_ud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=ud": {
- "model": "computercraft:block/monitor_advanced_ud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=ud": {
- "model": "computercraft:block/monitor_advanced_ud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=u": { "model": "computercraft:block/monitor_advanced_u", "x": 90 },
- "facing=south,orientation=down,state=u": {
- "model": "computercraft:block/monitor_advanced_u", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=u": {
- "model": "computercraft:block/monitor_advanced_u", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=u": { "model": "computercraft:block/monitor_advanced_u", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=rd": { "model": "computercraft:block/monitor_advanced_rd", "x": 90 },
- "facing=south,orientation=down,state=rd": {
- "model": "computercraft:block/monitor_advanced_rd", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=rd": {
- "model": "computercraft:block/monitor_advanced_rd", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=rd": {
- "model": "computercraft:block/monitor_advanced_rd", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lrd": { "model": "computercraft:block/monitor_advanced_lrd", "x": 90 },
- "facing=south,orientation=down,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lrd": {
- "model": "computercraft:block/monitor_advanced_lrd", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=ld": { "model": "computercraft:block/monitor_advanced_ld", "x": 90 },
- "facing=south,orientation=down,state=ld": {
- "model": "computercraft:block/monitor_advanced_ld", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=ld": {
- "model": "computercraft:block/monitor_advanced_ld", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=ld": {
- "model": "computercraft:block/monitor_advanced_ld", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=rud": { "model": "computercraft:block/monitor_advanced_rud", "x": 90 },
- "facing=south,orientation=down,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=rud": {
- "model": "computercraft:block/monitor_advanced_rud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lrud": { "model": "computercraft:block/monitor_advanced_lrud", "x": 90 },
- "facing=south,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_advanced_lrud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lud": { "model": "computercraft:block/monitor_advanced_lud", "x": 90 },
- "facing=south,orientation=down,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lud": {
- "model": "computercraft:block/monitor_advanced_lud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=ru": { "model": "computercraft:block/monitor_advanced_ru", "x": 90 },
- "facing=south,orientation=down,state=ru": {
- "model": "computercraft:block/monitor_advanced_ru", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=ru": {
- "model": "computercraft:block/monitor_advanced_ru", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=ru": {
- "model": "computercraft:block/monitor_advanced_ru", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lru": { "model": "computercraft:block/monitor_advanced_lru", "x": 90 },
- "facing=south,orientation=down,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lru": {
- "model": "computercraft:block/monitor_advanced_lru", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lu": { "model": "computercraft:block/monitor_advanced_lu", "x": 90 },
- "facing=south,orientation=down,state=lu": {
- "model": "computercraft:block/monitor_advanced_lu", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lu": {
- "model": "computercraft:block/monitor_advanced_lu", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lu": {
- "model": "computercraft:block/monitor_advanced_lu", "y": 90, "x": 90
- }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/monitor_normal.json b/src/main/resources/assets/computercraft/blockstates/monitor_normal.json
deleted file mode 100644
index b14a45a21..000000000
--- a/src/main/resources/assets/computercraft/blockstates/monitor_normal.json
+++ /dev/null
@@ -1,296 +0,0 @@
-{
- "variants": {
- "facing=north,orientation=north,state=none": { "model": "computercraft:block/monitor_normal" },
- "facing=south,orientation=north,state=none": { "model": "computercraft:block/monitor_normal", "y": 180 },
- "facing=west,orientation=north,state=none": { "model": "computercraft:block/monitor_normal", "y": 270 },
- "facing=east,orientation=north,state=none": { "model": "computercraft:block/monitor_normal", "y": 90 },
- "facing=north,orientation=north,state=r": { "model": "computercraft:block/monitor_normal_r" },
- "facing=south,orientation=north,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 180 },
- "facing=west,orientation=north,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 270 },
- "facing=east,orientation=north,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 90 },
- "facing=north,orientation=north,state=lr": { "model": "computercraft:block/monitor_normal_lr" },
- "facing=south,orientation=north,state=lr": { "model": "computercraft:block/monitor_normal_lr", "y": 180 },
- "facing=west,orientation=north,state=lr": { "model": "computercraft:block/monitor_normal_lr", "y": 270 },
- "facing=east,orientation=north,state=lr": { "model": "computercraft:block/monitor_normal_lr", "y": 90 },
- "facing=north,orientation=north,state=l": { "model": "computercraft:block/monitor_normal_l" },
- "facing=south,orientation=north,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 180 },
- "facing=west,orientation=north,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 270 },
- "facing=east,orientation=north,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 90 },
- "facing=north,orientation=north,state=d": { "model": "computercraft:block/monitor_normal_d" },
- "facing=south,orientation=north,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 180 },
- "facing=west,orientation=north,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 270 },
- "facing=east,orientation=north,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 90 },
- "facing=north,orientation=north,state=ud": { "model": "computercraft:block/monitor_normal_ud" },
- "facing=south,orientation=north,state=ud": { "model": "computercraft:block/monitor_normal_ud", "y": 180 },
- "facing=west,orientation=north,state=ud": { "model": "computercraft:block/monitor_normal_ud", "y": 270 },
- "facing=east,orientation=north,state=ud": { "model": "computercraft:block/monitor_normal_ud", "y": 90 },
- "facing=north,orientation=north,state=u": { "model": "computercraft:block/monitor_normal_u" },
- "facing=south,orientation=north,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 180 },
- "facing=west,orientation=north,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 270 },
- "facing=east,orientation=north,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 90 },
- "facing=north,orientation=north,state=rd": { "model": "computercraft:block/monitor_normal_rd" },
- "facing=south,orientation=north,state=rd": { "model": "computercraft:block/monitor_normal_rd", "y": 180 },
- "facing=west,orientation=north,state=rd": { "model": "computercraft:block/monitor_normal_rd", "y": 270 },
- "facing=east,orientation=north,state=rd": { "model": "computercraft:block/monitor_normal_rd", "y": 90 },
- "facing=north,orientation=north,state=lrd": { "model": "computercraft:block/monitor_normal_lrd" },
- "facing=south,orientation=north,state=lrd": { "model": "computercraft:block/monitor_normal_lrd", "y": 180 },
- "facing=west,orientation=north,state=lrd": { "model": "computercraft:block/monitor_normal_lrd", "y": 270 },
- "facing=east,orientation=north,state=lrd": { "model": "computercraft:block/monitor_normal_lrd", "y": 90 },
- "facing=north,orientation=north,state=ld": { "model": "computercraft:block/monitor_normal_ld" },
- "facing=south,orientation=north,state=ld": { "model": "computercraft:block/monitor_normal_ld", "y": 180 },
- "facing=west,orientation=north,state=ld": { "model": "computercraft:block/monitor_normal_ld", "y": 270 },
- "facing=east,orientation=north,state=ld": { "model": "computercraft:block/monitor_normal_ld", "y": 90 },
- "facing=north,orientation=north,state=rud": { "model": "computercraft:block/monitor_normal_rud" },
- "facing=south,orientation=north,state=rud": { "model": "computercraft:block/monitor_normal_rud", "y": 180 },
- "facing=west,orientation=north,state=rud": { "model": "computercraft:block/monitor_normal_rud", "y": 270 },
- "facing=east,orientation=north,state=rud": { "model": "computercraft:block/monitor_normal_rud", "y": 90 },
- "facing=north,orientation=north,state=lrud": { "model": "computercraft:block/monitor_normal_lrud" },
- "facing=south,orientation=north,state=lrud": { "model": "computercraft:block/monitor_normal_lrud", "y": 180 },
- "facing=west,orientation=north,state=lrud": { "model": "computercraft:block/monitor_normal_lrud", "y": 270 },
- "facing=east,orientation=north,state=lrud": { "model": "computercraft:block/monitor_normal_lrud", "y": 90 },
- "facing=north,orientation=north,state=lud": { "model": "computercraft:block/monitor_normal_lud" },
- "facing=south,orientation=north,state=lud": { "model": "computercraft:block/monitor_normal_lud", "y": 180 },
- "facing=west,orientation=north,state=lud": { "model": "computercraft:block/monitor_normal_lud", "y": 270 },
- "facing=east,orientation=north,state=lud": { "model": "computercraft:block/monitor_normal_lud", "y": 90 },
- "facing=north,orientation=north,state=ru": { "model": "computercraft:block/monitor_normal_ru" },
- "facing=south,orientation=north,state=ru": { "model": "computercraft:block/monitor_normal_ru", "y": 180 },
- "facing=west,orientation=north,state=ru": { "model": "computercraft:block/monitor_normal_ru", "y": 270 },
- "facing=east,orientation=north,state=ru": { "model": "computercraft:block/monitor_normal_ru", "y": 90 },
- "facing=north,orientation=north,state=lru": { "model": "computercraft:block/monitor_normal_lru" },
- "facing=south,orientation=north,state=lru": { "model": "computercraft:block/monitor_normal_lru", "y": 180 },
- "facing=west,orientation=north,state=lru": { "model": "computercraft:block/monitor_normal_lru", "y": 270 },
- "facing=east,orientation=north,state=lru": { "model": "computercraft:block/monitor_normal_lru", "y": 90 },
- "facing=north,orientation=north,state=lu": { "model": "computercraft:block/monitor_normal_lu" },
- "facing=south,orientation=north,state=lu": { "model": "computercraft:block/monitor_normal_lu", "y": 180 },
- "facing=west,orientation=north,state=lu": { "model": "computercraft:block/monitor_normal_lu", "y": 270 },
- "facing=east,orientation=north,state=lu": { "model": "computercraft:block/monitor_normal_lu", "y": 90 },
-
- "facing=north,orientation=up,state=none": { "model": "computercraft:block/monitor_normal", "x": 270 },
- "facing=south,orientation=up,state=none": { "model": "computercraft:block/monitor_normal", "y": 180, "x": 270 },
- "facing=west,orientation=up,state=none": { "model": "computercraft:block/monitor_normal", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=none": { "model": "computercraft:block/monitor_normal", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=r": { "model": "computercraft:block/monitor_normal_r", "x": 270 },
- "facing=south,orientation=up,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 180, "x": 270 },
- "facing=west,orientation=up,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=lr": { "model": "computercraft:block/monitor_normal_lr", "x": 270 },
- "facing=south,orientation=up,state=lr": {
- "model": "computercraft:block/monitor_normal_lr", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lr": { "model": "computercraft:block/monitor_normal_lr", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=lr": { "model": "computercraft:block/monitor_normal_lr", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=l": { "model": "computercraft:block/monitor_normal_l", "x": 270 },
- "facing=south,orientation=up,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 180, "x": 270 },
- "facing=west,orientation=up,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=d": { "model": "computercraft:block/monitor_normal_d", "x": 270 },
- "facing=south,orientation=up,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 180, "x": 270 },
- "facing=west,orientation=up,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=ud": { "model": "computercraft:block/monitor_normal_ud", "x": 270 },
- "facing=south,orientation=up,state=ud": {
- "model": "computercraft:block/monitor_normal_ud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=ud": { "model": "computercraft:block/monitor_normal_ud", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=ud": { "model": "computercraft:block/monitor_normal_ud", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=u": { "model": "computercraft:block/monitor_normal_u", "x": 270 },
- "facing=south,orientation=up,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 180, "x": 270 },
- "facing=west,orientation=up,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=rd": { "model": "computercraft:block/monitor_normal_rd", "x": 270 },
- "facing=south,orientation=up,state=rd": {
- "model": "computercraft:block/monitor_normal_rd", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=rd": { "model": "computercraft:block/monitor_normal_rd", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=rd": { "model": "computercraft:block/monitor_normal_rd", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=lrd": { "model": "computercraft:block/monitor_normal_lrd", "x": 270 },
- "facing=south,orientation=up,state=lrd": {
- "model": "computercraft:block/monitor_normal_lrd", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lrd": {
- "model": "computercraft:block/monitor_normal_lrd", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lrd": {
- "model": "computercraft:block/monitor_normal_lrd", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=ld": { "model": "computercraft:block/monitor_normal_ld", "x": 270 },
- "facing=south,orientation=up,state=ld": {
- "model": "computercraft:block/monitor_normal_ld", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=ld": { "model": "computercraft:block/monitor_normal_ld", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=ld": { "model": "computercraft:block/monitor_normal_ld", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=rud": { "model": "computercraft:block/monitor_normal_rud", "x": 270 },
- "facing=south,orientation=up,state=rud": {
- "model": "computercraft:block/monitor_normal_rud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=rud": {
- "model": "computercraft:block/monitor_normal_rud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=rud": {
- "model": "computercraft:block/monitor_normal_rud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lrud": { "model": "computercraft:block/monitor_normal_lrud", "x": 270 },
- "facing=south,orientation=up,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lud": { "model": "computercraft:block/monitor_normal_lud", "x": 270 },
- "facing=south,orientation=up,state=lud": {
- "model": "computercraft:block/monitor_normal_lud", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lud": {
- "model": "computercraft:block/monitor_normal_lud", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lud": {
- "model": "computercraft:block/monitor_normal_lud", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=ru": { "model": "computercraft:block/monitor_normal_ru", "x": 270 },
- "facing=south,orientation=up,state=ru": {
- "model": "computercraft:block/monitor_normal_ru", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=ru": { "model": "computercraft:block/monitor_normal_ru", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=ru": { "model": "computercraft:block/monitor_normal_ru", "y": 90, "x": 270 },
- "facing=north,orientation=up,state=lru": { "model": "computercraft:block/monitor_normal_lru", "x": 270 },
- "facing=south,orientation=up,state=lru": {
- "model": "computercraft:block/monitor_normal_lru", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lru": {
- "model": "computercraft:block/monitor_normal_lru", "y": 270, "x": 270
- },
- "facing=east,orientation=up,state=lru": {
- "model": "computercraft:block/monitor_normal_lru", "y": 90, "x": 270
- },
- "facing=north,orientation=up,state=lu": { "model": "computercraft:block/monitor_normal_lu", "x": 270 },
- "facing=south,orientation=up,state=lu": {
- "model": "computercraft:block/monitor_normal_lu", "y": 180, "x": 270
- },
- "facing=west,orientation=up,state=lu": { "model": "computercraft:block/monitor_normal_lu", "y": 270, "x": 270 },
- "facing=east,orientation=up,state=lu": { "model": "computercraft:block/monitor_normal_lu", "y": 90, "x": 270 },
-
- "facing=north,orientation=down,state=none": { "model": "computercraft:block/monitor_normal", "x": 90 },
- "facing=south,orientation=down,state=none": {
- "model": "computercraft:block/monitor_normal", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=none": { "model": "computercraft:block/monitor_normal", "y": 270, "x": 90 },
- "facing=east,orientation=down,state=none": { "model": "computercraft:block/monitor_normal", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=r": { "model": "computercraft:block/monitor_normal_r", "x": 90 },
- "facing=south,orientation=down,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 180, "x": 90 },
- "facing=west,orientation=down,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 270, "x": 90 },
- "facing=east,orientation=down,state=r": { "model": "computercraft:block/monitor_normal_r", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=lr": { "model": "computercraft:block/monitor_normal_lr", "x": 90 },
- "facing=south,orientation=down,state=lr": {
- "model": "computercraft:block/monitor_normal_lr", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lr": {
- "model": "computercraft:block/monitor_normal_lr", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lr": { "model": "computercraft:block/monitor_normal_lr", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=l": { "model": "computercraft:block/monitor_normal_l", "x": 90 },
- "facing=south,orientation=down,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 180, "x": 90 },
- "facing=west,orientation=down,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 270, "x": 90 },
- "facing=east,orientation=down,state=l": { "model": "computercraft:block/monitor_normal_l", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=d": { "model": "computercraft:block/monitor_normal_d", "x": 90 },
- "facing=south,orientation=down,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 180, "x": 90 },
- "facing=west,orientation=down,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 270, "x": 90 },
- "facing=east,orientation=down,state=d": { "model": "computercraft:block/monitor_normal_d", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=ud": { "model": "computercraft:block/monitor_normal_ud", "x": 90 },
- "facing=south,orientation=down,state=ud": {
- "model": "computercraft:block/monitor_normal_ud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=ud": {
- "model": "computercraft:block/monitor_normal_ud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=ud": { "model": "computercraft:block/monitor_normal_ud", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=u": { "model": "computercraft:block/monitor_normal_u", "x": 90 },
- "facing=south,orientation=down,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 180, "x": 90 },
- "facing=west,orientation=down,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 270, "x": 90 },
- "facing=east,orientation=down,state=u": { "model": "computercraft:block/monitor_normal_u", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=rd": { "model": "computercraft:block/monitor_normal_rd", "x": 90 },
- "facing=south,orientation=down,state=rd": {
- "model": "computercraft:block/monitor_normal_rd", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=rd": {
- "model": "computercraft:block/monitor_normal_rd", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=rd": { "model": "computercraft:block/monitor_normal_rd", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=lrd": { "model": "computercraft:block/monitor_normal_lrd", "x": 90 },
- "facing=south,orientation=down,state=lrd": {
- "model": "computercraft:block/monitor_normal_lrd", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lrd": {
- "model": "computercraft:block/monitor_normal_lrd", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lrd": {
- "model": "computercraft:block/monitor_normal_lrd", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=ld": { "model": "computercraft:block/monitor_normal_ld", "x": 90 },
- "facing=south,orientation=down,state=ld": {
- "model": "computercraft:block/monitor_normal_ld", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=ld": {
- "model": "computercraft:block/monitor_normal_ld", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=ld": { "model": "computercraft:block/monitor_normal_ld", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=rud": { "model": "computercraft:block/monitor_normal_rud", "x": 90 },
- "facing=south,orientation=down,state=rud": {
- "model": "computercraft:block/monitor_normal_rud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=rud": {
- "model": "computercraft:block/monitor_normal_rud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=rud": {
- "model": "computercraft:block/monitor_normal_rud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lrud": { "model": "computercraft:block/monitor_normal_lrud", "x": 90 },
- "facing=south,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lrud": {
- "model": "computercraft:block/monitor_normal_lrud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lud": { "model": "computercraft:block/monitor_normal_lud", "x": 90 },
- "facing=south,orientation=down,state=lud": {
- "model": "computercraft:block/monitor_normal_lud", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lud": {
- "model": "computercraft:block/monitor_normal_lud", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lud": {
- "model": "computercraft:block/monitor_normal_lud", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=ru": { "model": "computercraft:block/monitor_normal_ru", "x": 90 },
- "facing=south,orientation=down,state=ru": {
- "model": "computercraft:block/monitor_normal_ru", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=ru": {
- "model": "computercraft:block/monitor_normal_ru", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=ru": { "model": "computercraft:block/monitor_normal_ru", "y": 90, "x": 90 },
- "facing=north,orientation=down,state=lru": { "model": "computercraft:block/monitor_normal_lru", "x": 90 },
- "facing=south,orientation=down,state=lru": {
- "model": "computercraft:block/monitor_normal_lru", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lru": {
- "model": "computercraft:block/monitor_normal_lru", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lru": {
- "model": "computercraft:block/monitor_normal_lru", "y": 90, "x": 90
- },
- "facing=north,orientation=down,state=lu": { "model": "computercraft:block/monitor_normal_lu", "x": 90 },
- "facing=south,orientation=down,state=lu": {
- "model": "computercraft:block/monitor_normal_lu", "y": 180, "x": 90
- },
- "facing=west,orientation=down,state=lu": {
- "model": "computercraft:block/monitor_normal_lu", "y": 270, "x": 90
- },
- "facing=east,orientation=down,state=lu": { "model": "computercraft:block/monitor_normal_lu", "y": 90, "x": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/speaker.json b/src/main/resources/assets/computercraft/blockstates/speaker.json
deleted file mode 100644
index 4b3935a54..000000000
--- a/src/main/resources/assets/computercraft/blockstates/speaker.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "variants": {
- "facing=north": { "model": "computercraft:block/speaker" },
- "facing=south": { "model": "computercraft:block/speaker", "y": 180 },
- "facing=west": { "model": "computercraft:block/speaker", "y": 270 },
- "facing=east": { "model": "computercraft:block/speaker", "y": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/wired_modem_full.json b/src/main/resources/assets/computercraft/blockstates/wired_modem_full.json
deleted file mode 100644
index ac068443f..000000000
--- a/src/main/resources/assets/computercraft/blockstates/wired_modem_full.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "variants": {
- "modem=false,peripheral=false": { "model": "computercraft:block/wired_modem_full_off" },
- "modem=false,peripheral=true": { "model": "computercraft:block/wired_modem_full_off_peripheral" },
- "modem=true,peripheral=false": { "model": "computercraft:block/wired_modem_full_on" },
- "modem=true,peripheral=true": { "model": "computercraft:block/wired_modem_full_on_peripheral" }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/wireless_modem_advanced.json b/src/main/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
deleted file mode 100644
index 375ca9646..000000000
--- a/src/main/resources/assets/computercraft/blockstates/wireless_modem_advanced.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "variants": {
- "facing=up,on=false": { "model": "computercraft:block/wireless_modem_advanced_off", "x": 270 },
- "facing=down,on=false": { "model": "computercraft:block/wireless_modem_advanced_off", "x": 90 },
- "facing=north,on=false": { "model": "computercraft:block/wireless_modem_advanced_off" },
- "facing=south,on=false": { "model": "computercraft:block/wireless_modem_advanced_off", "y": 180 },
- "facing=west,on=false": { "model": "computercraft:block/wireless_modem_advanced_off", "y": 270 },
- "facing=east,on=false": { "model": "computercraft:block/wireless_modem_advanced_off", "y": 90 },
-
- "facing=up,on=true": { "model": "computercraft:block/wireless_modem_advanced_on", "x": 270 },
- "facing=down,on=true": { "model": "computercraft:block/wireless_modem_advanced_on", "x": 90 },
- "facing=north,on=true": { "model": "computercraft:block/wireless_modem_advanced_on" },
- "facing=south,on=true": { "model": "computercraft:block/wireless_modem_advanced_on", "y": 180 },
- "facing=west,on=true": { "model": "computercraft:block/wireless_modem_advanced_on", "y": 270 },
- "facing=east,on=true": { "model": "computercraft:block/wireless_modem_advanced_on", "y": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/blockstates/wireless_modem_normal.json b/src/main/resources/assets/computercraft/blockstates/wireless_modem_normal.json
deleted file mode 100644
index e39561423..000000000
--- a/src/main/resources/assets/computercraft/blockstates/wireless_modem_normal.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "variants": {
- "facing=up,on=false": { "model": "computercraft:block/wireless_modem_normal_off", "x": 270 },
- "facing=down,on=false": { "model": "computercraft:block/wireless_modem_normal_off", "x": 90 },
- "facing=north,on=false": { "model": "computercraft:block/wireless_modem_normal_off" },
- "facing=south,on=false": { "model": "computercraft:block/wireless_modem_normal_off", "y": 180 },
- "facing=west,on=false": { "model": "computercraft:block/wireless_modem_normal_off", "y": 270 },
- "facing=east,on=false": { "model": "computercraft:block/wireless_modem_normal_off", "y": 90 },
-
- "facing=up,on=true": { "model": "computercraft:block/wireless_modem_normal_on", "x": 270 },
- "facing=down,on=true": { "model": "computercraft:block/wireless_modem_normal_on", "x": 90 },
- "facing=north,on=true": { "model": "computercraft:block/wireless_modem_normal_on" },
- "facing=south,on=true": { "model": "computercraft:block/wireless_modem_normal_on", "y": 180 },
- "facing=west,on=true": { "model": "computercraft:block/wireless_modem_normal_on", "y": 270 },
- "facing=east,on=true": { "model": "computercraft:block/wireless_modem_normal_on", "y": 90 }
- }
-}
diff --git a/src/main/resources/assets/computercraft/lua/rom/motd.txt b/src/main/resources/assets/computercraft/lua/rom/motd.txt
deleted file mode 100644
index 3b347b252..000000000
--- a/src/main/resources/assets/computercraft/lua/rom/motd.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-View the source code at https://github.com/cc-tweaked/cc-restitched
-View the documentation at https://wiki.computercraft.cc
-Visit the forum at https://forums.computercraft.cc
-You can disable these messages by running "set motd.enable false"
-You can create directories with "mkdir".
-Want to see hidden files? Run "set list.show_hidden true".
-Run "list" or "ls" to see all files in a directory.
-You can delete files and directories with "delete" or "rm".
-Use "pastebin put" to upload a program to pastebin.
-Use "pastebin get" to download a program from pastebin.
-Use "pastebin run" to run a program from pastebin without saving it.
-Use the "edit" program to create and edit your programs.
-You can copy files with "copy" or "cp".
diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua b/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua
deleted file mode 100644
index 57ab7b916..000000000
--- a/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua
+++ /dev/null
@@ -1,24 +0,0 @@
-local date = os.date("*t")
-if date.month == 1 and date.day == 1 then
- print("Happy new year!")
-elseif date.month == 12 and date.day == 24 then
- print("Merry X-mas!")
-elseif date.month == 10 and date.day == 31 then
- print("OOoooOOOoooo! Spooky!")
-else
- local tMotd = {}
-
- for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do
- if fs.exists(sPath) then
- for sLine in io.lines(sPath) do
- table.insert(tMotd, sLine)
- end
- end
- end
-
- if #tMotd == 0 then
- print("missingno")
- else
- print(tMotd[math.random(1, #tMotd)])
- end
-end
diff --git a/src/main/resources/assets/computercraft/models/block/computer_advanced_blinking.json b/src/main/resources/assets/computercraft/models/block/computer_advanced_blinking.json
deleted file mode 100644
index ed4c9bec3..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_advanced_blinking.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_advanced_front_blink",
- "side": "computercraft:block/computer_advanced_side",
- "top": "computercraft:block/computer_advanced_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_advanced_off.json b/src/main/resources/assets/computercraft/models/block/computer_advanced_off.json
deleted file mode 100644
index 8631c88ed..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_advanced_off.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_advanced_front",
- "side": "computercraft:block/computer_advanced_side",
- "top": "computercraft:block/computer_advanced_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_advanced_on.json b/src/main/resources/assets/computercraft/models/block/computer_advanced_on.json
deleted file mode 100644
index 2692aab84..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_advanced_on.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_advanced_front_on",
- "side": "computercraft:block/computer_advanced_side",
- "top": "computercraft:block/computer_advanced_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_command_blinking.json b/src/main/resources/assets/computercraft/models/block/computer_command_blinking.json
deleted file mode 100644
index 09d7ac1b3..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_command_blinking.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_command_front_blink",
- "side": "computercraft:block/computer_command_side",
- "top": "computercraft:block/computer_command_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_command_off.json b/src/main/resources/assets/computercraft/models/block/computer_command_off.json
deleted file mode 100644
index eaa4970b9..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_command_off.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_command_front",
- "side": "computercraft:block/computer_command_side",
- "top": "computercraft:block/computer_command_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_command_on.json b/src/main/resources/assets/computercraft/models/block/computer_command_on.json
deleted file mode 100644
index 7d4730140..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_command_on.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_command_front_on",
- "side": "computercraft:block/computer_command_side",
- "top": "computercraft:block/computer_command_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_normal_blinking.json b/src/main/resources/assets/computercraft/models/block/computer_normal_blinking.json
deleted file mode 100644
index d6d70f696..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_normal_blinking.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_normal_front_blink",
- "side": "computercraft:block/computer_normal_side",
- "top": "computercraft:block/computer_normal_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_normal_off.json b/src/main/resources/assets/computercraft/models/block/computer_normal_off.json
deleted file mode 100644
index 5d200f816..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_normal_off.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_normal_front",
- "side": "computercraft:block/computer_normal_side",
- "top": "computercraft:block/computer_normal_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/computer_normal_on.json b/src/main/resources/assets/computercraft/models/block/computer_normal_on.json
deleted file mode 100644
index f434d7fa8..000000000
--- a/src/main/resources/assets/computercraft/models/block/computer_normal_on.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/computer_normal_front_on",
- "side": "computercraft:block/computer_normal_side",
- "top": "computercraft:block/computer_normal_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced.json
deleted file mode 100644
index 3ff0ed5dc..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_16",
- "side": "computercraft:block/monitor_advanced_4",
- "top": "computercraft:block/monitor_advanced_0",
- "back": "computercraft:block/monitor_advanced_4"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_d.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_d.json
deleted file mode 100644
index d8936956d..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_d.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_20",
- "side": "computercraft:block/monitor_advanced_36",
- "top": "computercraft:block/monitor_advanced_0",
- "back": "computercraft:block/monitor_advanced_36"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_item.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_item.json
deleted file mode 100644
index 9420c1350..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_item.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/monitor_advanced_15",
- "side": "computercraft:block/monitor_advanced_4",
- "top": "computercraft:block/monitor_advanced_0"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_l.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_l.json
deleted file mode 100644
index 3abee2204..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_l.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_19",
- "side": "computercraft:block/monitor_advanced_4",
- "top": "computercraft:block/monitor_advanced_1",
- "back": "computercraft:block/monitor_advanced_33"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_ld.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_ld.json
deleted file mode 100644
index ee054dbc8..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_ld.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_31",
- "side": "computercraft:block/monitor_advanced_7",
- "top": "computercraft:block/monitor_advanced_1",
- "back": "computercraft:block/monitor_advanced_45"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lr.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_lr.json
deleted file mode 100644
index 320c8d354..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lr.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_18",
- "side": "computercraft:block/monitor_advanced_4",
- "top": "computercraft:block/monitor_advanced_2",
- "back": "computercraft:block/monitor_advanced_34"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lrd.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_lrd.json
deleted file mode 100644
index 5f7a43407..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lrd.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_30",
- "side": "computercraft:block/monitor_advanced_7",
- "top": "computercraft:block/monitor_advanced_2",
- "back": "computercraft:block/monitor_advanced_46"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lru.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_lru.json
deleted file mode 100644
index 56acdd077..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lru.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_24",
- "side": "computercraft:block/monitor_advanced_38",
- "top": "computercraft:block/monitor_advanced_2",
- "back": "computercraft:block/monitor_advanced_40"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lrud.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_lrud.json
deleted file mode 100644
index dce3c2baf..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lrud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_27",
- "side": "computercraft:block/monitor_advanced_37",
- "top": "computercraft:block/monitor_advanced_2",
- "back": "computercraft:block/monitor_advanced_43"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lu.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_lu.json
deleted file mode 100644
index 905ebdce3..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lu.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_25",
- "side": "computercraft:block/monitor_advanced_38",
- "top": "computercraft:block/monitor_advanced_1",
- "back": "computercraft:block/monitor_advanced_39"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lud.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_lud.json
deleted file mode 100644
index 43b110c03..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_lud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_28",
- "side": "computercraft:block/monitor_advanced_37",
- "top": "computercraft:block/monitor_advanced_1",
- "back": "computercraft:block/monitor_advanced_42"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_r.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_r.json
deleted file mode 100644
index 03d1d386f..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_r.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_17",
- "side": "computercraft:block/monitor_advanced_4",
- "top": "computercraft:block/monitor_advanced_3",
- "back": "computercraft:block/monitor_advanced_35"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_rd.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_rd.json
deleted file mode 100644
index 4ded80dba..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_rd.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_29",
- "side": "computercraft:block/monitor_advanced_7",
- "top": "computercraft:block/monitor_advanced_3",
- "back": "computercraft:block/monitor_advanced_47"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_ru.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_ru.json
deleted file mode 100644
index 67cd36920..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_ru.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_23",
- "side": "computercraft:block/monitor_advanced_38",
- "top": "computercraft:block/monitor_advanced_3",
- "back": "computercraft:block/monitor_advanced_41"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_rud.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_rud.json
deleted file mode 100644
index 0573ad049..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_rud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_26",
- "side": "computercraft:block/monitor_advanced_37",
- "top": "computercraft:block/monitor_advanced_3",
- "back": "computercraft:block/monitor_advanced_44"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_u.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_u.json
deleted file mode 100644
index bdc9f7a2d..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_u.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_22",
- "side": "computercraft:block/monitor_advanced_38",
- "top": "computercraft:block/monitor_advanced_0",
- "back": "computercraft:block/monitor_advanced_38"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_advanced_ud.json b/src/main/resources/assets/computercraft/models/block/monitor_advanced_ud.json
deleted file mode 100644
index 3987b109f..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_advanced_ud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_advanced_21",
- "side": "computercraft:block/monitor_advanced_37",
- "top": "computercraft:block/monitor_advanced_0",
- "back": "computercraft:block/monitor_advanced_37"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal.json b/src/main/resources/assets/computercraft/models/block/monitor_normal.json
deleted file mode 100644
index e7a669695..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_16",
- "side": "computercraft:block/monitor_normal_4",
- "top": "computercraft:block/monitor_normal_0",
- "back": "computercraft:block/monitor_normal_4"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_d.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_d.json
deleted file mode 100644
index ab3cf8bf4..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_d.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_20",
- "side": "computercraft:block/monitor_normal_36",
- "top": "computercraft:block/monitor_normal_0",
- "back": "computercraft:block/monitor_normal_36"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_item.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_item.json
deleted file mode 100644
index a8f01f314..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_item.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/monitor_normal_15",
- "side": "computercraft:block/monitor_normal_4",
- "top": "computercraft:block/monitor_normal_0"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_l.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_l.json
deleted file mode 100644
index def167210..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_l.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_19",
- "side": "computercraft:block/monitor_normal_4",
- "top": "computercraft:block/monitor_normal_1",
- "back": "computercraft:block/monitor_normal_33"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_ld.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_ld.json
deleted file mode 100644
index 410d65029..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_ld.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_31",
- "side": "computercraft:block/monitor_normal_7",
- "top": "computercraft:block/monitor_normal_1",
- "back": "computercraft:block/monitor_normal_45"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_lr.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_lr.json
deleted file mode 100644
index 36243944d..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_lr.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_18",
- "side": "computercraft:block/monitor_normal_4",
- "top": "computercraft:block/monitor_normal_2",
- "back": "computercraft:block/monitor_normal_34"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_lrd.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_lrd.json
deleted file mode 100644
index 1fe673561..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_lrd.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_30",
- "side": "computercraft:block/monitor_normal_7",
- "top": "computercraft:block/monitor_normal_2",
- "back": "computercraft:block/monitor_normal_46"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_lru.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_lru.json
deleted file mode 100644
index d6c5b2ce4..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_lru.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_24",
- "side": "computercraft:block/monitor_normal_38",
- "top": "computercraft:block/monitor_normal_2",
- "back": "computercraft:block/monitor_normal_40"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_lrud.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_lrud.json
deleted file mode 100644
index ba96ee76d..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_lrud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_27",
- "side": "computercraft:block/monitor_normal_37",
- "top": "computercraft:block/monitor_normal_2",
- "back": "computercraft:block/monitor_normal_43"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_lu.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_lu.json
deleted file mode 100644
index 444408d25..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_lu.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_25",
- "side": "computercraft:block/monitor_normal_38",
- "top": "computercraft:block/monitor_normal_1",
- "back": "computercraft:block/monitor_normal_39"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_lud.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_lud.json
deleted file mode 100644
index 28f06d420..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_lud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_28",
- "side": "computercraft:block/monitor_normal_37",
- "top": "computercraft:block/monitor_normal_1",
- "back": "computercraft:block/monitor_normal_42"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_r.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_r.json
deleted file mode 100644
index 8b8eb4df1..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_r.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_17",
- "side": "computercraft:block/monitor_normal_4",
- "top": "computercraft:block/monitor_normal_3",
- "back": "computercraft:block/monitor_normal_35"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_rd.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_rd.json
deleted file mode 100644
index 61d7d1231..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_rd.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_29",
- "side": "computercraft:block/monitor_normal_7",
- "top": "computercraft:block/monitor_normal_3",
- "back": "computercraft:block/monitor_normal_47"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_ru.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_ru.json
deleted file mode 100644
index ddb474736..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_ru.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_23",
- "side": "computercraft:block/monitor_normal_38",
- "top": "computercraft:block/monitor_normal_3",
- "back": "computercraft:block/monitor_normal_41"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_rud.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_rud.json
deleted file mode 100644
index a8f272995..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_rud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_26",
- "side": "computercraft:block/monitor_normal_37",
- "top": "computercraft:block/monitor_normal_3",
- "back": "computercraft:block/monitor_normal_44"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_u.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_u.json
deleted file mode 100644
index c7d67619b..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_u.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_22",
- "side": "computercraft:block/monitor_normal_38",
- "top": "computercraft:block/monitor_normal_0",
- "back": "computercraft:block/monitor_normal_38"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/monitor_normal_ud.json b/src/main/resources/assets/computercraft/models/block/monitor_normal_ud.json
deleted file mode 100644
index 0cbadcc4b..000000000
--- a/src/main/resources/assets/computercraft/models/block/monitor_normal_ud.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "parent": "computercraft:block/monitor_base",
- "textures": {
- "front": "computercraft:block/monitor_normal_21",
- "side": "computercraft:block/monitor_normal_37",
- "top": "computercraft:block/monitor_normal_0",
- "back": "computercraft:block/monitor_normal_37"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/speaker.json b/src/main/resources/assets/computercraft/models/block/speaker.json
deleted file mode 100644
index a2f45c10a..000000000
--- a/src/main/resources/assets/computercraft/models/block/speaker.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "parent": "block/orientable",
- "textures": {
- "front": "computercraft:block/speaker_front",
- "side": "computercraft:block/speaker_side",
- "top": "computercraft:block/speaker_top"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_full_off.json b/src/main/resources/assets/computercraft/models/block/wired_modem_full_off.json
deleted file mode 100644
index d6d2e5303..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_full_off.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "parent": "block/cube_all",
- "textures": {
- "all": "computercraft:block/wired_modem_face"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_full_off_peripheral.json b/src/main/resources/assets/computercraft/models/block/wired_modem_full_off_peripheral.json
deleted file mode 100644
index 0c5c0e50c..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_full_off_peripheral.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "parent": "block/cube_all",
- "textures": {
- "all": "computercraft:block/wired_modem_face_peripheral"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_full_on.json b/src/main/resources/assets/computercraft/models/block/wired_modem_full_on.json
deleted file mode 100644
index c36e45cff..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_full_on.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "parent": "block/cube_all",
- "textures": {
- "all": "computercraft:block/wired_modem_face_on"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_full_on_peripheral.json b/src/main/resources/assets/computercraft/models/block/wired_modem_full_on_peripheral.json
deleted file mode 100644
index e4fb9858b..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_full_on_peripheral.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "parent": "block/cube_all",
- "textures": {
- "all": "computercraft:block/wired_modem_face_peripheral_on"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_off.json b/src/main/resources/assets/computercraft/models/block/wired_modem_off.json
deleted file mode 100644
index 9c500700d..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_off.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wired_modem_face",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_off_peripheral.json b/src/main/resources/assets/computercraft/models/block/wired_modem_off_peripheral.json
deleted file mode 100644
index 9532df960..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_off_peripheral.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wired_modem_face_peripheral",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_on.json b/src/main/resources/assets/computercraft/models/block/wired_modem_on.json
deleted file mode 100644
index f69937299..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_on.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wired_modem_face_on",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wired_modem_on_peripheral.json b/src/main/resources/assets/computercraft/models/block/wired_modem_on_peripheral.json
deleted file mode 100644
index 22cccf90b..000000000
--- a/src/main/resources/assets/computercraft/models/block/wired_modem_on_peripheral.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wired_modem_face_peripheral_on",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wireless_modem_advanced_off.json b/src/main/resources/assets/computercraft/models/block/wireless_modem_advanced_off.json
deleted file mode 100644
index a818ba19a..000000000
--- a/src/main/resources/assets/computercraft/models/block/wireless_modem_advanced_off.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wireless_modem_advanced_face",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wireless_modem_advanced_on.json b/src/main/resources/assets/computercraft/models/block/wireless_modem_advanced_on.json
deleted file mode 100644
index 7327fa44a..000000000
--- a/src/main/resources/assets/computercraft/models/block/wireless_modem_advanced_on.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wireless_modem_advanced_face_on",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wireless_modem_normal_off.json b/src/main/resources/assets/computercraft/models/block/wireless_modem_normal_off.json
deleted file mode 100644
index 2ac06bfd5..000000000
--- a/src/main/resources/assets/computercraft/models/block/wireless_modem_normal_off.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wireless_modem_normal_face",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/block/wireless_modem_normal_on.json b/src/main/resources/assets/computercraft/models/block/wireless_modem_normal_on.json
deleted file mode 100644
index 0b0697741..000000000
--- a/src/main/resources/assets/computercraft/models/block/wireless_modem_normal_on.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "parent": "computercraft:block/modem",
- "textures": {
- "front": "computercraft:block/wireless_modem_normal_face_on",
- "back": "computercraft:block/modem_back"
- }
-}
diff --git a/src/main/resources/assets/computercraft/models/item/computer_advanced.json b/src/main/resources/assets/computercraft/models/item/computer_advanced.json
deleted file mode 100644
index bbaf95089..000000000
--- a/src/main/resources/assets/computercraft/models/item/computer_advanced.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/computer_advanced_blinking"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/computer_command.json b/src/main/resources/assets/computercraft/models/item/computer_command.json
deleted file mode 100644
index c0df0ddd6..000000000
--- a/src/main/resources/assets/computercraft/models/item/computer_command.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/computer_command_blinking"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/computer_normal.json b/src/main/resources/assets/computercraft/models/item/computer_normal.json
deleted file mode 100644
index bf906f744..000000000
--- a/src/main/resources/assets/computercraft/models/item/computer_normal.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/computer_normal_blinking"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/monitor_advanced.json b/src/main/resources/assets/computercraft/models/item/monitor_advanced.json
deleted file mode 100644
index 52ff045f8..000000000
--- a/src/main/resources/assets/computercraft/models/item/monitor_advanced.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/monitor_advanced_item"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/monitor_normal.json b/src/main/resources/assets/computercraft/models/item/monitor_normal.json
deleted file mode 100644
index 29079e6b3..000000000
--- a/src/main/resources/assets/computercraft/models/item/monitor_normal.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/monitor_normal_item"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/speaker.json b/src/main/resources/assets/computercraft/models/item/speaker.json
deleted file mode 100644
index 3aed864e9..000000000
--- a/src/main/resources/assets/computercraft/models/item/speaker.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/speaker"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/wired_modem.json b/src/main/resources/assets/computercraft/models/item/wired_modem.json
deleted file mode 100644
index a1d809c37..000000000
--- a/src/main/resources/assets/computercraft/models/item/wired_modem.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/wired_modem_off"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/wired_modem_full.json b/src/main/resources/assets/computercraft/models/item/wired_modem_full.json
deleted file mode 100644
index 77237e07a..000000000
--- a/src/main/resources/assets/computercraft/models/item/wired_modem_full.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/wired_modem_full_off"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/wireless_modem_advanced.json b/src/main/resources/assets/computercraft/models/item/wireless_modem_advanced.json
deleted file mode 100644
index 801d0dd42..000000000
--- a/src/main/resources/assets/computercraft/models/item/wireless_modem_advanced.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/wireless_modem_advanced_off"
-}
diff --git a/src/main/resources/assets/computercraft/models/item/wireless_modem_normal.json b/src/main/resources/assets/computercraft/models/item/wireless_modem_normal.json
deleted file mode 100644
index bf403a943..000000000
--- a/src/main/resources/assets/computercraft/models/item/wireless_modem_normal.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "computercraft:block/wireless_modem_normal_off"
-}
diff --git a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh
index f89635ca5..c1e089fed 100644
--- a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh
+++ b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh
@@ -1,17 +1,31 @@
#version 150
+#moj_import
+
#define FONT_WIDTH 6.0
#define FONT_HEIGHT 9.0
uniform sampler2D Sampler0; // Font
-uniform int Width;
-uniform int Height;
uniform usamplerBuffer Tbo;
-uniform vec3 Palette[16];
-in vec2 f_pos;
+layout(std140) uniform MonitorData {
+ vec3 Palette[16];
+ int Width;
+ int Height;
+ ivec2 CursorPos;
+ int CursorColour;
+};
+uniform int CursorBlink;
+
+uniform vec4 ColorModulator;
+uniform float FogStart;
+uniform float FogEnd;
+uniform vec4 FogColor;
-out vec4 colour;
+in float vertexDistance;
+in vec2 fontPos;
+
+out vec4 fragColor;
vec2 texture_corner(int index) {
float x = 1.0 + float(index % 16) * (FONT_WIDTH + 2.0);
@@ -19,8 +33,12 @@ vec2 texture_corner(int index) {
return vec2(x, y);
}
+vec4 recolour(vec4 texture, int colour) {
+ return vec4(texture.rgb * Palette[colour], texture.rgba);
+}
+
void main() {
- vec2 term_pos = vec2(f_pos.x / FONT_WIDTH, f_pos.y / FONT_HEIGHT);
+ vec2 term_pos = vec2(fontPos.x / FONT_WIDTH, fontPos.y / FONT_HEIGHT);
vec2 corner = floor(term_pos);
ivec2 cell = ivec2(corner);
@@ -35,6 +53,14 @@ void main() {
int bg = int(texelFetch(Tbo, index + 2).r);
vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT);
- vec4 img = texture(Sampler0, (texture_corner(character) + pos) / 256.0);
- colour = vec4(mix(Palette[bg], img.rgb * Palette[fg], img.a * mult), 1.0);
+ vec4 charTex = recolour(texture(Sampler0, (texture_corner(character) + pos) / 256.0), fg);
+
+ // Applies the cursor on top of the current character if we're blinking and in the current cursor's cell. We do it
+ // this funky way to avoid branches.
+ vec4 cursorTex = recolour(texture(Sampler0, (texture_corner(95) + pos) / 256.0), CursorColour); // 95 = '_'
+ vec4 img = mix(charTex, cursorTex, cursorTex.a * float(CursorBlink) * (CursorPos == cell ? 1.0 : 0.0));
+
+ vec4 colour = vec4(mix(Palette[bg], img.rgb, img.a * mult), 1.0) * ColorModulator;
+
+ fragColor = linear_fog(colour, vertexDistance, FogStart, FogEnd, FogColor);
}
diff --git a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json
index 9196160ee..fe2320b0e 100644
--- a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json
+++ b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json
@@ -6,8 +6,14 @@
"uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
- { "name": "Width", "type": "int", "count": 1, "values": [ 1 ] },
- { "name": "Height", "type": "int", "count": 1, "values": [ 1 ] },
- { "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] }
+ { "name": "IViewRotMat", "type": "matrix3x3", "count": 9, "values": [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] },
+ { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
+ { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
+ { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },
+ { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] },
+ { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] },
+
+ { "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] },
+ { "name": "CursorBlink", "type": "int", "count": 1, "values": [ 0 ] }
]
}
diff --git a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh
index 8b673710b..885b20df5 100644
--- a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh
+++ b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh
@@ -1,14 +1,21 @@
#version 150
+#moj_import
+
in vec3 Position;
in vec2 UV0;
uniform mat4 ModelViewMat;
uniform mat4 ProjMat;
+uniform mat3 IViewRotMat;
+uniform int FogShape;
-out vec2 f_pos;
+out float vertexDistance;
+out vec2 fontPos;
void main() {
gl_Position = ProjMat * ModelViewMat * vec4(Position, 1);
- f_pos = UV0;
+
+ vertexDistance = fog_distance(ModelViewMat, IViewRotMat * Position, FogShape);
+ fontPos = UV0;
}
diff --git a/src/main/resources/assets/minecraft/shaders/core/terminal.json b/src/main/resources/assets/minecraft/shaders/core/terminal.json
deleted file mode 100644
index 2cd53c182..000000000
--- a/src/main/resources/assets/minecraft/shaders/core/terminal.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "vertex": "position_color_tex",
- "fragment": "position_color_tex",
- "attributes": [ "Position", "Color", "UV0" ],
- "samplers": [ { "name": "Sampler0" } ],
- "uniforms": [
- { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
- { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
- { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
- ]
-}
diff --git a/src/main/resources/cc.accesswidener b/src/main/resources/cc.accesswidener
index 5225f4bb7..148021c85 100644
--- a/src/main/resources/cc.accesswidener
+++ b/src/main/resources/cc.accesswidener
@@ -6,3 +6,11 @@ accessible method net/minecraft/client/renderer/RenderType create (Ljava/lang/St
# SpeakerInstance/SpeakerManager
accessible method com/mojang/blaze3d/audio/Channel pumpBuffers (I)V
accessible field net/minecraft/client/sounds/SoundEngine executor Lnet/minecraft/client/sounds/SoundEngineExecutor;
+
+# DirectVertexBuffer
+accessible field com/mojang/blaze3d/vertex/VertexBuffer vertextBufferId I
+accessible field com/mojang/blaze3d/vertex/VertexBuffer indexType Lcom/mojang/blaze3d/vertex/VertexFormat$IndexType;
+accessible field com/mojang/blaze3d/vertex/VertexBuffer indexCount I
+accessible field com/mojang/blaze3d/vertex/VertexBuffer mode Lcom/mojang/blaze3d/vertex/VertexFormat$Mode;
+accessible field com/mojang/blaze3d/vertex/VertexBuffer sequentialIndices Z
+accessible field com/mojang/blaze3d/vertex/VertexBuffer format Lcom/mojang/blaze3d/vertex/VertexFormat;
diff --git a/src/main/resources/data/c/tags/items/black_dyes.json b/src/main/resources/data/c/tags/items/black_dyes.json
deleted file mode 100644
index 26b4e622b..000000000
--- a/src/main/resources/data/c/tags/items/black_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:black_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/blue_dyes.json b/src/main/resources/data/c/tags/items/blue_dyes.json
deleted file mode 100644
index d22bad7b3..000000000
--- a/src/main/resources/data/c/tags/items/blue_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:blue_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/brown_dyes.json b/src/main/resources/data/c/tags/items/brown_dyes.json
deleted file mode 100644
index eaa513928..000000000
--- a/src/main/resources/data/c/tags/items/brown_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:brown_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/cyan_dyes.json b/src/main/resources/data/c/tags/items/cyan_dyes.json
deleted file mode 100644
index e9abb1523..000000000
--- a/src/main/resources/data/c/tags/items/cyan_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:cyan_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/ender_pearls.json b/src/main/resources/data/c/tags/items/ender_pearls.json
deleted file mode 100644
index 98e6e7034..000000000
--- a/src/main/resources/data/c/tags/items/ender_pearls.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:ender_pearl"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/glass_panes.json b/src/main/resources/data/c/tags/items/glass_panes.json
deleted file mode 100644
index 0e953d099..000000000
--- a/src/main/resources/data/c/tags/items/glass_panes.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:green_stained_glass_pane",
- "minecraft:light_blue_stained_glass_pane",
- "minecraft:gray_stained_glass_pane",
- "minecraft:red_stained_glass_pane",
- "minecraft:lime_stained_glass_pane",
- "minecraft:yellow_stained_glass_pane",
- "minecraft:blue_stained_glass_pane",
- "minecraft:pink_stained_glass_pane",
- "minecraft:light_gray_stained_glass_pane",
- "minecraft:orange_stained_glass_pane",
- "minecraft:glass_pane",
- "minecraft:purple_stained_glass_pane",
- "minecraft:white_stained_glass_pane",
- "minecraft:magenta_stained_glass_pane",
- "minecraft:brown_stained_glass_pane",
- "minecraft:black_stained_glass_pane",
- "minecraft:cyan_stained_glass_pane"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/gold_blocks.json b/src/main/resources/data/c/tags/items/gold_blocks.json
deleted file mode 100644
index c67537a25..000000000
--- a/src/main/resources/data/c/tags/items/gold_blocks.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:gold_block"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/gold_ingots.json b/src/main/resources/data/c/tags/items/gold_ingots.json
deleted file mode 100644
index d95117008..000000000
--- a/src/main/resources/data/c/tags/items/gold_ingots.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:gold_ingot"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/gray_dyes.json b/src/main/resources/data/c/tags/items/gray_dyes.json
deleted file mode 100644
index 41ac3efcb..000000000
--- a/src/main/resources/data/c/tags/items/gray_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:gray_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/green_dyes.json b/src/main/resources/data/c/tags/items/green_dyes.json
deleted file mode 100644
index 117da17a2..000000000
--- a/src/main/resources/data/c/tags/items/green_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:green_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/iron_ingots.json b/src/main/resources/data/c/tags/items/iron_ingots.json
deleted file mode 100644
index 042626f2d..000000000
--- a/src/main/resources/data/c/tags/items/iron_ingots.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:iron_ingot"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/light_blue_dyes.json b/src/main/resources/data/c/tags/items/light_blue_dyes.json
deleted file mode 100644
index 5abfe433a..000000000
--- a/src/main/resources/data/c/tags/items/light_blue_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:light_blue_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/light_gray_dyes.json b/src/main/resources/data/c/tags/items/light_gray_dyes.json
deleted file mode 100644
index 1efbe1882..000000000
--- a/src/main/resources/data/c/tags/items/light_gray_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:light_gray_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/lime_dyes.json b/src/main/resources/data/c/tags/items/lime_dyes.json
deleted file mode 100644
index a5c6cadfd..000000000
--- a/src/main/resources/data/c/tags/items/lime_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:lime_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/magenta_dyes.json b/src/main/resources/data/c/tags/items/magenta_dyes.json
deleted file mode 100644
index b53ca3614..000000000
--- a/src/main/resources/data/c/tags/items/magenta_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:magenta_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/orange_dyes.json b/src/main/resources/data/c/tags/items/orange_dyes.json
deleted file mode 100644
index ce9bdea4a..000000000
--- a/src/main/resources/data/c/tags/items/orange_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:orange_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/pink_dyes.json b/src/main/resources/data/c/tags/items/pink_dyes.json
deleted file mode 100644
index 62eab2038..000000000
--- a/src/main/resources/data/c/tags/items/pink_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:pink_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/purple_dyes.json b/src/main/resources/data/c/tags/items/purple_dyes.json
deleted file mode 100644
index a0cc925d7..000000000
--- a/src/main/resources/data/c/tags/items/purple_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:purple_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/red_dyes.json b/src/main/resources/data/c/tags/items/red_dyes.json
deleted file mode 100644
index c02a69287..000000000
--- a/src/main/resources/data/c/tags/items/red_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:red_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/redstone_dusts.json b/src/main/resources/data/c/tags/items/redstone_dusts.json
deleted file mode 100644
index 7f9d96f1a..000000000
--- a/src/main/resources/data/c/tags/items/redstone_dusts.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:redstone"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/stones.json b/src/main/resources/data/c/tags/items/stones.json
deleted file mode 100644
index 28110bfa6..000000000
--- a/src/main/resources/data/c/tags/items/stones.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:andesite",
- "minecraft:diorite",
- "minecraft:granite",
- "minecraft:infested_stone",
- "minecraft:stone",
- "minecraft:polished_andesite",
- "minecraft:polished_diorite",
- "minecraft:polished_granite"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/white_dyes.json b/src/main/resources/data/c/tags/items/white_dyes.json
deleted file mode 100644
index 59601ecf1..000000000
--- a/src/main/resources/data/c/tags/items/white_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:white_dye"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/wooden_chests.json b/src/main/resources/data/c/tags/items/wooden_chests.json
deleted file mode 100644
index 0777fa5ba..000000000
--- a/src/main/resources/data/c/tags/items/wooden_chests.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:trapped_chest",
- "minecraft:chest"
- ]
-}
diff --git a/src/main/resources/data/c/tags/items/yellow_dyes.json b/src/main/resources/data/c/tags/items/yellow_dyes.json
deleted file mode 100644
index 4d4b0c28a..000000000
--- a/src/main/resources/data/c/tags/items/yellow_dyes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "replace": false,
- "values": [
- "minecraft:yellow_dye"
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/cable.json b/src/main/resources/data/computercraft/advancements/recipes/cable.json
deleted file mode 100644
index e0aeae8ba..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/cable.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:cable" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_modem": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:cable" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:cable" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_modem",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/command_computer.json b/src/main/resources/data/computercraft/advancements/recipes/command_computer.json
deleted file mode 100644
index 692278666..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/command_computer.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:command_computer" ]
- },
- "criteria": {
- "has_redstone": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "minecraft:command_block" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:command_computer" }
- }
- },
- "requirements": [
- [
- "has_redstone",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computer_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computer_advanced.json
deleted file mode 100644
index 96c0f57e4..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computer_advanced.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:advanced_computer" ]
- },
- "criteria": {
- "has_redstone": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "minecraft:redstone" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:advanced_computer" }
- }
- },
- "requirements": [
- [
- "has_redstone",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computer_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computer_normal.json
deleted file mode 100644
index b8ef77055..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computer_normal.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:normal_computer" ]
- },
- "criteria": {
- "has_redstone": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "minecraft:redstone" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:normal_computer" }
- }
- },
- "requirements": [
- [
- "has_redstone",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/cable.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/cable.json
deleted file mode 100644
index a702efff2..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/cable.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:cable"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_modem": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:cable"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_modem",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_advanced.json
deleted file mode 100644
index 53a82f857..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_advanced.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:computer_advanced"
- ]
- },
- "criteria": {
- "has_components": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "minecraft:redstone"
- },
- {
- "item": "minecraft:gold_ingot"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:computer_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_components",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_command.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_command.json
deleted file mode 100644
index 88e5fa410..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_command.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:computer_command"
- ]
- },
- "criteria": {
- "has_components": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "minecraft:command_block"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:computer_command"
- }
- }
- },
- "requirements": [
- [
- "has_components",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_normal.json
deleted file mode 100644
index 2f326d8a8..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/computer_normal.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:computer_normal"
- ]
- },
- "criteria": {
- "has_redstone": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "c:redstone_dusts"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:computer_normal"
- }
- }
- },
- "requirements": [
- [
- "has_redstone",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_1.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_1.json
deleted file mode 100644
index e1956a298..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_1.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_1"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_1"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_10.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_10.json
deleted file mode 100644
index 6070f0464..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_10.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_10"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_10"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_11.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_11.json
deleted file mode 100644
index 60be444fd..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_11.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_11"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_11"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_12.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_12.json
deleted file mode 100644
index 911d4d202..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_12.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_12"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_12"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_13.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_13.json
deleted file mode 100644
index 7d6cb40a3..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_13.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_13"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_13"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_14.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_14.json
deleted file mode 100644
index 5d6f58e8a..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_14.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_14"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_14"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_15.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_15.json
deleted file mode 100644
index e2436725c..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_15.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_15"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_15"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_16.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_16.json
deleted file mode 100644
index b9dcb12e9..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_16.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_16"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_16"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_2.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_2.json
deleted file mode 100644
index 3b26dc5d0..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_2.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_2"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_2"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_3.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_3.json
deleted file mode 100644
index bbf5e3df1..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_3.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_3"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_3"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_4.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_4.json
deleted file mode 100644
index dc6a40b92..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_4.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_4"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_4"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_5.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_5.json
deleted file mode 100644
index 14e6e0284..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_5.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_5"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_5"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_6.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_6.json
deleted file mode 100644
index 302f48a6c..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_6.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_6"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_6"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_7.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_7.json
deleted file mode 100644
index c0f28ff83..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_7.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_7"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_7"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_8.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_8.json
deleted file mode 100644
index 75923e0e0..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_8.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_8"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_8"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_9.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_9.json
deleted file mode 100644
index 4a31c654f..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_9.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_9"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:disk_drive"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_9"
- }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_drive.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_drive.json
deleted file mode 100644
index 02d1205cb..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/disk_drive.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:disk_drive"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:disk_drive"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/monitor_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/monitor_advanced.json
deleted file mode 100644
index 57ceb3ec2..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/monitor_advanced.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:monitor_advanced"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:monitor_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/monitor_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/monitor_normal.json
deleted file mode 100644
index 550e17388..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/monitor_normal.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:monitor_normal"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:monitor_normal"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/speaker.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/speaker.json
deleted file mode 100644
index 91ee1d9cc..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/speaker.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/computercraft/speaker"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "computercraft:speaker"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/computercraft/speaker"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index 51a2525d2..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/computercraft/wireless_modem_advanced"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "computercraft:wireless_modem_advanced"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/computercraft/wireless_modem_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_normal.json
deleted file mode 100644
index a3653511d..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/computercraft/wireless_modem_normal"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "computercraft:wireless_modem_normal"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/computercraft/wireless_modem_normal"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/crafting_table.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/crafting_table.json
deleted file mode 100644
index 844dcf149..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/crafting_table.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/minecraft/crafting_table"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "minecraft:crafting_table"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/minecraft/crafting_table"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_axe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_axe.json
deleted file mode 100644
index cffab3444..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_axe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/minecraft/diamond_axe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "minecraft:diamond_axe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/minecraft/diamond_axe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_hoe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_hoe.json
deleted file mode 100644
index 8e3b66b89..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_hoe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/minecraft/diamond_hoe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "minecraft:diamond_hoe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/minecraft/diamond_hoe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_pickaxe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_pickaxe.json
deleted file mode 100644
index 80c229d74..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_pickaxe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/minecraft/diamond_pickaxe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "minecraft:diamond_pickaxe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/minecraft/diamond_pickaxe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_shovel.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_shovel.json
deleted file mode 100644
index 51a0b82e3..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_shovel.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/minecraft/diamond_shovel"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "minecraft:diamond_shovel"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/minecraft/diamond_shovel"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_sword.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_sword.json
deleted file mode 100644
index 5d1ff7615..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_advanced/minecraft/diamond_sword.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_advanced/minecraft/diamond_sword"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_advanced"
- },
- {
- "item": "minecraft:diamond_sword"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_advanced/minecraft/diamond_sword"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_advanced.json
deleted file mode 100644
index cfd276eee..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_advanced.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_computer_advanced"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_apple": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "minecraft:golden_apple"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_computer_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_apple",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_normal.json
deleted file mode 100644
index d52a8d59e..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_computer_normal.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_computer_normal"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_apple": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "minecraft:golden_apple"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_computer_normal"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_apple",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/speaker.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/speaker.json
deleted file mode 100644
index d4bd7a753..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/speaker.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/computercraft/speaker"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "computercraft:speaker"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/computercraft/speaker"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index fcb68fc98..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/computercraft/wireless_modem_advanced"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "computercraft:wireless_modem_advanced"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/computercraft/wireless_modem_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_normal.json
deleted file mode 100644
index d8f8c39e5..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/computercraft/wireless_modem_normal"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "computercraft:wireless_modem_normal"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/computercraft/wireless_modem_normal"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/crafting_table.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/crafting_table.json
deleted file mode 100644
index 7c28e4abf..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/crafting_table.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/minecraft/crafting_table"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "minecraft:crafting_table"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/minecraft/crafting_table"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_axe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_axe.json
deleted file mode 100644
index 0db58f942..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_axe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/minecraft/diamond_axe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "minecraft:diamond_axe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/minecraft/diamond_axe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_hoe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_hoe.json
deleted file mode 100644
index 8853ce7a8..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_hoe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/minecraft/diamond_hoe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "minecraft:diamond_hoe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/minecraft/diamond_hoe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_pickaxe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_pickaxe.json
deleted file mode 100644
index 32b10b215..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_pickaxe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/minecraft/diamond_pickaxe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "minecraft:diamond_pickaxe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/minecraft/diamond_pickaxe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_shovel.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_shovel.json
deleted file mode 100644
index 36ec5a262..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_shovel.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/minecraft/diamond_shovel"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "minecraft:diamond_shovel"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/minecraft/diamond_shovel"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_sword.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_sword.json
deleted file mode 100644
index 951089086..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/pocket_normal/minecraft/diamond_sword.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:pocket_normal/minecraft/diamond_sword"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:pocket_computer_normal"
- },
- {
- "item": "minecraft:diamond_sword"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:pocket_normal/minecraft/diamond_sword"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/printer.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/printer.json
deleted file mode 100644
index cf068812a..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/printer.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:printer"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:printer"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/speaker.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/speaker.json
deleted file mode 100644
index 4f786e601..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/speaker.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:speaker"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:speaker"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/speaker.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/speaker.json
deleted file mode 100644
index dd742e376..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/speaker.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/computercraft/speaker"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "computercraft:speaker"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/computercraft/speaker"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index 008476105..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/computercraft/wireless_modem_advanced"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "computercraft:wireless_modem_advanced"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/computercraft/wireless_modem_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_normal.json
deleted file mode 100644
index 05716273b..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/computercraft/wireless_modem_normal"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "computercraft:wireless_modem_normal"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/computercraft/wireless_modem_normal"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/crafting_table.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/crafting_table.json
deleted file mode 100644
index 67e80634f..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/crafting_table.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/minecraft/crafting_table"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "minecraft:crafting_table"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/minecraft/crafting_table"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_axe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_axe.json
deleted file mode 100644
index cd6cb053c..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_axe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/minecraft/diamond_axe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "minecraft:diamond_axe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/minecraft/diamond_axe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_hoe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_hoe.json
deleted file mode 100644
index ff2b00194..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_hoe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/minecraft/diamond_hoe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "minecraft:diamond_hoe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/minecraft/diamond_hoe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_pickaxe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_pickaxe.json
deleted file mode 100644
index af0143b03..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_pickaxe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/minecraft/diamond_pickaxe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "minecraft:diamond_pickaxe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/minecraft/diamond_pickaxe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_shovel.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_shovel.json
deleted file mode 100644
index 8400b43ce..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_shovel.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/minecraft/diamond_shovel"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "minecraft:diamond_shovel"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/minecraft/diamond_shovel"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_sword.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_sword.json
deleted file mode 100644
index d48e41149..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_advanced/minecraft/diamond_sword.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_advanced/minecraft/diamond_sword"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_advanced"
- },
- {
- "item": "minecraft:diamond_sword"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_advanced/minecraft/diamond_sword"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/speaker.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/speaker.json
deleted file mode 100644
index d73caac29..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/speaker.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/computercraft/speaker"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "computercraft:speaker"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/computercraft/speaker"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index 5570d1113..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/computercraft/wireless_modem_advanced"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "computercraft:wireless_modem_advanced"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/computercraft/wireless_modem_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_normal.json
deleted file mode 100644
index 1f5eac5ad..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/computercraft/wireless_modem_normal"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "computercraft:wireless_modem_normal"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/computercraft/wireless_modem_normal"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/crafting_table.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/crafting_table.json
deleted file mode 100644
index 2b6a255de..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/crafting_table.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/minecraft/crafting_table"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "minecraft:crafting_table"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/minecraft/crafting_table"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_axe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_axe.json
deleted file mode 100644
index 1b9997448..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_axe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/minecraft/diamond_axe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "minecraft:diamond_axe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/minecraft/diamond_axe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_hoe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_hoe.json
deleted file mode 100644
index 3435cc116..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_hoe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/minecraft/diamond_hoe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "minecraft:diamond_hoe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/minecraft/diamond_hoe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_pickaxe.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_pickaxe.json
deleted file mode 100644
index d86ee5e65..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_pickaxe.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/minecraft/diamond_pickaxe"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "minecraft:diamond_pickaxe"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/minecraft/diamond_pickaxe"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_shovel.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_shovel.json
deleted file mode 100644
index 447f249d2..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_shovel.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/minecraft/diamond_shovel"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "minecraft:diamond_shovel"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/minecraft/diamond_shovel"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_sword.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_sword.json
deleted file mode 100644
index 5a6457a28..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/turtle_normal/minecraft/diamond_sword.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:turtle_normal/minecraft/diamond_sword"
- ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:turtle_normal"
- },
- {
- "item": "minecraft:diamond_sword"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:turtle_normal/minecraft/diamond_sword"
- }
- }
- },
- "requirements": [
- [
- "has_items",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem.json
deleted file mode 100644
index bd1237c8d..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:wired_modem"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_cable": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:cable"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:wired_modem"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_cable",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_from.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_from.json
deleted file mode 100644
index 7ebb299b0..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_from.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:wired_modem_full_from"
- ]
- },
- "criteria": {
- "has_modem": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:wired_modem"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:wired_modem_full_from"
- }
- }
- },
- "requirements": [
- [
- "has_modem",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_to.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_to.json
deleted file mode 100644
index 40eafc1cb..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wired_modem_full_to.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:wired_modem_full_to"
- ]
- },
- "criteria": {
- "has_modem": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:wired_modem"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:wired_modem_full_to"
- }
- }
- },
- "requirements": [
- [
- "has_modem",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index bdad47dd0..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:wireless_modem_advanced"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_wireless": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "item": "computercraft:wireless_modem_normal"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:wireless_modem_advanced"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_wireless",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_normal.json
deleted file mode 100644
index c0bc76f9d..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:wireless_modem_normal"
- ]
- },
- "criteria": {
- "has_computer": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- {
- "tag": "computercraft:computer"
- }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": {
- "recipe": "computercraft:wireless_modem_normal"
- }
- }
- },
- "requirements": [
- [
- "has_computer",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/disk_drive.json b/src/main/resources/data/computercraft/advancements/recipes/disk_drive.json
deleted file mode 100644
index b1225ca70..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/disk_drive.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:disk_drive" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:disk_drive" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/disk.json b/src/main/resources/data/computercraft/advancements/recipes/generated/disk.json
deleted file mode 100644
index 94c07e6d9..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/disk.json
+++ /dev/null
@@ -1,116 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:generated/disk/disk_1",
- "computercraft:generated/disk/disk_2",
- "computercraft:generated/disk/disk_3",
- "computercraft:generated/disk/disk_4",
- "computercraft:generated/disk/disk_5",
- "computercraft:generated/disk/disk_6",
- "computercraft:generated/disk/disk_7",
- "computercraft:generated/disk/disk_8",
- "computercraft:generated/disk/disk_9",
- "computercraft:generated/disk/disk_10",
- "computercraft:generated/disk/disk_11",
- "computercraft:generated/disk/disk_12",
- "computercraft:generated/disk/disk_13",
- "computercraft:generated/disk/disk_14",
- "computercraft:generated/disk/disk_15",
- "computercraft:generated/disk/disk_16"
- ]
- },
- "criteria": {
- "has_drive": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:disk_drive" } ]
- }
- },
- "has_recipe_1": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_1" }
- },
- "has_recipe_2": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_2" }
- },
- "has_recipe_3": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_3" }
- },
- "has_recipe_4": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_4" }
- },
- "has_recipe_5": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_5" }
- },
- "has_recipe_6": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_6" }
- },
- "has_recipe_7": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_7" }
- },
- "has_recipe_8": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_8" }
- },
- "has_recipe_9": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_9" }
- },
- "has_recipe_10": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_10" }
- },
- "has_recipe_11": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_11" }
- },
- "has_recipe_12": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_12" }
- },
- "has_recipe_13": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_13" }
- },
- "has_recipe_14": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_14" }
- },
- "has_recipe_15": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_15" }
- },
- "has_recipe_16": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/disk/disk_16" }
- }
- },
- "requirements": [
- [
- "has_drive",
- "has_recipe_1",
- "has_recipe_2",
- "has_recipe_3",
- "has_recipe_4",
- "has_recipe_5",
- "has_recipe_6",
- "has_recipe_7",
- "has_recipe_8",
- "has_recipe_9",
- "has_recipe_10",
- "has_recipe_11",
- "has_recipe_12",
- "has_recipe_13",
- "has_recipe_14",
- "has_recipe_15",
- "has_recipe_16"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_speaker.json b/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_speaker.json
deleted file mode 100644
index 3b9da6c6f..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_speaker.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/pocket_advanced/computercraft_speaker" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:pocket_computer_advanced" },
- { "item": "computercraft:speaker" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/pocket_advanced/computercraft_speaker" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_wireless_modem_advanced.json
deleted file mode 100644
index ce426a0f9..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_wireless_modem_advanced.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/pocket_advanced/computercraft_wireless_modem_advanced" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:pocket_computer_advanced" },
- { "item": "computercraft:wireless_modem_advanced" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/pocket_advanced/computercraft_wireless_modem_advanced" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_wireless_modem_normal.json
deleted file mode 100644
index d92bae7b0..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_advanced/computercraft_wireless_modem_normal.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/pocket_advanced/computercraft_wireless_modem_normal" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:pocket_computer_advanced" },
- { "item": "computercraft:wireless_modem_normal" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/pocket_advanced/computercraft_wireless_modem_normal" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_speaker.json b/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_speaker.json
deleted file mode 100644
index a0fac97f9..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_speaker.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/pocket_normal/computercraft_speaker" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:pocket_computer_normal" },
- { "item": "computercraft:speaker" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/pocket_normal/computercraft_speaker" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_wireless_modem_advanced.json
deleted file mode 100644
index 29c8cb86b..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_wireless_modem_advanced.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/pocket_normal/computercraft_wireless_modem_advanced" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:pocket_computer_normal" },
- { "item": "computercraft:wireless_modem_advanced" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/pocket_normal/computercraft_wireless_modem_advanced" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_wireless_modem_normal.json
deleted file mode 100644
index 19fe726ee..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/pocket_normal/computercraft_wireless_modem_normal.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/pocket_normal/computercraft_wireless_modem_normal" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:pocket_computer_normal" },
- { "item": "computercraft:wireless_modem_normal" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/pocket_normal/computercraft_wireless_modem_normal" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_speaker.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_speaker.json
deleted file mode 100644
index 09942d3d5..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_speaker.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/computercraft_speaker" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "computercraft:speaker" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/computercraft_speaker" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_wireless_modem_advanced.json
deleted file mode 100644
index 23ed3111c..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_wireless_modem_advanced.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/computercraft_wireless_modem_advanced" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "computercraft:wireless_modem_advanced" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/computercraft_wireless_modem_advanced" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_wireless_modem_normal.json
deleted file mode 100644
index e2613b21f..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/computercraft_wireless_modem_normal.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/computercraft_wireless_modem_normal" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "computercraft:wireless_modem_normal" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/computercraft_wireless_modem_normal" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_crafting_table.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_crafting_table.json
deleted file mode 100644
index c6c86d47a..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_crafting_table.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/minecraft_crafting_table" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "minecraft:crafting_table" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/minecraft_crafting_table" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_axe.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_axe.json
deleted file mode 100644
index 6c1adcd84..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_axe.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/minecraft_diamond_axe" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "minecraft:diamond_axe" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/minecraft_diamond_axe" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_hoe.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_hoe.json
deleted file mode 100644
index d906a8317..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_hoe.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/minecraft_diamond_hoe" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "minecraft:diamond_hoe" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/minecraft_diamond_hoe" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_pickaxe.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_pickaxe.json
deleted file mode 100644
index 9d59c615e..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_pickaxe.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/minecraft_diamond_pickaxe" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "minecraft:diamond_pickaxe" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/minecraft_diamond_pickaxe" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_shovel.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_shovel.json
deleted file mode 100644
index efc7a3fce..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_shovel.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/minecraft_diamond_shovel" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "minecraft:diamond_shovel" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/minecraft_diamond_shovel" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_sword.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_sword.json
deleted file mode 100644
index ad9f19c17..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_advanced/minecraft_diamond_sword.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_advanced/minecraft_diamond_sword" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_advanced" },
- { "item": "minecraft:diamond_sword" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_advanced/minecraft_diamond_sword" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_speaker.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_speaker.json
deleted file mode 100644
index 6d18d9ee2..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_speaker.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/computercraft_speaker" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "computercraft:speaker" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/computercraft_speaker" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_wireless_modem_advanced.json
deleted file mode 100644
index 5fe595acc..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_wireless_modem_advanced.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/computercraft_wireless_modem_advanced" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "computercraft:wireless_modem_advanced" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/computercraft_wireless_modem_advanced" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_wireless_modem_normal.json
deleted file mode 100644
index 8d43a7c65..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/computercraft_wireless_modem_normal.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/computercraft_wireless_modem_normal" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "computercraft:wireless_modem_normal" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/computercraft_wireless_modem_normal" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_crafting_table.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_crafting_table.json
deleted file mode 100644
index 2bee28454..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_crafting_table.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/minecraft_crafting_table" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "minecraft:crafting_table" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/minecraft_crafting_table" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_axe.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_axe.json
deleted file mode 100644
index df0b816a6..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_axe.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/minecraft_diamond_axe" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "minecraft:diamond_axe" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/minecraft_diamond_axe" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_hoe.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_hoe.json
deleted file mode 100644
index 8df26b012..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_hoe.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/minecraft_diamond_hoe" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "minecraft:diamond_hoe" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/minecraft_diamond_hoe" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_pickaxe.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_pickaxe.json
deleted file mode 100644
index e83d19e7b..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_pickaxe.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/minecraft_diamond_pickaxe" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "minecraft:diamond_pickaxe" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/minecraft_diamond_pickaxe" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_shovel.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_shovel.json
deleted file mode 100644
index abeddd89b..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_shovel.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/minecraft_diamond_shovel" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "minecraft:diamond_shovel" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/minecraft_diamond_shovel" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_sword.json b/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_sword.json
deleted file mode 100644
index 8930c40dd..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/generated/turtle_normal/minecraft_diamond_sword.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:generated/turtle_normal/minecraft_diamond_sword" ]
- },
- "criteria": {
- "has_items": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [
- { "item": "computercraft:turtle_normal" },
- { "item": "minecraft:diamond_sword" }
- ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:generated/turtle_normal/minecraft_diamond_sword" }
- }
- },
- "requirements": [ [ "has_items", "has_the_recipe" ] ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/monitor_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/monitor_advanced.json
deleted file mode 100644
index 270f01765..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/monitor_advanced.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:monitor_advanced" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:monitor_advanced" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/monitor_normal.json b/src/main/resources/data/computercraft/advancements/recipes/monitor_normal.json
deleted file mode 100644
index b1076ddf0..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/monitor_normal.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:normal_monitor" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:normal_monitor" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/pocket_computer_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/pocket_computer_advanced.json
deleted file mode 100644
index b74f2d529..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/pocket_computer_advanced.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:pocket_computer_advanced" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_apple": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "minecraft:golden_apple" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:pocket_computer_advanced" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_apple",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/pocket_computer_normal.json b/src/main/resources/data/computercraft/advancements/recipes/pocket_computer_normal.json
deleted file mode 100644
index 02f6be078..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/pocket_computer_normal.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:normal_pocket_computer_normal" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_apple": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "minecraft:golden_apple" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:pocket_computer_normal" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_apple",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/printed_book.json b/src/main/resources/data/computercraft/advancements/recipes/printed_book.json
deleted file mode 100644
index 1d6bd81ac..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/printed_book.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:printed_book" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:printed_page" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:printed_book" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/printed_pages.json b/src/main/resources/data/computercraft/advancements/recipes/printed_pages.json
deleted file mode 100644
index b87a31a50..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/printed_pages.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:printed_pages" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:printer" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:printed_pages" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/printer.json b/src/main/resources/data/computercraft/advancements/recipes/printer.json
deleted file mode 100644
index 9e00f37a7..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/printer.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:printer" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:printer" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/printout.json b/src/main/resources/data/computercraft/advancements/recipes/printout.json
deleted file mode 100644
index ef0e9f314..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/printout.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:printout" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:printer" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:printout" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/skull_cloudy.json b/src/main/resources/data/computercraft/advancements/recipes/skull_cloudy.json
deleted file mode 100644
index 6ab01fe65..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/skull_cloudy.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:skull_cloudy" ]
- },
- "criteria": {
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:skull_cloudy" }
- }
- },
- "requirements": [
- [
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/skull_dan200.json b/src/main/resources/data/computercraft/advancements/recipes/skull_dan200.json
deleted file mode 100644
index 277c0b477..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/skull_dan200.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:skull_dan200" ]
- },
- "criteria": {
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:skull_dan200" }
- }
- },
- "requirements": [
- [
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/speaker.json b/src/main/resources/data/computercraft/advancements/recipes/speaker.json
deleted file mode 100644
index 721c93595..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/speaker.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:speaker" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_noteblock": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "minecraft:note_block" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:speaker" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_noteblock",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/turtle_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/turtle_advanced.json
deleted file mode 100644
index 77dc9d4e8..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/turtle_advanced.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:turtle_advanced" ]
- },
- "criteria": {
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:turtle_advanced" }
- }
- },
- "requirements": [
- [
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/turtle_normal.json b/src/main/resources/data/computercraft/advancements/recipes/turtle_normal.json
deleted file mode 100644
index 482be3e1c..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/turtle_normal.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:normal_turtle_normal" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:turtle_normal" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/wired_modem.json b/src/main/resources/data/computercraft/advancements/recipes/wired_modem.json
deleted file mode 100644
index ec4cef5a9..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/wired_modem.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [
- "computercraft:wired_modem",
- "computercraft:wired_modem_full_to",
- "computercraft:wired_modem_full_from"
- ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_cable": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:wired_modem" } ]
- }
- },
- "has_modem_full": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:wired_modem_full" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:wired_modem" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_cable",
- "has_modem_full",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/wireless_modem_advanced.json b/src/main/resources/data/computercraft/advancements/recipes/wireless_modem_advanced.json
deleted file mode 100644
index 8819a76f5..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/wireless_modem_advanced.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:wireless_modem_advanced" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_wireless": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:wireless_modem_normal" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:wireless_modem_advanced" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_wireless",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/advancements/recipes/wireless_modem_normal.json b/src/main/resources/data/computercraft/advancements/recipes/wireless_modem_normal.json
deleted file mode 100644
index f82624da3..000000000
--- a/src/main/resources/data/computercraft/advancements/recipes/wireless_modem_normal.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "parent": "minecraft:recipes/root",
- "rewards": {
- "recipes": [ "computercraft:wireless_modem_normal" ]
- },
- "criteria": {
- "has_normal": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_normal" } ]
- }
- },
- "has_advanced": {
- "trigger": "minecraft:inventory_changed",
- "conditions": {
- "items": [ { "item": "computercraft:computer_advanced" } ]
- }
- },
- "has_the_recipe": {
- "trigger": "minecraft:recipe_unlocked",
- "conditions": { "recipe": "computercraft:wireless_modem_normal" }
- }
- },
- "requirements": [
- [
- "has_normal",
- "has_advanced",
- "has_the_recipe"
- ]
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/cable.json b/src/main/resources/data/computercraft/loot_tables/blocks/cable.json
deleted file mode 100644
index 0bc4cd077..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/cable.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "cable",
- "rolls": 1,
- "entries": [
- { "type": "minecraft:item", "name": "computercraft:cable" }
- ],
- "conditions": [
- { "condition": "minecraft:survives_explosion" },
- {
- "condition": "minecraft:block_state_property",
- "block": "computercraft:cable",
- "properties": { "cable": "true" }
- }
- ]
- },
- {
- "name": "wired_modem",
- "rolls": 1,
- "entries": [
- { "type": "minecraft:item", "name": "computercraft:wired_modem" }
- ],
- "conditions": [
- { "condition": "minecraft:survives_explosion" },
- {
- "condition": "minecraft:inverted",
- "term": {
- "condition": "minecraft:block_state_property",
- "block": "computercraft:cable",
- "properties": { "modem": "none" }
- }
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/computer_advanced.json b/src/main/resources/data/computercraft/loot_tables/blocks/computer_advanced.json
deleted file mode 100644
index f5174622f..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/computer_advanced.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:dynamic",
- "name": "computercraft:computer"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:alternative",
- "terms": [
- {
- "condition": "computercraft:block_named"
- },
- {
- "condition": "computercraft:has_id"
- },
- {
- "condition": "minecraft:inverted",
- "term": {
- "condition": "computercraft:player_creative"
- }
- }
- ]
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/computer_command.json b/src/main/resources/data/computercraft/loot_tables/blocks/computer_command.json
deleted file mode 100644
index f5174622f..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/computer_command.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:dynamic",
- "name": "computercraft:computer"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:alternative",
- "terms": [
- {
- "condition": "computercraft:block_named"
- },
- {
- "condition": "computercraft:has_id"
- },
- {
- "condition": "minecraft:inverted",
- "term": {
- "condition": "computercraft:player_creative"
- }
- }
- ]
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/computer_normal.json b/src/main/resources/data/computercraft/loot_tables/blocks/computer_normal.json
deleted file mode 100644
index f5174622f..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/computer_normal.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:dynamic",
- "name": "computercraft:computer"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:alternative",
- "terms": [
- {
- "condition": "computercraft:block_named"
- },
- {
- "condition": "computercraft:has_id"
- },
- {
- "condition": "minecraft:inverted",
- "term": {
- "condition": "computercraft:player_creative"
- }
- }
- ]
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/disk_drive.json b/src/main/resources/data/computercraft/loot_tables/blocks/disk_drive.json
deleted file mode 100644
index 26dd811af..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/disk_drive.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:disk_drive"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/monitor_advanced.json b/src/main/resources/data/computercraft/loot_tables/blocks/monitor_advanced.json
deleted file mode 100644
index 9b9653008..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/monitor_advanced.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:monitor_advanced"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/monitor_normal.json b/src/main/resources/data/computercraft/loot_tables/blocks/monitor_normal.json
deleted file mode 100644
index 1181c96e9..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/monitor_normal.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:monitor_normal"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/printer.json b/src/main/resources/data/computercraft/loot_tables/blocks/printer.json
deleted file mode 100644
index af72c6461..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/printer.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:printer"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/speaker.json b/src/main/resources/data/computercraft/loot_tables/blocks/speaker.json
deleted file mode 100644
index 3549a6a79..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/speaker.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:speaker"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/turtle_advanced.json b/src/main/resources/data/computercraft/loot_tables/blocks/turtle_advanced.json
deleted file mode 100644
index f5174622f..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/turtle_advanced.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:dynamic",
- "name": "computercraft:computer"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:alternative",
- "terms": [
- {
- "condition": "computercraft:block_named"
- },
- {
- "condition": "computercraft:has_id"
- },
- {
- "condition": "minecraft:inverted",
- "term": {
- "condition": "computercraft:player_creative"
- }
- }
- ]
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/turtle_normal.json b/src/main/resources/data/computercraft/loot_tables/blocks/turtle_normal.json
deleted file mode 100644
index f5174622f..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/turtle_normal.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:dynamic",
- "name": "computercraft:computer"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:alternative",
- "terms": [
- {
- "condition": "computercraft:block_named"
- },
- {
- "condition": "computercraft:has_id"
- },
- {
- "condition": "minecraft:inverted",
- "term": {
- "condition": "computercraft:player_creative"
- }
- }
- ]
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/wired_modem_full.json b/src/main/resources/data/computercraft/loot_tables/blocks/wired_modem_full.json
deleted file mode 100644
index f167008af..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/wired_modem_full.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:wired_modem_full"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/wireless_modem_advanced.json b/src/main/resources/data/computercraft/loot_tables/blocks/wireless_modem_advanced.json
deleted file mode 100644
index 56c77b36f..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/wireless_modem_advanced.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:wireless_modem_advanced"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/loot_tables/blocks/wireless_modem_normal.json b/src/main/resources/data/computercraft/loot_tables/blocks/wireless_modem_normal.json
deleted file mode 100644
index b4a0a9914..000000000
--- a/src/main/resources/data/computercraft/loot_tables/blocks/wireless_modem_normal.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:block",
- "pools": [
- {
- "name": "main",
- "rolls": 1,
- "entries": [
- {
- "type": "minecraft:item",
- "name": "computercraft:wireless_modem_normal"
- }
- ],
- "conditions": [
- {
- "condition": "minecraft:survives_explosion"
- }
- ]
- }
- ]
-}
diff --git a/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/src/main/resources/data/computercraft/lua/rom/help/changelog.md
index 8f58dd7b3..99d652883 100644
--- a/src/main/resources/data/computercraft/lua/rom/help/changelog.md
+++ b/src/main/resources/data/computercraft/lua/rom/help/changelog.md
@@ -1,3 +1,24 @@
+# New features in CC: Tweaked 1.100.5 (CC: Restitched 1.100.4)
+
+* Improve performance of monitor rendering.
+
+Several bug fixes:
+* Various documentation fixes (bclindner, Hasaabitt)
+* Speaker sounds are now correctly positioned on the centre of the speaker block.
+
+# New features in CC: Tweaked 1.100.4
+
+Several bug fixes:
+* Fix the monitor watching blocking the main thread when chunks are slow to load.
+
+# New features in CC: Tweaked 1.100.3
+
+Several bug fixes:
+* Fix client disconnect when uploading large files.
+* Correctly handling empty computer ID file.
+* Fix the normal turtle recipe not being unlocked.
+* Remove turtle fake EntityType.
+
# New features in CC: Tweaked 1.100.2
Several bug fixes:
diff --git a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md
index bbd0e76f2..592eb933f 100644
--- a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md
+++ b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md
@@ -1,7 +1,9 @@
-New features in CC: Tweaked 1.100.2
+New features in CC: Tweaked 1.100.5 (CC: Restitched 1.100.4)
+
+* Improve performance of monitor rendering.
Several bug fixes:
-* Fix wired modems swapping the modem/peripheral block state.
-* Remove debugging logging line from `turtle.attack`.
+* Various documentation fixes (bclindner, Hasaabitt)
+* Speaker sounds are now correctly positioned on the centre of the speaker block.
Type "help changelog" to see the full version history.
diff --git a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua
index f24c0c4fc..f2649f47a 100644
--- a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua
+++ b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua
@@ -3,7 +3,7 @@
-- It allows you to @{run|start programs}, @{setCompletionFunction|add
-- completion for a program}, and much more.
--
--- @{shell} is not a "true" API. Instead, it is a standard program, which its
+-- @{shell} is not a "true" API. Instead, it is a standard program, which injects its
-- API into the programs that it launches. This allows for multiple shells to
-- run at the same time, but means that the API is not available in the global
-- environment, and so is unavailable to other @{os.loadAPI|APIs}.
diff --git a/src/main/resources/data/computercraft/recipes/cable.json b/src/main/resources/data/computercraft/recipes/cable.json
deleted file mode 100644
index 2d0f86d73..000000000
--- a/src/main/resources/data/computercraft/recipes/cable.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- " # ",
- "#R#",
- " # "
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "R": {
- "tag": "c:redstone_dusts"
- }
- },
- "result": {
- "item": "computercraft:cable",
- "count": 6
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/colour.json b/src/main/resources/data/computercraft/recipes/colour.json
deleted file mode 100644
index ff190e755..000000000
--- a/src/main/resources/data/computercraft/recipes/colour.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "type": "computercraft:colour" }
diff --git a/src/main/resources/data/computercraft/recipes/computer_advanced.json b/src/main/resources/data/computercraft/recipes/computer_advanced.json
deleted file mode 100644
index 02208c079..000000000
--- a/src/main/resources/data/computercraft/recipes/computer_advanced.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#R#",
- "#G#"
- ],
- "key": {
- "#": {
- "tag": "c:gold_ingots"
- },
- "R": {
- "tag": "c:redstone_dusts"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:computer_advanced"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/computer_command.json b/src/main/resources/data/computercraft/recipes/computer_command.json
deleted file mode 100644
index 61b1b3b14..000000000
--- a/src/main/resources/data/computercraft/recipes/computer_command.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#R#",
- "#G#"
- ],
- "key": {
- "#": {
- "tag": "c:gold_ingots"
- },
- "R": {
- "item": "minecraft:command_block"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:computer_command"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/computer_normal.json b/src/main/resources/data/computercraft/recipes/computer_normal.json
deleted file mode 100644
index 3271c2551..000000000
--- a/src/main/resources/data/computercraft/recipes/computer_normal.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#R#",
- "#G#"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "R": {
- "tag": "c:redstone_dusts"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:computer_normal"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk.json b/src/main/resources/data/computercraft/recipes/disk.json
deleted file mode 100644
index 5dfba2271..000000000
--- a/src/main/resources/data/computercraft/recipes/disk.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "type": "computercraft:disk" }
diff --git a/src/main/resources/data/computercraft/recipes/disk_1.json b/src/main/resources/data/computercraft/recipes/disk_1.json
deleted file mode 100644
index f58c4524d..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_1.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:black_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:1118481}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_10.json b/src/main/resources/data/computercraft/recipes/disk_10.json
deleted file mode 100644
index be3c706d6..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_10.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:pink_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:15905484}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_11.json b/src/main/resources/data/computercraft/recipes/disk_11.json
deleted file mode 100644
index b16467e7a..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_11.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:lime_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:8375321}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_12.json b/src/main/resources/data/computercraft/recipes/disk_12.json
deleted file mode 100644
index c0ff791a8..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_12.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:yellow_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:14605932}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_13.json b/src/main/resources/data/computercraft/recipes/disk_13.json
deleted file mode 100644
index 8cf914c0c..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_13.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:light_blue_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:10072818}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_14.json b/src/main/resources/data/computercraft/recipes/disk_14.json
deleted file mode 100644
index a63ed9a0c..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_14.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:magenta_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:15040472}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_15.json b/src/main/resources/data/computercraft/recipes/disk_15.json
deleted file mode 100644
index 96e20a117..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_15.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:orange_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:15905331}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_16.json b/src/main/resources/data/computercraft/recipes/disk_16.json
deleted file mode 100644
index b3e31354f..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_16.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:white_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:15790320}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_2.json b/src/main/resources/data/computercraft/recipes/disk_2.json
deleted file mode 100644
index b211373ca..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_2.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:red_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:13388876}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_3.json b/src/main/resources/data/computercraft/recipes/disk_3.json
deleted file mode 100644
index 311e7fc28..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_3.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:green_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:5744206}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_4.json b/src/main/resources/data/computercraft/recipes/disk_4.json
deleted file mode 100644
index dad728d09..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_4.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:brown_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:8349260}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_5.json b/src/main/resources/data/computercraft/recipes/disk_5.json
deleted file mode 100644
index 52eca6cf8..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_5.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:blue_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:3368652}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_6.json b/src/main/resources/data/computercraft/recipes/disk_6.json
deleted file mode 100644
index f21b7d509..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_6.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:purple_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:11691749}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_7.json b/src/main/resources/data/computercraft/recipes/disk_7.json
deleted file mode 100644
index 6c5d9335f..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_7.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:cyan_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:5020082}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_8.json b/src/main/resources/data/computercraft/recipes/disk_8.json
deleted file mode 100644
index 5670d5fdb..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_8.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:light_gray_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:10066329}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_9.json b/src/main/resources/data/computercraft/recipes/disk_9.json
deleted file mode 100644
index b1b28a606..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_9.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "group": "computercraft:disk",
- "ingredients": [
- {
- "tag": "c:redstone_dusts"
- },
- {
- "item": "minecraft:paper"
- },
- {
- "item": "minecraft:gray_dye"
- }
- ],
- "result": {
- "item": "computercraft:disk",
- "nbt": "{Color:5000268}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/disk_drive.json b/src/main/resources/data/computercraft/recipes/disk_drive.json
deleted file mode 100644
index af158529d..000000000
--- a/src/main/resources/data/computercraft/recipes/disk_drive.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#R#",
- "#R#"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "R": {
- "tag": "c:redstone_dusts"
- }
- },
- "result": {
- "item": "computercraft:disk_drive"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/monitor_advanced.json b/src/main/resources/data/computercraft/recipes/monitor_advanced.json
deleted file mode 100644
index 48cb2c3d6..000000000
--- a/src/main/resources/data/computercraft/recipes/monitor_advanced.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#G#",
- "###"
- ],
- "key": {
- "#": {
- "tag": "c:gold_ingots"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:monitor_advanced",
- "count": 4
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/monitor_normal.json b/src/main/resources/data/computercraft/recipes/monitor_normal.json
deleted file mode 100644
index 93954505c..000000000
--- a/src/main/resources/data/computercraft/recipes/monitor_normal.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#G#",
- "###"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:monitor_normal"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/speaker.json b/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/speaker.json
deleted file mode 100644
index 26813f857..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/speaker.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:pocket_advanced",
- "pattern": [
- "#",
- "P"
- ],
- "key": {
- "#": {
- "item": "computercraft:pocket_computer_advanced"
- },
- "P": {
- "item": "computercraft:speaker"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_advanced",
- "nbt": "{Upgrade:\"computercraft:speaker\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index 86b42d9c9..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:pocket_advanced",
- "pattern": [
- "#",
- "P"
- ],
- "key": {
- "#": {
- "item": "computercraft:pocket_computer_advanced"
- },
- "P": {
- "item": "computercraft:wireless_modem_advanced"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_advanced",
- "nbt": "{Upgrade:\"computercraft:wireless_modem_advanced\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_normal.json
deleted file mode 100644
index f0f5ae53f..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_advanced/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:pocket_advanced",
- "pattern": [
- "#",
- "P"
- ],
- "key": {
- "#": {
- "item": "computercraft:pocket_computer_advanced"
- },
- "P": {
- "item": "computercraft:wireless_modem_normal"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_advanced",
- "nbt": "{Upgrade:\"computercraft:wireless_modem_normal\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_computer_advanced.json b/src/main/resources/data/computercraft/recipes/pocket_computer_advanced.json
deleted file mode 100644
index fefde5b00..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_computer_advanced.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#A#",
- "#G#"
- ],
- "key": {
- "#": {
- "tag": "c:gold_ingots"
- },
- "A": {
- "item": "minecraft:golden_apple"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_advanced"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_computer_normal.json b/src/main/resources/data/computercraft/recipes/pocket_computer_normal.json
deleted file mode 100644
index 4f6a519db..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_computer_normal.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#A#",
- "#G#"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "A": {
- "item": "minecraft:golden_apple"
- },
- "G": {
- "tag": "c:glass_panes"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_normal"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_computer_upgrade.json b/src/main/resources/data/computercraft/recipes/pocket_computer_upgrade.json
deleted file mode 100644
index 1dc7038c7..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_computer_upgrade.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "type": "computercraft:pocket_computer_upgrade" }
diff --git a/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/speaker.json b/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/speaker.json
deleted file mode 100644
index 834c82670..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/speaker.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:pocket_normal",
- "pattern": [
- "#",
- "P"
- ],
- "key": {
- "#": {
- "item": "computercraft:pocket_computer_normal"
- },
- "P": {
- "item": "computercraft:speaker"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_normal",
- "nbt": "{Upgrade:\"computercraft:speaker\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index 732102efe..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:pocket_normal",
- "pattern": [
- "#",
- "P"
- ],
- "key": {
- "#": {
- "item": "computercraft:pocket_computer_normal"
- },
- "P": {
- "item": "computercraft:wireless_modem_advanced"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_normal",
- "nbt": "{Upgrade:\"computercraft:wireless_modem_advanced\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_normal.json
deleted file mode 100644
index a7e923bc1..000000000
--- a/src/main/resources/data/computercraft/recipes/pocket_normal/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:pocket_normal",
- "pattern": [
- "#",
- "P"
- ],
- "key": {
- "#": {
- "item": "computercraft:pocket_computer_normal"
- },
- "P": {
- "item": "computercraft:wireless_modem_normal"
- }
- },
- "result": {
- "item": "computercraft:pocket_computer_normal",
- "nbt": "{Upgrade:\"computercraft:wireless_modem_normal\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/printed_book.json b/src/main/resources/data/computercraft/recipes/printed_book.json
deleted file mode 100644
index aac7eb4e2..000000000
--- a/src/main/resources/data/computercraft/recipes/printed_book.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "ingredients": [
- { "item": "minecraft:leather" },
- { "item": "computercraft:printed_page" },
- { "item": "minecraft:string" }
- ],
- "result": { "item": "computercraft:printed_book" }
-}
diff --git a/src/main/resources/data/computercraft/recipes/printed_pages.json b/src/main/resources/data/computercraft/recipes/printed_pages.json
deleted file mode 100644
index 38d32c223..000000000
--- a/src/main/resources/data/computercraft/recipes/printed_pages.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "type": "computercraft:impostor_shapeless",
- "ingredients": [
- { "item": "computercraft:printed_page" },
- { "item": "computercraft:printed_page" },
- { "item": "minecraft:string" }
- ],
- "result": { "item": "computercraft:printed_pages" }
-}
diff --git a/src/main/resources/data/computercraft/recipes/printer.json b/src/main/resources/data/computercraft/recipes/printer.json
deleted file mode 100644
index 8ad853103..000000000
--- a/src/main/resources/data/computercraft/recipes/printer.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#R#",
- "#D#"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "R": {
- "tag": "c:redstone_dusts"
- },
- "D": [
- {"tag": "c:black_dyes"},
- {"tag": "c:blue_dyes"},
- {"tag": "c:brown_dyes"},
- {"tag": "c:cyan_dyes"},
- {"tag": "c:gray_dyes"},
- {"tag": "c:green_dyes"},
- {"tag": "c:light_blue_dyes"},
- {"tag": "c:light_gray_dyes"},
- {"tag": "c:lime_dyes"},
- {"tag": "c:magenta_dyes"},
- {"tag": "c:orange_dyes"},
- {"tag": "c:pink_dyes"},
- {"tag": "c:purple_dyes"},
- {"tag": "c:red_dyes"},
- {"tag": "c:white_dyes"},
- {"tag": "c:yellow_dyes"}
- ]
- },
- "result": {
- "item": "computercraft:printer"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/printout.json b/src/main/resources/data/computercraft/recipes/printout.json
deleted file mode 100644
index 20588569a..000000000
--- a/src/main/resources/data/computercraft/recipes/printout.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "type": "computercraft:printout" }
diff --git a/src/main/resources/data/computercraft/recipes/skull_cloudy.json b/src/main/resources/data/computercraft/recipes/skull_cloudy.json
deleted file mode 100644
index 4d7a77642..000000000
--- a/src/main/resources/data/computercraft/recipes/skull_cloudy.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "type": "crafting_shapeless",
- "ingredients": [
- { "item": "minecraft:wither_skeleton_skull" },
- { "item": "computercraft:monitor_normal" }
- ],
- "result": {
- "item": "minecraft:player_head",
- "nbt": { "SkullOwner": { "Name": "Cloudhunter", "Id": "6d074736-b1e9-4378-a99b-bd8777821c9c" } }
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/skull_dan200.json b/src/main/resources/data/computercraft/recipes/skull_dan200.json
deleted file mode 100644
index 5f7bb84a8..000000000
--- a/src/main/resources/data/computercraft/recipes/skull_dan200.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "type": "crafting_shapeless",
- "ingredients": [
- { "item": "minecraft:wither_skeleton_skull" },
- { "item": "computercraft:computer_normal" }
- ],
- "result": {
- "item": "minecraft:player_head",
- "nbt": { "SkullOwner": { "Name": "dan200", "Id": "f3c8d69b-0776-4512-8434-d1b2165909eb" } }
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/speaker.json b/src/main/resources/data/computercraft/recipes/speaker.json
deleted file mode 100644
index 76570ce5a..000000000
--- a/src/main/resources/data/computercraft/recipes/speaker.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#N#",
- "#R#"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "N": {
- "item": "minecraft:note_block"
- },
- "R": {
- "tag": "c:redstone_dusts"
- }
- },
- "result": {
- "item": "computercraft:speaker"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/speaker.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/speaker.json
deleted file mode 100644
index 6976ec56f..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/speaker.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "computercraft:speaker"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"computercraft:speaker\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index a78f7ee0d..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "computercraft:wireless_modem_advanced"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"computercraft:wireless_modem_advanced\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_normal.json
deleted file mode 100644
index 362ed2b21..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "computercraft:wireless_modem_normal"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"computercraft:wireless_modem_normal\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/crafting_table.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/crafting_table.json
deleted file mode 100644
index 0e4546f83..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/crafting_table.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:crafting_table"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:crafting_table\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_axe.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_axe.json
deleted file mode 100644
index 03cb4a175..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_axe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:diamond_axe"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:diamond_axe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_hoe.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_hoe.json
deleted file mode 100644
index 2fc7dfa57..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_hoe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:diamond_hoe"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:diamond_hoe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_pickaxe.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_pickaxe.json
deleted file mode 100644
index ef3601443..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_pickaxe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:diamond_pickaxe"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:diamond_pickaxe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_shovel.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_shovel.json
deleted file mode 100644
index bea8b7268..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_shovel.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:diamond_shovel"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:diamond_shovel\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_sword.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_sword.json
deleted file mode 100644
index 99609523f..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/diamond_sword.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:diamond_sword"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:diamond_sword\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/netherite_pickaxe.json b/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/netherite_pickaxe.json
deleted file mode 100644
index 022f3fabd..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_advanced/minecraft/netherite_pickaxe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_advanced",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_advanced"
- },
- "T": {
- "item": "minecraft:netherite_pickaxe"
- }
- },
- "result": {
- "item": "computercraft:turtle_advanced",
- "nbt": "{RightUpgrade:\"minecraft:netherite_pickaxe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/speaker.json b/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/speaker.json
deleted file mode 100644
index 32741abaa..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/speaker.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "computercraft:speaker"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"computercraft:speaker\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_advanced.json b/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_advanced.json
deleted file mode 100644
index 16e5ce57e..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_advanced.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "computercraft:wireless_modem_advanced"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"computercraft:wireless_modem_advanced\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_normal.json b/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_normal.json
deleted file mode 100644
index 63c6c0ae4..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/computercraft/wireless_modem_normal.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "computercraft:wireless_modem_normal"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"computercraft:wireless_modem_normal\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/crafting_table.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/crafting_table.json
deleted file mode 100644
index a190856da..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/crafting_table.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:crafting_table"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:crafting_table\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_axe.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_axe.json
deleted file mode 100644
index 10be7b2c7..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_axe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:diamond_axe"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:diamond_axe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_hoe.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_hoe.json
deleted file mode 100644
index 75ee73491..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_hoe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:diamond_hoe"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:diamond_hoe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_pickaxe.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_pickaxe.json
deleted file mode 100644
index 8aca9926a..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_pickaxe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:diamond_pickaxe"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:diamond_pickaxe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_shovel.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_shovel.json
deleted file mode 100644
index e9514e830..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_shovel.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:diamond_shovel"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:diamond_shovel\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_sword.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_sword.json
deleted file mode 100644
index ff8719f0d..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/diamond_sword.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:diamond_sword"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:diamond_sword\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/netherite_pickaxe.json b/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/netherite_pickaxe.json
deleted file mode 100644
index 8c70b6d6c..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_normal/minecraft/netherite_pickaxe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "computercraft:impostor_shaped",
- "group": "computercraft:turtle_normal",
- "pattern": [
- "#T"
- ],
- "key": {
- "#": {
- "item": "computercraft:turtle_normal"
- },
- "T": {
- "item": "minecraft:netherite_pickaxe"
- }
- },
- "result": {
- "item": "computercraft:turtle_normal",
- "nbt": "{RightUpgrade:\"minecraft:netherite_pickaxe\"}"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/turtle_upgrade.json b/src/main/resources/data/computercraft/recipes/turtle_upgrade.json
deleted file mode 100644
index cf3b74e94..000000000
--- a/src/main/resources/data/computercraft/recipes/turtle_upgrade.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "type": "computercraft:turtle_upgrade" }
diff --git a/src/main/resources/data/computercraft/recipes/wired_modem.json b/src/main/resources/data/computercraft/recipes/wired_modem.json
deleted file mode 100644
index 6b05f4698..000000000
--- a/src/main/resources/data/computercraft/recipes/wired_modem.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#R#",
- "###"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "R": {
- "tag": "c:redstone_dusts"
- }
- },
- "result": {
- "item": "computercraft:wired_modem"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/wired_modem_full_from.json b/src/main/resources/data/computercraft/recipes/wired_modem_full_from.json
deleted file mode 100644
index b9016548b..000000000
--- a/src/main/resources/data/computercraft/recipes/wired_modem_full_from.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "type": "minecraft:crafting_shapeless",
- "ingredients": [
- {
- "item": "computercraft:wired_modem"
- }
- ],
- "result": {
- "item": "computercraft:wired_modem_full"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/wired_modem_full_to.json b/src/main/resources/data/computercraft/recipes/wired_modem_full_to.json
deleted file mode 100644
index 877ca24f4..000000000
--- a/src/main/resources/data/computercraft/recipes/wired_modem_full_to.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "type": "minecraft:crafting_shapeless",
- "ingredients": [
- {
- "item": "computercraft:wired_modem_full"
- }
- ],
- "result": {
- "item": "computercraft:wired_modem"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/wireless_modem_advanced.json b/src/main/resources/data/computercraft/recipes/wireless_modem_advanced.json
deleted file mode 100644
index 8cddf9682..000000000
--- a/src/main/resources/data/computercraft/recipes/wireless_modem_advanced.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#E#",
- "###"
- ],
- "key": {
- "#": {
- "tag": "c:gold_ingots"
- },
- "E": {
- "item": "minecraft:ender_eye"
- }
- },
- "result": {
- "item": "computercraft:wireless_modem_advanced"
- }
-}
diff --git a/src/main/resources/data/computercraft/recipes/wireless_modem_normal.json b/src/main/resources/data/computercraft/recipes/wireless_modem_normal.json
deleted file mode 100644
index ddd1646dd..000000000
--- a/src/main/resources/data/computercraft/recipes/wireless_modem_normal.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "type": "minecraft:crafting_shaped",
- "pattern": [
- "###",
- "#E#",
- "###"
- ],
- "key": {
- "#": {
- "tag": "c:stones"
- },
- "E": {
- "tag": "c:ender_pearls"
- }
- },
- "result": {
- "item": "computercraft:wireless_modem_normal"
- }
-}
diff --git a/src/main/resources/data/computercraft/tags/blocks/computer.json b/src/main/resources/data/computercraft/tags/blocks/computer.json
deleted file mode 100644
index 533f54dd1..000000000
--- a/src/main/resources/data/computercraft/tags/blocks/computer.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:computer_normal",
- "computercraft:computer_advanced",
- "computercraft:computer_command"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/blocks/monitor.json b/src/main/resources/data/computercraft/tags/blocks/monitor.json
deleted file mode 100644
index 8a05bcbef..000000000
--- a/src/main/resources/data/computercraft/tags/blocks/monitor.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:monitor_normal",
- "computercraft:monitor_advanced"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/blocks/turtle.json b/src/main/resources/data/computercraft/tags/blocks/turtle.json
deleted file mode 100644
index 03d07a8cd..000000000
--- a/src/main/resources/data/computercraft/tags/blocks/turtle.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:turtle_normal",
- "computercraft:turtle_advanced"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/blocks/wired_modem.json b/src/main/resources/data/computercraft/tags/blocks/wired_modem.json
deleted file mode 100644
index 19b5ee97d..000000000
--- a/src/main/resources/data/computercraft/tags/blocks/wired_modem.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:cable",
- "computercraft:wired_modem_full"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/items/computer.json b/src/main/resources/data/computercraft/tags/items/computer.json
deleted file mode 100644
index 533f54dd1..000000000
--- a/src/main/resources/data/computercraft/tags/items/computer.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:computer_normal",
- "computercraft:computer_advanced",
- "computercraft:computer_command"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/items/monitor.json b/src/main/resources/data/computercraft/tags/items/monitor.json
deleted file mode 100644
index 8a05bcbef..000000000
--- a/src/main/resources/data/computercraft/tags/items/monitor.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:monitor_normal",
- "computercraft:monitor_advanced"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/items/turtle.json b/src/main/resources/data/computercraft/tags/items/turtle.json
deleted file mode 100644
index 03d07a8cd..000000000
--- a/src/main/resources/data/computercraft/tags/items/turtle.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:turtle_normal",
- "computercraft:turtle_advanced"
- ]
-}
diff --git a/src/main/resources/data/computercraft/tags/items/wired_modem.json b/src/main/resources/data/computercraft/tags/items/wired_modem.json
deleted file mode 100644
index be62458d8..000000000
--- a/src/main/resources/data/computercraft/tags/items/wired_modem.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:wired_modem",
- "computercraft:wired_modem_full"
- ]
-}
diff --git a/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
deleted file mode 100644
index 84f9f8953..000000000
--- a/src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "replace": false,
- "values": [
- "computercraft:monitor_normal",
- "computercraft:monitor_advanced",
- "computercraft:computer_normal",
- "computercraft:computer_advanced",
- "computercraft:computer_command",
- "computercraft:turtle_normal",
- "computercraft:turtle_advanced",
- "computercraft:speaker",
- "computercraft:disk_drive",
- "computercraft:printer",
- "computercraft:wireless_modem_normal",
- "computercraft:wireless_modem_advanced",
- "computercraft:wired_modem_full",
- "computercraft:cable"
- ]
-}
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 41a8330bb..d42d28808 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -22,8 +22,8 @@
],
"depends": {
"minecraft": ">=1.18.2-rc.1 <1.19",
- "fabricloader": ">=0.12.9",
- "fabric": "*"
+ "fabricloader": ">=0.13.3",
+ "fabric": ">=0.51.1"
},
"suggests": {
"modmenu": "*"
@@ -32,13 +32,16 @@
"environment": "*",
"entrypoints": {
"main": [
- "dan200.computercraft.ComputerCraft"
+ "dan200.computercraft.ComputerCraft::onInitialize"
],
"client": [
"dan200.computercraft.client.proxy.ComputerCraftProxyClient"
],
"modmenu": [
"dan200.computercraft.shared.integration.ModMenuIntegration"
+ ],
+ "fabric-datagen": [
+ "dan200.computercraft.data.Generators"
]
},
"mixins": [
diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta
index a6665f9e4..5726da7e4 100755
--- a/src/main/resources/pack.mcmeta
+++ b/src/main/resources/pack.mcmeta
@@ -1,6 +1,6 @@
{
"pack": {
- "pack_format": 4,
- "description": "CC:T for Fabric"
+ "pack_format": 9,
+ "description": "CC: Restitched"
}
}
diff --git a/src/main/resources/resourcepacks/overhaul b/src/main/resources/resourcepacks/overhaul
index 8785fe249..02485736d 160000
--- a/src/main/resources/resourcepacks/overhaul
+++ b/src/main/resources/resourcepacks/overhaul
@@ -1 +1 @@
-Subproject commit 8785fe24967be1b8628de834fa6ba387d2ab2d7a
+Subproject commit 02485736d0bf1ea3803d7d43e68225310bdea69e
diff --git a/src/test/java/dan200/computercraft/ContramapMatcher.java b/src/test/java/dan200/computercraft/ContramapMatcher.java
new file mode 100644
index 000000000..5126ef280
--- /dev/null
+++ b/src/test/java/dan200/computercraft/ContramapMatcher.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+
+import java.util.function.Function;
+
+public class ContramapMatcher extends TypeSafeDiagnosingMatcher
+{
+ private final String desc;
+ private final Function convert;
+ private final Matcher matcher;
+
+ public ContramapMatcher( String desc, Function convert, Matcher matcher )
+ {
+ this.desc = desc;
+ this.convert = convert;
+ this.matcher = matcher;
+ }
+
+ @Override
+ protected boolean matchesSafely( T item, Description mismatchDescription )
+ {
+ U converted = convert.apply( item );
+ if( matcher.matches( converted ) ) return true;
+
+ mismatchDescription.appendText( desc ).appendText( " " );
+ matcher.describeMismatch( converted, mismatchDescription );
+ return false;
+ }
+
+ @Override
+ public void describeTo( Description description )
+ {
+ description.appendText( desc ).appendText( " " ).appendDescriptionOf( matcher );
+ }
+
+ public static Matcher contramap( Matcher matcher, String desc, Function convert )
+ {
+ return new ContramapMatcher<>( desc, convert, matcher );
+ }
+
+ public static Matcher contramap( Matcher matcher, Function convert )
+ {
+ return new ContramapMatcher<>( "-f(_)->", convert, matcher );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java
new file mode 100644
index 000000000..1f95865bf
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java
@@ -0,0 +1,503 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.api.lua.ILuaAPI;
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.core.computer.BasicEnvironment;
+import dan200.computercraft.core.computer.Computer;
+import dan200.computercraft.core.computer.ComputerSide;
+import dan200.computercraft.core.computer.MainThread;
+import dan200.computercraft.core.filesystem.FileMount;
+import dan200.computercraft.core.filesystem.FileSystemException;
+import dan200.computercraft.core.terminal.Terminal;
+import dan200.computercraft.shared.peripheral.modem.ModemState;
+import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec3;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.function.Executable;
+import org.opentest4j.AssertionFailedError;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.channels.Channels;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static dan200.computercraft.api.lua.LuaValues.getType;
+
+/**
+ * Loads tests from {@code test-rom/spec} and executes them.
+ *
+ * This spins up a new computer and runs the {@code mcfly.lua} script. This will then load all files in the {@code spec}
+ * directory and register them with {@code cct_test.start}.
+ *
+ * From the test names, we generate a tree of {@link DynamicNode}s which queue an event and wait for
+ * {@code cct_test.submit} to be called. McFly pulls these events, executes the tests and then calls the submit method.
+ *
+ * Once all tests are done, we invoke {@code cct_test.finish} in order to mark everything as complete.
+ */
+public class ComputerTestDelegate
+{
+ private static final File REPORT_PATH = new File( "test-files/luacov.report.out" );
+
+ private static final Logger LOG = LogManager.getLogger( ComputerTestDelegate.class );
+
+ private static final long TICK_TIME = TimeUnit.MILLISECONDS.toNanos( 50 );
+
+ private static final long TIMEOUT = TimeUnit.SECONDS.toNanos( 10 );
+
+ private static final Set SKIP_KEYWORDS = new HashSet<>(
+ Arrays.asList( System.getProperty( "cc.skip_keywords", "" ).split( "," ) )
+ );
+
+ private static final Pattern KEYWORD = Pattern.compile( ":([a-z_]+)" );
+
+ private final ReentrantLock lock = new ReentrantLock();
+ private Computer computer;
+
+ private final Condition hasTests = lock.newCondition();
+ private DynamicNodeBuilder tests;
+
+ private final Condition hasRun = lock.newCondition();
+ private String currentTest;
+ private boolean runFinished;
+ private Throwable runResult;
+
+ private final Condition hasFinished = lock.newCondition();
+ private boolean finished = false;
+ private Map> finishedWith;
+
+ @BeforeEach
+ public void before() throws IOException
+ {
+ ComputerCraft.logComputerErrors = true;
+
+ if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );
+
+ Terminal term = new Terminal( 80, 100 );
+ IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
+
+ // Remove any existing files
+ List children = new ArrayList<>();
+ mount.list( "", children );
+ for( String child : children ) mount.delete( child );
+
+ // And add our startup file
+ try( WritableByteChannel channel = mount.openForWrite( "startup.lua" );
+ Writer writer = Channels.newWriter( channel, StandardCharsets.UTF_8.newEncoder(), -1 ) )
+ {
+ writer.write( "loadfile('test-rom/mcfly.lua', nil, _ENV)('test-rom/spec') cct_test.finish()" );
+ }
+
+ computer = new Computer( new BasicEnvironment( mount ), term, 0 );
+ computer.getEnvironment().setPeripheral( ComputerSide.TOP, new FakeModem() );
+ computer.addApi( new CctTestAPI() );
+
+ computer.turnOn();
+ }
+
+ @AfterEach
+ public void after() throws InterruptedException, IOException
+ {
+ try
+ {
+ LOG.info( "Finished execution" );
+ computer.queueEvent( "cct_test_run", null );
+
+ // Wait for test execution to fully finish
+ lock.lockInterruptibly();
+ try
+ {
+ long remaining = TIMEOUT;
+ while( remaining > 0 && !finished )
+ {
+ tick();
+ if( hasFinished.awaitNanos( TICK_TIME ) > 0 ) break;
+ remaining -= TICK_TIME;
+ }
+
+ if( remaining <= 0 ) throw new IllegalStateException( "Timed out waiting for finish." + dump() );
+ if( !finished ) throw new IllegalStateException( "Computer did not finish." + dump() );
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+ finally
+ {
+ // Show a dump of computer output
+ System.out.println( dump() );
+
+ // And shutdown
+ computer.shutdown();
+ }
+
+ if( finishedWith != null )
+ {
+ REPORT_PATH.getParentFile().mkdirs();
+ try( BufferedWriter writer = Files.newBufferedWriter( REPORT_PATH.toPath() ) )
+ {
+ new LuaCoverage( finishedWith ).write( writer );
+ }
+ }
+ }
+
+ @TestFactory
+ public Stream get() throws InterruptedException
+ {
+ lock.lockInterruptibly();
+ try
+ {
+ long remaining = TIMEOUT;
+ while( remaining > 0 & tests == null )
+ {
+ tick();
+ if( hasTests.awaitNanos( TICK_TIME ) > 0 ) break;
+ remaining -= TICK_TIME;
+ }
+
+ if( remaining <= 0 ) throw new IllegalStateException( "Timed out waiting for tests. " + dump() );
+ if( tests == null ) throw new IllegalStateException( "Computer did not provide any tests. " + dump() );
+ }
+ finally
+ {
+ lock.unlock();
+ }
+
+ return tests.buildChildren();
+ }
+
+ private static class DynamicNodeBuilder
+ {
+ private final String name;
+ private final Map children;
+ private final Executable executor;
+
+ DynamicNodeBuilder( String name )
+ {
+ this.name = name;
+ this.children = new HashMap<>();
+ this.executor = null;
+ }
+
+ DynamicNodeBuilder( String name, Executable executor )
+ {
+ this.name = name;
+ this.children = Collections.emptyMap();
+ this.executor = executor;
+ }
+
+ DynamicNodeBuilder get( String name )
+ {
+ DynamicNodeBuilder child = children.get( name );
+ if( child == null ) children.put( name, child = new DynamicNodeBuilder( name ) );
+ return child;
+ }
+
+ void runs( String name, Executable executor )
+ {
+ if( this.executor != null ) throw new IllegalStateException( name + " is leaf node" );
+ if( children.containsKey( name ) ) throw new IllegalStateException( "Duplicate key for " + name );
+
+ children.put( name, new DynamicNodeBuilder( name, executor ) );
+ }
+
+ boolean isActive()
+ {
+ Matcher matcher = KEYWORD.matcher( name );
+ while( matcher.find() )
+ {
+ if( SKIP_KEYWORDS.contains( matcher.group( 1 ) ) ) return false;
+ }
+
+ return true;
+ }
+
+ DynamicNode build()
+ {
+ return executor == null
+ ? DynamicContainer.dynamicContainer( name, buildChildren() )
+ : DynamicTest.dynamicTest( name, executor );
+ }
+
+ Stream buildChildren()
+ {
+ return children.values().stream()
+ .filter( DynamicNodeBuilder::isActive )
+ .map( DynamicNodeBuilder::build );
+ }
+ }
+
+ private String dump()
+ {
+ if( !computer.isOn() ) return "Computer is currently off.";
+
+ Terminal term = computer.getAPIEnvironment().getTerminal();
+ StringBuilder builder = new StringBuilder().append( "Computer is currently on.\n" );
+
+ for( int line = 0; line < term.getHeight(); line++ )
+ {
+ builder.append( String.format( "%2d | %" + term.getWidth() + "s |\n", line + 1, term.getLine( line ) ) );
+ }
+
+ computer.shutdown();
+ return builder.toString();
+ }
+
+ private void tick()
+ {
+ computer.tick();
+ MainThread.executePendingTasks();
+ }
+
+ private static String formatName( String name )
+ {
+ return name.replace( "\0", " -> " );
+ }
+
+ private static class FakeModem extends WirelessModemPeripheral
+ {
+ FakeModem()
+ {
+ super( new ModemState(), true );
+ }
+
+ @Nonnull
+ @Override
+ @SuppressWarnings( "ConstantConditions" )
+ public Level getLevel()
+ {
+ return null;
+ }
+
+ @Nonnull
+ @Override
+ public Vec3 getPosition()
+ {
+ return Vec3.ZERO;
+ }
+
+ @Override
+ public boolean equals( @Nullable IPeripheral other )
+ {
+ return this == other;
+ }
+ }
+
+ public class CctTestAPI implements ILuaAPI
+ {
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "cct_test" };
+ }
+
+ @Override
+ public void startup()
+ {
+ try
+ {
+ computer.getAPIEnvironment().getFileSystem().mount(
+ "test-rom", "test-rom",
+ BasicEnvironment.createMount( ComputerTestDelegate.class, "test-rom", "test" )
+ );
+ }
+ catch( FileSystemException e )
+ {
+ throw new IllegalStateException( e );
+ }
+ }
+
+ @LuaFunction
+ public final void start( Map, ?> tests ) throws LuaException
+ {
+ // Submit several tests and signal for #get to run
+ LOG.info( "Received tests from computer" );
+ DynamicNodeBuilder root = new DynamicNodeBuilder( "" );
+ for( Object key : tests.keySet() )
+ {
+ if( !(key instanceof String name) ) throw new LuaException( "Non-key string " + getType( key ) );
+
+ String[] parts = name.split( "\0" );
+ DynamicNodeBuilder builder = root;
+ for( int i = 0; i < parts.length - 1; i++ ) builder = builder.get( parts[i] );
+ builder.runs( parts[parts.length - 1], () -> {
+ // Run it
+ lock.lockInterruptibly();
+ try
+ {
+ // Set the current test
+ runResult = null;
+ runFinished = false;
+ currentTest = name;
+
+ // Tell the computer to run it
+ LOG.info( "Starting '{}'", formatName( name ) );
+ computer.queueEvent( "cct_test_run", new Object[] { name } );
+
+ long remaining = TIMEOUT;
+ while( remaining > 0 && computer.isOn() && !runFinished )
+ {
+ tick();
+
+ long waiting = hasRun.awaitNanos( TICK_TIME );
+ if( waiting > 0 ) break;
+ remaining -= TICK_TIME;
+ }
+
+ LOG.info( "Finished '{}'", formatName( name ) );
+
+ if( remaining <= 0 )
+ {
+ throw new IllegalStateException( "Timed out waiting for test" );
+ }
+ else if( !computer.isOn() )
+ {
+ throw new IllegalStateException( "Computer turned off mid-execution" );
+ }
+
+ if( runResult != null ) throw runResult;
+ }
+ finally
+ {
+ lock.unlock();
+ currentTest = null;
+ }
+ } );
+ }
+
+ try
+ {
+ lock.lockInterruptibly();
+ }
+ catch( InterruptedException e )
+ {
+ throw new RuntimeException( e );
+ }
+ try
+ {
+ ComputerTestDelegate.this.tests = root;
+ hasTests.signal();
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ @LuaFunction
+ public final void submit( Map, ?> tbl )
+ {
+ // Submit the result of a test, allowing the test executor to continue
+ String name = (String) tbl.get( "name" );
+ if( name == null )
+ {
+ ComputerCraft.log.error( "Oh no: {}", tbl );
+ }
+ String status = (String) tbl.get( "status" );
+ String message = (String) tbl.get( "message" );
+ String trace = (String) tbl.get( "trace" );
+
+ StringBuilder wholeMessage = new StringBuilder();
+ if( message != null ) wholeMessage.append( message );
+ if( trace != null )
+ {
+ if( wholeMessage.length() != 0 ) wholeMessage.append( '\n' );
+ wholeMessage.append( trace );
+ }
+
+ try
+ {
+ lock.lockInterruptibly();
+ }
+ catch( InterruptedException e )
+ {
+ throw new RuntimeException( e );
+ }
+ try
+ {
+ LOG.info( "'{}' finished with {}", formatName( name ), status );
+
+ // Skip if a test mismatch
+ if( !name.equals( currentTest ) )
+ {
+ LOG.warn( "Skipping test '{}', as we're currently executing '{}'", formatName( name ), formatName( currentTest ) );
+ return;
+ }
+
+ switch( status )
+ {
+ case "ok":
+ case "pending":
+ break;
+ case "fail":
+ runResult = new AssertionFailedError( wholeMessage.toString() );
+ break;
+ case "error":
+ runResult = new IllegalStateException( wholeMessage.toString() );
+ break;
+ }
+
+ runFinished = true;
+ hasRun.signal();
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ @LuaFunction
+ public final void finish( Optional> result )
+ {
+ @SuppressWarnings( "unchecked" )
+ Map> finishedResult = (Map>) result.orElse( null );
+ LOG.info( "Finished" );
+
+ // Signal to after that execution has finished
+ try
+ {
+ lock.lockInterruptibly();
+ }
+ catch( InterruptedException e )
+ {
+ throw new RuntimeException( e );
+ }
+ try
+ {
+ finished = true;
+ if( finishedResult != null ) finishedWith = finishedResult;
+
+ hasFinished.signal();
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/LuaCoverage.java b/src/test/java/dan200/computercraft/core/LuaCoverage.java
new file mode 100644
index 000000000..28435f001
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/LuaCoverage.java
@@ -0,0 +1,157 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core;
+
+import com.google.common.base.Strings;
+import dan200.computercraft.ComputerCraft;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import org.squiddev.cobalt.Prototype;
+import org.squiddev.cobalt.compiler.CompileException;
+import org.squiddev.cobalt.compiler.LuaC;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+class LuaCoverage
+{
+ private static final Path ROOT = new File( "src/main/resources/data/computercraft/lua" ).toPath();
+ private static final Path BIOS = ROOT.resolve( "bios.lua" );
+ private static final Path APIS = ROOT.resolve( "rom/apis" );
+ private static final Path SHELL = ROOT.resolve( "rom/programs/shell.lua" );
+ private static final Path MULTISHELL = ROOT.resolve( "rom/programs/advanced/multishell.lua" );
+ private static final Path TREASURE = ROOT.resolve( "treasure" );
+
+ private final Map> coverage;
+ private final String blank;
+ private final String zero;
+ private final String countFormat;
+
+ LuaCoverage( Map> coverage )
+ {
+ this.coverage = coverage;
+
+ int max = (int) coverage.values().stream()
+ .flatMapToDouble( x -> x.values().stream().mapToDouble( y -> y ) )
+ .max().orElse( 0 );
+ int maxLen = Math.max( 1, (int) Math.ceil( Math.log10( max ) ) );
+ blank = Strings.repeat( " ", maxLen + 1 );
+ zero = Strings.repeat( "*", maxLen ) + "0";
+ countFormat = "%" + (maxLen + 1) + "d";
+ }
+
+ void write( Writer out ) throws IOException
+ {
+ Files.find( ROOT, Integer.MAX_VALUE, ( path, attr ) -> attr.isRegularFile() && !path.startsWith( TREASURE ) ).forEach( path -> {
+ Path relative = ROOT.relativize( path );
+ String full = relative.toString().replace( '\\', '/' );
+ if( !full.endsWith( ".lua" ) ) return;
+
+ Map files = Stream.of(
+ coverage.remove( "/" + full ),
+ path.equals( BIOS ) ? coverage.remove( "bios.lua" ) : null,
+ path.equals( SHELL ) ? coverage.remove( "shell.lua" ) : null,
+ path.equals( MULTISHELL ) ? coverage.remove( "multishell.lua" ) : null,
+ path.startsWith( APIS ) ? coverage.remove( path.getFileName().toString() ) : null
+ )
+ .filter( Objects::nonNull )
+ .flatMap( x -> x.entrySet().stream() )
+ .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, Double::sum ) );
+
+ try
+ {
+ writeCoverageFor( out, path, files );
+ }
+ catch( IOException e )
+ {
+ throw new UncheckedIOException( e );
+ }
+ } );
+
+ for( String filename : coverage.keySet() )
+ {
+ if( filename.startsWith( "/test-rom/" ) ) continue;
+ ComputerCraft.log.warn( "Unknown file {}", filename );
+ }
+ }
+
+ private void writeCoverageFor( Writer out, Path fullName, Map visitedLines ) throws IOException
+ {
+ if( !Files.exists( fullName ) )
+ {
+ ComputerCraft.log.error( "Cannot locate file {}", fullName );
+ return;
+ }
+
+ IntSet activeLines = getActiveLines( fullName.toFile() );
+
+ out.write( "==============================================================================\n" );
+ out.write( fullName.toString().replace( '\\', '/' ) );
+ out.write( "\n" );
+ out.write( "==============================================================================\n" );
+
+ try( BufferedReader reader = Files.newBufferedReader( fullName ) )
+ {
+ String line;
+ int lineNo = 0;
+ while( (line = reader.readLine()) != null )
+ {
+ lineNo++;
+ Double count = visitedLines.get( (double) lineNo );
+ if( count != null )
+ {
+ out.write( String.format( countFormat, count.intValue() ) );
+ }
+ else if( activeLines.contains( lineNo ) )
+ {
+ out.write( zero );
+ }
+ else
+ {
+ out.write( blank );
+ }
+
+ out.write( ' ' );
+ out.write( line );
+ out.write( "\n" );
+ }
+ }
+ }
+
+ private static IntSet getActiveLines( File file ) throws IOException
+ {
+ IntSet activeLines = new IntOpenHashSet();
+ try( InputStream stream = new FileInputStream( file ) )
+ {
+ Prototype proto = LuaC.compile( stream, "@" + file.getPath() );
+ Queue queue = new ArrayDeque<>();
+ queue.add( proto );
+
+ while( (proto = queue.poll()) != null )
+ {
+ int[] lines = proto.lineinfo;
+ if( lines != null )
+ {
+ for( int line : lines )
+ {
+ activeLines.add( line );
+ }
+ }
+ if( proto.p != null ) Collections.addAll( queue, proto.p );
+ }
+ }
+ catch( CompileException e )
+ {
+ throw new IllegalStateException( "Cannot compile", e );
+ }
+
+ return activeLines;
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt b/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt
new file mode 100644
index 000000000..fd976eedb
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/apis/AsyncRunner.kt
@@ -0,0 +1,121 @@
+package dan200.computercraft.core.apis
+
+import dan200.computercraft.ComputerCraft
+import dan200.computercraft.api.lua.ILuaAPI
+import dan200.computercraft.api.lua.MethodResult
+import dan200.computercraft.api.peripheral.IPeripheral
+import dan200.computercraft.api.peripheral.IWorkMonitor
+import dan200.computercraft.core.computer.BasicEnvironment
+import dan200.computercraft.core.computer.ComputerSide
+import dan200.computercraft.core.computer.IComputerEnvironment
+import dan200.computercraft.core.filesystem.FileSystem
+import dan200.computercraft.core.terminal.Terminal
+import dan200.computercraft.core.tracking.TrackingField
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.ExperimentalTime
+
+
+abstract class NullApiEnvironment : IAPIEnvironment {
+ private val computerEnv = BasicEnvironment()
+
+ override fun getComputerID(): Int = 0
+ override fun getComputerEnvironment(): IComputerEnvironment = computerEnv
+ override fun getMainThreadMonitor(): IWorkMonitor = throw IllegalStateException("Work monitor not available")
+ override fun getTerminal(): Terminal = throw IllegalStateException("Terminal not available")
+ override fun getFileSystem(): FileSystem = throw IllegalStateException("Terminal not available")
+ override fun shutdown() {}
+ override fun reboot() {}
+ override fun setOutput(side: ComputerSide?, output: Int) {}
+ override fun getOutput(side: ComputerSide?): Int = 0
+ override fun getInput(side: ComputerSide?): Int = 0
+ override fun setBundledOutput(side: ComputerSide?, output: Int) {}
+ override fun getBundledOutput(side: ComputerSide?): Int = 0
+ override fun getBundledInput(side: ComputerSide?): Int = 0
+ override fun setPeripheralChangeListener(listener: IAPIEnvironment.IPeripheralChangeListener?) {}
+ override fun getPeripheral(side: ComputerSide?): IPeripheral? = null
+ override fun getLabel(): String? = null
+ override fun setLabel(label: String?) {}
+ override fun startTimer(ticks: Long): Int = 0
+ override fun cancelTimer(id: Int) {}
+ override fun addTrackingChange(field: TrackingField, change: Long) {}
+}
+
+class EventResult(val name: String, val args: Array)
+
+class AsyncRunner : NullApiEnvironment() {
+ private val eventStream: Channel> = Channel(Int.MAX_VALUE)
+ private val apis: MutableList = mutableListOf()
+
+ override fun queueEvent(event: String?, vararg args: Any?) {
+ ComputerCraft.log.debug("Queue event $event ${args.contentToString()}")
+ if (eventStream.trySend(arrayOf(event, *args)).isFailure) {
+ throw IllegalStateException("Queue is full")
+ }
+ }
+
+ override fun shutdown() {
+ super.shutdown()
+ eventStream.close()
+ apis.forEach { it.shutdown() }
+ }
+
+ fun addApi(api: T): T {
+ apis.add(api)
+ api.startup()
+ return api
+ }
+
+ suspend fun resultOf(toRun: MethodResult): Array {
+ var running = toRun
+ while (running.callback != null) running = runOnce(running)
+ return running.result ?: empty
+ }
+
+ private suspend fun runOnce(obj: MethodResult): MethodResult {
+ val callback = obj.callback ?: throw NullPointerException("Callback cannot be null")
+
+ val result = obj.result
+ val filter: String? = if (result.isNullOrEmpty() || result[0] !is String) {
+ null
+ } else {
+ result[0] as String
+ }
+
+ return callback.resume(pullEventImpl(filter))
+ }
+
+ private suspend fun pullEventImpl(filter: String?): Array {
+ for (event in eventStream) {
+ ComputerCraft.log.debug("Pulled event ${event.contentToString()}")
+ val eventName = event[0] as String
+ if (filter == null || eventName == filter || eventName == "terminate") return event
+ }
+
+ throw IllegalStateException("No more events")
+ }
+
+ suspend fun pullEvent(filter: String? = null): EventResult {
+ val result = pullEventImpl(filter)
+ return EventResult(result[0] as String, result.copyOfRange(1, result.size))
+ }
+
+ companion object {
+ private val empty: Array = arrayOf()
+
+ @OptIn(ExperimentalTime::class)
+ fun runTest(timeout: Duration = Duration.seconds(5), fn: suspend AsyncRunner.() -> Unit) {
+ runBlocking {
+ val runner = AsyncRunner()
+ try {
+ withTimeout(timeout) { fn(runner) }
+ } finally {
+ runner.shutdown()
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java b/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java
new file mode 100644
index 000000000..19e743ae3
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.apis;
+
+import dan200.computercraft.api.lua.*;
+import dan200.computercraft.core.asm.LuaMethod;
+import dan200.computercraft.core.asm.NamedMethod;
+
+import javax.annotation.Nonnull;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class ObjectWrapper implements ILuaContext
+{
+ private final Object object;
+ private final Map methodMap;
+
+ public ObjectWrapper( Object object )
+ {
+ this.object = object;
+ String[] dynamicMethods = object instanceof IDynamicLuaObject dynamic
+ ? Objects.requireNonNull( dynamic.getMethodNames(), "Methods cannot be null" )
+ : LuaMethod.EMPTY_METHODS;
+
+ List> methods = LuaMethod.GENERATOR.getMethods( object.getClass() );
+
+ Map methodMap = this.methodMap = new HashMap<>( methods.size() + dynamicMethods.length );
+ for( int i = 0; i < dynamicMethods.length; i++ )
+ {
+ methodMap.put( dynamicMethods[i], LuaMethod.DYNAMIC.get( i ) );
+ }
+ for( NamedMethod method : methods )
+ {
+ methodMap.put( method.getName(), method.getMethod() );
+ }
+ }
+
+ public Object[] call( String name, Object... args ) throws LuaException
+ {
+ LuaMethod method = methodMap.get( name );
+ if( method == null ) throw new IllegalStateException( "No such method '" + name + "'" );
+
+ return method.apply( object, this, new ObjectArguments( args ) ).getResult();
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public T callOf( String name, Object... args ) throws LuaException
+ {
+ return (T) call( name, args )[0];
+ }
+
+ public T callOf( Class klass, String name, Object... args ) throws LuaException
+ {
+ return klass.cast( call( name, args )[0] );
+ }
+
+ @Override
+ public long issueMainThreadTask( @Nonnull ILuaTask task )
+ {
+ throw new IllegalStateException( "Method should never queue events" );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/apis/handles/BinaryReadableHandleTest.java b/src/test/java/dan200/computercraft/core/apis/handles/BinaryReadableHandleTest.java
new file mode 100644
index 000000000..172ae4c83
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/apis/handles/BinaryReadableHandleTest.java
@@ -0,0 +1,86 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.apis.handles;
+
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.core.apis.ObjectWrapper;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class BinaryReadableHandleTest
+{
+ @Test
+ public void testReadChar() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 5 );
+ assertEquals( 'A', (int) wrapper.callOf( Integer.class, "read" ) );
+ }
+
+ @Test
+ public void testReadShortComplete() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 10 );
+ assertEquals( 5, wrapper.callOf( "read", 5 ).remaining() );
+ }
+
+ @Test
+ public void testReadShortPartial() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 5 );
+ assertEquals( 5, wrapper.callOf( "read", 10 ).remaining() );
+ }
+
+ @Test
+ public void testReadLongComplete() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 10000 );
+ assertEquals( 9000, wrapper.callOf( "read", 9000 ).length );
+ }
+
+ @Test
+ public void testReadLongPartial() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 10000 );
+ assertEquals( 10000, wrapper.callOf( "read", 11000 ).length );
+ }
+
+ @Test
+ public void testReadLongPartialSmaller() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 1000 );
+ assertEquals( 1000, wrapper.callOf( "read", 11000 ).remaining() );
+ }
+
+ @Test
+ public void testReadLine() throws LuaException
+ {
+ ObjectWrapper wrapper = new ObjectWrapper( BinaryReadableHandle.of( new ArrayByteChannel( "hello\r\nworld\r!".getBytes( StandardCharsets.UTF_8 ) ) ) );
+ assertArrayEquals( "hello".getBytes( StandardCharsets.UTF_8 ), wrapper.callOf( "readLine" ) );
+ assertArrayEquals( "world\r!".getBytes( StandardCharsets.UTF_8 ), wrapper.callOf( "readLine" ) );
+ assertNull( wrapper.call( "readLine" ) );
+ }
+
+ @Test
+ public void testReadLineTrailing() throws LuaException
+ {
+ ObjectWrapper wrapper = new ObjectWrapper( BinaryReadableHandle.of( new ArrayByteChannel( "hello\r\nworld\r!".getBytes( StandardCharsets.UTF_8 ) ) ) );
+ assertArrayEquals( "hello\r\n".getBytes( StandardCharsets.UTF_8 ), wrapper.callOf( "readLine", true ) );
+ assertArrayEquals( "world\r!".getBytes( StandardCharsets.UTF_8 ), wrapper.callOf( "readLine", true ) );
+ assertNull( wrapper.call( "readLine", true ) );
+ }
+
+ private static ObjectWrapper fromLength( int length )
+ {
+ byte[] input = new byte[length];
+ Arrays.fill( input, (byte) 'A' );
+ return new ObjectWrapper( BinaryReadableHandle.of( new ArrayByteChannel( input ) ) );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/apis/handles/EncodedReadableHandleTest.java b/src/test/java/dan200/computercraft/core/apis/handles/EncodedReadableHandleTest.java
new file mode 100644
index 000000000..7028a12d8
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/apis/handles/EncodedReadableHandleTest.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.apis.handles;
+
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.core.apis.ObjectWrapper;
+import org.junit.jupiter.api.Test;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EncodedReadableHandleTest
+{
+ @Test
+ public void testReadChar() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 5 );
+ assertEquals( "A", wrapper.callOf( "read" ) );
+ }
+
+ @Test
+ public void testReadShortComplete() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 10 );
+ assertEquals( "AAAAA", wrapper.callOf( "read", 5 ) );
+ }
+
+ @Test
+ public void testReadShortPartial() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 5 );
+ assertEquals( "AAAAA", wrapper.callOf( "read", 10 ) );
+ }
+
+
+ @Test
+ public void testReadLongComplete() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 10000 );
+ assertEquals( 9000, wrapper.callOf( "read", 9000 ).length() );
+ }
+
+ @Test
+ public void testReadLongPartial() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 10000 );
+ assertEquals( 10000, wrapper.callOf( "read", 11000 ).length() );
+ }
+
+ @Test
+ public void testReadLongPartialSmaller() throws LuaException
+ {
+ ObjectWrapper wrapper = fromLength( 1000 );
+ assertEquals( 1000, wrapper.callOf( "read", 11000 ).length() );
+ }
+
+ private static ObjectWrapper fromLength( int length )
+ {
+ char[] input = new char[length];
+ Arrays.fill( input, 'A' );
+ return new ObjectWrapper( new EncodedReadableHandle( new BufferedReader( new CharArrayReader( input ) ) ) );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java b/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java
new file mode 100644
index 000000000..77701b5d1
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.apis.http.options;
+
+import dan200.computercraft.ComputerCraft;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AddressRuleTest
+{
+ @Test
+ public void matchesPort()
+ {
+ Iterable rules = Collections.singletonList( AddressRule.parse(
+ "127.0.0.1", 8080,
+ new PartialOptions( Action.ALLOW, null, null, null, null )
+ ) );
+
+ assertEquals( apply( rules, "localhost", 8080 ).action, Action.ALLOW );
+ assertEquals( apply( rules, "localhost", 8081 ).action, Action.DENY );
+ }
+
+ @ParameterizedTest
+ @ValueSource( strings = {
+ "0.0.0.0", "[::]",
+ "localhost", "127.0.0.1.nip.io", "127.0.0.1", "[::1]",
+ "172.17.0.1", "192.168.1.114", "[0:0:0:0:0:ffff:c0a8:172]", "10.0.0.1"
+ } )
+ public void blocksLocalDomains( String domain )
+ {
+ assertEquals( apply( ComputerCraft.httpRules, domain, 80 ).action, Action.DENY );
+ }
+
+ private Options apply( Iterable rules, String host, int port )
+ {
+ return AddressRule.apply( rules, host, new InetSocketAddress( host, port ) );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/apis/http/options/TestHttpApi.kt b/src/test/java/dan200/computercraft/core/apis/http/options/TestHttpApi.kt
new file mode 100644
index 000000000..206924cc0
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/apis/http/options/TestHttpApi.kt
@@ -0,0 +1,51 @@
+package dan200.computercraft.core.apis.http.options
+
+import dan200.computercraft.ComputerCraft
+import dan200.computercraft.core.apis.AsyncRunner
+import dan200.computercraft.core.apis.HTTPAPI
+import org.junit.jupiter.api.AfterAll
+import org.junit.jupiter.api.Assertions.assertArrayEquals
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+import java.util.*
+
+@Disabled("Requires some setup locally.")
+class TestHttpApi {
+ companion object {
+ private const val WS_ADDRESS = "ws://127.0.0.1:8080"
+
+ @JvmStatic
+ @BeforeAll
+ fun before() {
+ ComputerCraft.httpRules = listOf(AddressRule.parse("*", null, Action.ALLOW.toPartial()))
+ }
+
+ @JvmStatic
+ @AfterAll
+ fun after() {
+ ComputerCraft.httpRules = Collections.unmodifiableList(
+ listOf(
+ AddressRule.parse("\$private", null, Action.DENY.toPartial()),
+ AddressRule.parse("*", null, Action.ALLOW.toPartial())
+ )
+ )
+ }
+ }
+
+ @Test
+ fun `Connects to websocket`() {
+ AsyncRunner.runTest {
+ val httpApi = addApi(HTTPAPI(this))
+
+ val result = httpApi.websocket(WS_ADDRESS, Optional.empty())
+ assertArrayEquals(arrayOf(true), result, "Should have created websocket")
+
+ val event = pullEvent()
+ assertEquals("websocket_success", event.name) {
+ "Websocket failed to connect: ${event.args.contentToString()}"
+ }
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java b/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java
new file mode 100644
index 000000000..51e98d257
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java
@@ -0,0 +1,282 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.asm;
+
+import dan200.computercraft.api.lua.*;
+import dan200.computercraft.api.peripheral.IComputerAccess;
+import dan200.computercraft.core.computer.ComputerSide;
+import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static dan200.computercraft.ContramapMatcher.contramap;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class GeneratorTest
+{
+ @Test
+ public void testBasic()
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( Basic.class );
+ assertThat( methods, contains(
+ allOf(
+ named( "go" ),
+ contramap( is( true ), "non-yielding", NamedMethod::nonYielding )
+ )
+ ) );
+ }
+
+ @Test
+ public void testIdentical()
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( Basic.class );
+ List> methods2 = LuaMethod.GENERATOR.getMethods( Basic.class );
+ assertThat( methods, sameInstance( methods2 ) );
+ }
+
+ @Test
+ public void testIdenticalMethods()
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( Basic.class );
+ List> methods2 = LuaMethod.GENERATOR.getMethods( Basic2.class );
+ assertThat( methods, contains( named( "go" ) ) );
+ assertThat( methods.get( 0 ).getMethod(), sameInstance( methods2.get( 0 ).getMethod() ) );
+ }
+
+ @Test
+ public void testEmptyClass()
+ {
+ assertThat( LuaMethod.GENERATOR.getMethods( Empty.class ), is( empty() ) );
+ }
+
+ @Test
+ public void testNonPublicClass()
+ {
+ assertThat( LuaMethod.GENERATOR.getMethods( NonPublic.class ), is( empty() ) );
+ }
+
+ @Test
+ public void testNonInstance()
+ {
+ assertThat( LuaMethod.GENERATOR.getMethods( NonInstance.class ), is( empty() ) );
+ }
+
+ @Test
+ public void testIllegalThrows()
+ {
+ assertThat( LuaMethod.GENERATOR.getMethods( IllegalThrows.class ), is( empty() ) );
+ }
+
+ @Test
+ public void testCustomNames()
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( CustomNames.class );
+ assertThat( methods, contains( named( "go1" ), named( "go2" ) ) );
+ }
+
+ @Test
+ public void testArgKinds()
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( ArgKinds.class );
+ assertThat( methods, containsInAnyOrder(
+ named( "objectArg" ), named( "intArg" ), named( "optIntArg" ),
+ named( "context" ), named( "arguments" )
+ ) );
+ }
+
+ @Test
+ public void testEnum() throws LuaException
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( EnumMethods.class );
+ assertThat( methods, containsInAnyOrder( named( "getEnum" ), named( "optEnum" ) ) );
+
+ assertThat( apply( methods, new EnumMethods(), "getEnum", "front" ), one( is( "FRONT" ) ) );
+ assertThat( apply( methods, new EnumMethods(), "optEnum", "front" ), one( is( "FRONT" ) ) );
+ assertThat( apply( methods, new EnumMethods(), "optEnum" ), one( is( "?" ) ) );
+ assertThrows( LuaException.class, () -> apply( methods, new EnumMethods(), "getEnum", "not as side" ) );
+ }
+
+ @Test
+ public void testMainThread() throws LuaException
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( MainThread.class );
+ assertThat( methods, contains( allOf(
+ named( "go" ),
+ contramap( is( false ), "non-yielding", NamedMethod::nonYielding )
+ ) ) );
+
+ assertThat( apply( methods, new MainThread(), "go" ),
+ contramap( notNullValue(), "callback", MethodResult::getCallback ) );
+ }
+
+ @Test
+ public void testUnsafe()
+ {
+ List> methods = LuaMethod.GENERATOR.getMethods( Unsafe.class );
+ assertThat( methods, contains( named( "withUnsafe" ) ) );
+ }
+
+ public static class Basic
+ {
+ @LuaFunction
+ public final void go()
+ {}
+ }
+
+ public static class Basic2 extends Basic
+ {
+ }
+
+ public static class Empty
+ {
+ }
+
+ static class NonPublic
+ {
+ @LuaFunction
+ public final void go()
+ {}
+ }
+
+ public static class NonInstance
+ {
+ @LuaFunction
+ public static void go()
+ {}
+ }
+
+ public static class IllegalThrows
+ {
+ @LuaFunction
+ public final void go() throws IOException
+ {
+ throw new IOException();
+ }
+ }
+
+ public static class CustomNames
+ {
+ @LuaFunction( { "go1", "go2" } )
+ public final void go()
+ {}
+ }
+
+ public static class ArgKinds
+ {
+ @LuaFunction
+ public final void objectArg( Object arg )
+ {}
+
+ @LuaFunction
+ public final void intArg( int arg )
+ {}
+
+ @LuaFunction
+ public final void optIntArg( Optional arg )
+ {}
+
+ @LuaFunction
+ public final void context( ILuaContext arg )
+ {}
+
+ @LuaFunction
+ public final void arguments( IArguments arg )
+ {}
+
+ @LuaFunction
+ public final void unknown( IComputerAccess arg )
+ {}
+
+ @LuaFunction
+ public final void illegalMap( Map arg )
+ {}
+
+ @LuaFunction
+ public final void optIllegalMap( Optional> arg )
+ {}
+ }
+
+ public static class EnumMethods
+ {
+ @LuaFunction
+ public final String getEnum( ComputerSide side )
+ {
+ return side.name();
+ }
+
+ @LuaFunction
+ public final String optEnum( Optional side )
+ {
+ return side.map( ComputerSide::name ).orElse( "?" );
+ }
+ }
+
+ public static class MainThread
+ {
+ @LuaFunction( mainThread = true )
+ public final void go()
+ {}
+ }
+
+ public static class Unsafe
+ {
+ @LuaFunction( unsafe = true )
+ public final void withUnsafe( LuaTable, ?> table )
+ {}
+
+ @LuaFunction
+ public final void withoutUnsafe( LuaTable, ?> table )
+ {}
+
+ @LuaFunction( unsafe = true, mainThread = true )
+ public final void invalid( LuaTable, ?> table )
+ {}
+ }
+
+ private static T find( Collection> methods, String name )
+ {
+ return methods.stream()
+ .filter( x -> x.getName().equals( name ) )
+ .map( NamedMethod::getMethod )
+ .findAny()
+ .orElseThrow( NullPointerException::new );
+ }
+
+ public static MethodResult apply( Collection> methods, Object instance, String name, Object... args ) throws LuaException
+ {
+ return find( methods, name ).apply( instance, CONTEXT, new ObjectArguments( args ) );
+ }
+
+ public static Matcher one( Matcher object )
+ {
+ return allOf(
+ contramap( nullValue(), "callback", MethodResult::getCallback ),
+ contramap( array( object ), "result", MethodResult::getResult )
+ );
+ }
+
+ public static Matcher> named( String method )
+ {
+ return contramap( is( method ), "name", NamedMethod::getName );
+ }
+
+ private static final ILuaContext CONTEXT = new ILuaContext()
+ {
+ @Override
+ public long issueMainThreadTask( @Nonnull ILuaTask task )
+ {
+ return 0;
+ }
+ };
+}
diff --git a/src/test/java/dan200/computercraft/core/asm/IntCacheTest.java b/src/test/java/dan200/computercraft/core/asm/IntCacheTest.java
new file mode 100644
index 000000000..eeded8a31
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/asm/IntCacheTest.java
@@ -0,0 +1,26 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.asm;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class IntCacheTest
+{
+ @Test
+ public void testCache()
+ {
+ IntCache c = new IntCache<>( i -> new Object() );
+ assertEquals( c.get( 0 ), c.get( 0 ) );
+ }
+
+ @Test
+ public void testMassive()
+ {
+ assertEquals( 40, new IntCache<>( i -> i ).get( 40 ) );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/asm/MethodTest.java b/src/test/java/dan200/computercraft/core/asm/MethodTest.java
new file mode 100644
index 000000000..fae4ae712
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/asm/MethodTest.java
@@ -0,0 +1,264 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.asm;
+
+import dan200.computercraft.api.lua.*;
+import dan200.computercraft.api.peripheral.IComputerAccess;
+import dan200.computercraft.api.peripheral.IDynamicPeripheral;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.core.computer.ComputerBootstrap;
+import dan200.computercraft.core.computer.ComputerSide;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collections;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class MethodTest
+{
+ @Test
+ public void testMainThread()
+ {
+ ComputerBootstrap.run( "assert(main_thread.go() == 123)", x -> x.addApi( new MainThread() ), 50 );
+ }
+
+ @Test
+ public void testMainThreadPeripheral()
+ {
+ ComputerBootstrap.run( "assert(peripheral.call('top', 'go') == 123)",
+ x -> x.getEnvironment().setPeripheral( ComputerSide.TOP, new MainThread() ),
+ 50 );
+ }
+
+ @Test
+ public void testDynamic()
+ {
+ ComputerBootstrap.run(
+ "assert(dynamic.foo() == 123, 'foo: ' .. tostring(dynamic.foo()))\n" +
+ "assert(dynamic.bar() == 321, 'bar: ' .. tostring(dynamic.bar()))",
+ x -> x.addApi( new Dynamic() ), 50 );
+ }
+
+ @Test
+ public void testDynamicPeripheral()
+ {
+ ComputerBootstrap.run(
+ "local dynamic = peripheral.wrap('top')\n" +
+ "assert(dynamic.foo() == 123, 'foo: ' .. tostring(dynamic.foo()))\n" +
+ "assert(dynamic.bar() == 321, 'bar: ' .. tostring(dynamic.bar()))",
+ x -> x.getEnvironment().setPeripheral( ComputerSide.TOP, new Dynamic() ),
+ 50
+ );
+ }
+
+ @Test
+ public void testExtra()
+ {
+ ComputerBootstrap.run( "assert(extra.go, 'go')\nassert(extra.go2, 'go2')",
+ x -> x.addApi( new ExtraObject() ),
+ 50 );
+ }
+
+ @Test
+ public void testPeripheralThrow()
+ {
+ ComputerBootstrap.run(
+ "local throw = peripheral.wrap('top')\n" +
+ "local _, err = pcall(throw.thisThread) assert(err == 'pcall: !', err)\n" +
+ "local _, err = pcall(throw.mainThread) assert(err == 'pcall: !', err)",
+ x -> x.getEnvironment().setPeripheral( ComputerSide.TOP, new PeripheralThrow() ),
+ 50
+ );
+ }
+
+ @Test
+ public void testMany()
+ {
+ ComputerBootstrap.run(
+ "assert(many.method_0)\n" +
+ "assert(many.method_39)",
+ x -> x.addApi( new ManyMethods() ), 50 );
+ }
+
+ @Test
+ public void testFunction()
+ {
+ ComputerBootstrap.run(
+ "assert(func.call()(123) == 123)",
+ x -> x.addApi( new ReturnFunction() ), 50 );
+ }
+
+ public static class MainThread implements ILuaAPI, IPeripheral
+ {
+ public final String thread = Thread.currentThread().getName();
+
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "main_thread" };
+ }
+
+ @LuaFunction( mainThread = true )
+ public final int go()
+ {
+ assertThat( Thread.currentThread().getName(), is( thread ) );
+ return 123;
+ }
+
+ @Nonnull
+ @Override
+ public String getType()
+ {
+ return "main_thread";
+ }
+
+ @Override
+ public boolean equals( @Nullable IPeripheral other )
+ {
+ return this == other;
+ }
+ }
+
+ public static class Dynamic implements IDynamicLuaObject, ILuaAPI, IDynamicPeripheral
+ {
+ @Nonnull
+ @Override
+ public String[] getMethodNames()
+ {
+ return new String[] { "foo" };
+ }
+
+ @Nonnull
+ @Override
+ public MethodResult callMethod( @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments )
+ {
+ return MethodResult.of( 123 );
+ }
+
+ @Nonnull
+ @Override
+ public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments )
+ {
+ return callMethod( context, method, arguments );
+ }
+
+ @LuaFunction
+ public final int bar()
+ {
+ return 321;
+ }
+
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "dynamic" };
+ }
+
+ @Nonnull
+ @Override
+ public String getType()
+ {
+ return "dynamic";
+ }
+
+ @Override
+ public boolean equals( @Nullable IPeripheral other )
+ {
+ return this == other;
+ }
+ }
+
+ public static class ExtraObject implements ObjectSource, ILuaAPI
+ {
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "extra" };
+ }
+
+ @LuaFunction
+ public final void go2()
+ {
+ }
+
+ @Override
+ public Iterable getExtra()
+ {
+ return Collections.singletonList( new GeneratorTest.Basic() );
+ }
+ }
+
+ public static class PeripheralThrow implements IPeripheral
+ {
+ @LuaFunction
+ public final void thisThread() throws LuaException
+ {
+ throw new LuaException( "!" );
+ }
+
+ @LuaFunction( mainThread = true )
+ public final void mainThread() throws LuaException
+ {
+ throw new LuaException( "!" );
+ }
+
+ @Nonnull
+ @Override
+ public String getType()
+ {
+ return "throw";
+ }
+
+ @Override
+ public boolean equals( @Nullable IPeripheral other )
+ {
+ return this == other;
+ }
+ }
+
+ public static class ManyMethods implements IDynamicLuaObject, ILuaAPI
+ {
+ @Nonnull
+ @Override
+ public String[] getMethodNames()
+ {
+ String[] methods = new String[40];
+ for( int i = 0; i < methods.length; i++ ) methods[i] = "method_" + i;
+ return methods;
+ }
+
+ @Nonnull
+ @Override
+ public MethodResult callMethod( @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException
+ {
+ return MethodResult.of();
+ }
+
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "many" };
+ }
+ }
+
+ public static class ReturnFunction implements ILuaAPI
+ {
+ @LuaFunction
+ public final ILuaFunction call()
+ {
+ return args -> MethodResult.of( args.getAll() );
+ }
+
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "func" };
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java b/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java
new file mode 100644
index 000000000..ea4734e26
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java
@@ -0,0 +1,165 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.computer;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.api.filesystem.IMount;
+import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.core.filesystem.FileMount;
+import dan200.computercraft.core.filesystem.JarMount;
+import dan200.computercraft.core.filesystem.MemoryMount;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+ * A very basic environment.
+ */
+public class BasicEnvironment implements IComputerEnvironment
+{
+ private final IWritableMount mount;
+
+ public BasicEnvironment()
+ {
+ this( new MemoryMount() );
+ }
+
+ public BasicEnvironment( IWritableMount mount )
+ {
+ this.mount = mount;
+ }
+
+ @Override
+ public int assignNewID()
+ {
+ return 0;
+ }
+
+ @Override
+ public IWritableMount createSaveDirMount( String path, long space )
+ {
+ return mount;
+ }
+
+ @Override
+ public int getDay()
+ {
+ return 0;
+ }
+
+ @Override
+ public double getTimeOfDay()
+ {
+ return 0;
+ }
+
+ @Override
+ public boolean isColour()
+ {
+ return true;
+ }
+
+ @Override
+ public long getComputerSpaceLimit()
+ {
+ return ComputerCraft.computerSpaceLimit;
+ }
+
+ @Nonnull
+ @Override
+ public String getHostString()
+ {
+ return "ComputerCraft 1.0 (Test environment)";
+ }
+
+ @Nonnull
+ @Override
+ public String getUserAgent()
+ {
+ return "ComputerCraft/1.0";
+ }
+
+ @Override
+ public IMount createResourceMount( String domain, String subPath )
+ {
+ return createMount( ComputerCraft.class, "data/" + domain + "/" + subPath, "main" );
+ }
+
+ @Override
+ public InputStream createResourceFile( String domain, String subPath )
+ {
+ return ComputerCraft.class.getClassLoader().getResourceAsStream( "data/" + domain + "/" + subPath );
+ }
+
+ public static IMount createMount( Class> klass, String path, String fallback )
+ {
+ File file = getContainingFile( klass );
+
+ if( file.isFile() )
+ {
+ try
+ {
+ return new JarMount( file, path );
+ }
+ catch( IOException e )
+ {
+ throw new UncheckedIOException( e );
+ }
+ }
+ else
+ {
+ File wholeFile = new File( file, path );
+
+ // If we don't exist, walk up the tree looking for resource folders
+ File baseFile = file;
+ while( baseFile != null && !wholeFile.exists() )
+ {
+ baseFile = baseFile.getParentFile();
+ wholeFile = new File( baseFile, "src/" + fallback + "/resources/" + path );
+ }
+
+ if( !wholeFile.exists() ) throw new IllegalStateException( "Cannot find ROM mount at " + file );
+
+ return new FileMount( wholeFile, 0 );
+ }
+ }
+
+
+ private static File getContainingFile( Class> klass )
+ {
+ String path = klass.getProtectionDomain().getCodeSource().getLocation().getPath();
+ int bangIndex = path.indexOf( "!" );
+
+ // Plain old file, so step up from dan200.computercraft.
+ if( bangIndex < 0 ) return new File( path );
+
+ path = path.substring( 0, bangIndex );
+ URL url;
+ try
+ {
+ url = new URL( path );
+ }
+ catch( MalformedURLException e )
+ {
+ throw new IllegalStateException( e );
+ }
+
+ try
+ {
+ return new File( url.toURI() );
+ }
+ catch( URISyntaxException e )
+ {
+ return new File( url.getPath() );
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/computer/ComputerBootstrap.java b/src/test/java/dan200/computercraft/core/computer/ComputerBootstrap.java
new file mode 100644
index 000000000..e22e5dad8
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/computer/ComputerBootstrap.java
@@ -0,0 +1,138 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.computer;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.api.lua.IArguments;
+import dan200.computercraft.api.lua.ILuaAPI;
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.LuaFunction;
+import dan200.computercraft.core.filesystem.MemoryMount;
+import dan200.computercraft.core.terminal.Terminal;
+import org.junit.jupiter.api.Assertions;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+/**
+ * Helper class to run a program on a computer.
+ */
+public class ComputerBootstrap
+{
+ private static final int TPS = 20;
+ public static final int MAX_TIME = 10;
+
+ public static void run( String program, Consumer setup, int maxTimes )
+ {
+ MemoryMount mount = new MemoryMount()
+ .addFile( "test.lua", program )
+ .addFile( "startup.lua", "assertion.assert(pcall(loadfile('test.lua', nil, _ENV))) os.shutdown()" );
+
+ run( mount, setup, maxTimes );
+ }
+
+ public static void run( String program, int maxTimes )
+ {
+ run( program, x -> { }, maxTimes );
+ }
+
+ public static void run( IWritableMount mount, Consumer setup, int maxTicks )
+ {
+ ComputerCraft.logComputerErrors = true;
+ ComputerCraft.maxMainComputerTime = ComputerCraft.maxMainGlobalTime = Integer.MAX_VALUE;
+
+ Terminal term = new Terminal( ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight );
+ final Computer computer = new Computer( new BasicEnvironment( mount ), term, 0 );
+
+ AssertApi api = new AssertApi();
+ computer.addApi( api );
+
+ setup.accept( computer );
+
+ try
+ {
+ computer.turnOn();
+ boolean everOn = false;
+
+ for( int tick = 0; tick < TPS * maxTicks; tick++ )
+ {
+ long start = System.currentTimeMillis();
+
+ computer.tick();
+ MainThread.executePendingTasks();
+
+ if( api.message != null )
+ {
+ ComputerCraft.log.debug( "Shutting down due to error" );
+ computer.shutdown();
+ Assertions.fail( api.message );
+ return;
+ }
+
+ long remaining = (1000 / TPS) - (System.currentTimeMillis() - start);
+ if( remaining > 0 ) Thread.sleep( remaining );
+
+ // Break if the computer was once on, and is now off.
+ everOn |= computer.isOn();
+ if( (everOn || tick > TPS) && !computer.isOn() ) break;
+ }
+
+ if( computer.isOn() || !api.didAssert )
+ {
+ StringBuilder builder = new StringBuilder().append( "Did not correctly [" );
+ if( !api.didAssert ) builder.append( " assert" );
+ if( computer.isOn() ) builder.append( " shutdown" );
+ builder.append( " ]\n" );
+
+ for( int line = 0; line < 19; line++ )
+ {
+ builder.append( String.format( "%2d | %" + term.getWidth() + "s |\n", line + 1, term.getLine( line ) ) );
+ }
+
+ computer.shutdown();
+ Assertions.fail( builder.toString() );
+ }
+ }
+ catch( InterruptedException ignored )
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public static class AssertApi implements ILuaAPI
+ {
+ boolean didAssert;
+ String message;
+
+ @Override
+ public String[] getNames()
+ {
+ return new String[] { "assertion" };
+ }
+
+ @LuaFunction
+ public final void log( IArguments arguments )
+ {
+ ComputerCraft.log.info( "[Computer] {}", Arrays.toString( arguments.getAll() ) );
+ }
+
+ @LuaFunction( "assert" )
+ public final Object[] doAssert( IArguments arguments ) throws LuaException
+ {
+ didAssert = true;
+
+ Object arg = arguments.get( 0 );
+ if( arg == null || arg == Boolean.FALSE )
+ {
+ message = arguments.optString( 1, "Assertion failed" );
+ throw new LuaException( message );
+ }
+
+ return arguments.getAll();
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/computer/ComputerTest.java b/src/test/java/dan200/computercraft/core/computer/ComputerTest.java
new file mode 100644
index 000000000..eee9c2303
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/computer/ComputerTest.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.computer;
+
+import com.google.common.io.CharStreams;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import static java.time.Duration.ofSeconds;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+
+public class ComputerTest
+{
+ @Test
+ public void testTimeout()
+ {
+ assertTimeoutPreemptively( ofSeconds( 20 ), () -> {
+ try
+ {
+ ComputerBootstrap.run( "print('Hello') while true do end", ComputerBootstrap.MAX_TIME );
+ }
+ catch( AssertionError e )
+ {
+ if( e.getMessage().equals( "test.lua:1: Too long without yielding" ) ) return;
+ throw e;
+ }
+
+ Assertions.fail( "Expected computer to timeout" );
+ } );
+ }
+
+ public static void main( String[] args ) throws Exception
+ {
+ InputStream stream = ComputerTest.class.getClassLoader().getResourceAsStream( "benchmark.lua" );
+ try( InputStreamReader reader = new InputStreamReader( Objects.requireNonNull( stream ), StandardCharsets.UTF_8 ) )
+ {
+ String contents = CharStreams.toString( reader );
+ ComputerBootstrap.run( contents, 1000 );
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/filesystem/FileSystemTest.java b/src/test/java/dan200/computercraft/core/filesystem/FileSystemTest.java
new file mode 100644
index 000000000..a2a25ed14
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/filesystem/FileSystemTest.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.filesystem;
+
+import com.google.common.io.Files;
+import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.core.apis.ObjectWrapper;
+import dan200.computercraft.core.apis.handles.EncodedWritableHandle;
+import org.junit.jupiter.api.Test;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class FileSystemTest
+{
+ private static final File ROOT = new File( "test-files/filesystem" );
+ private static final long CAPACITY = 1000000;
+
+ private static FileSystem mkFs() throws FileSystemException
+ {
+ IWritableMount writableMount = new FileMount( ROOT, CAPACITY );
+ return new FileSystem( "hdd", writableMount );
+
+ }
+
+ /**
+ * Ensures writing a file truncates it.
+ *
+ * @throws FileSystemException When the file system cannot be constructed.
+ * @throws LuaException When Lua functions fail.
+ * @throws IOException When reading and writing from strings
+ */
+ @Test
+ public void testWriteTruncates() throws FileSystemException, LuaException, IOException
+ {
+ FileSystem fs = mkFs();
+
+ {
+ FileSystemWrapper writer = fs.openForWrite( "out.txt", false, EncodedWritableHandle::openUtf8 );
+ ObjectWrapper wrapper = new ObjectWrapper( new EncodedWritableHandle( writer.get(), writer ) );
+ wrapper.call( "write", "This is a long line" );
+ wrapper.call( "close" );
+ }
+
+ assertEquals( "This is a long line", Files.asCharSource( new File( ROOT, "out.txt" ), StandardCharsets.UTF_8 ).read() );
+
+ {
+ FileSystemWrapper writer = fs.openForWrite( "out.txt", false, EncodedWritableHandle::openUtf8 );
+ ObjectWrapper wrapper = new ObjectWrapper( new EncodedWritableHandle( writer.get(), writer ) );
+ wrapper.call( "write", "Tiny line" );
+ wrapper.call( "close" );
+ }
+
+ assertEquals( "Tiny line", Files.asCharSource( new File( ROOT, "out.txt" ), StandardCharsets.UTF_8 ).read() );
+ }
+
+ @Test
+ public void testUnmountCloses() throws FileSystemException
+ {
+ FileSystem fs = mkFs();
+ IWritableMount mount = new FileMount( new File( ROOT, "child" ), CAPACITY );
+ fs.mountWritable( "disk", "disk", mount );
+
+ FileSystemWrapper writer = fs.openForWrite( "disk/out.txt", false, EncodedWritableHandle::openUtf8 );
+ ObjectWrapper wrapper = new ObjectWrapper( new EncodedWritableHandle( writer.get(), writer ) );
+
+ fs.unmount( "disk" );
+
+ LuaException err = assertThrows( LuaException.class, () -> wrapper.call( "write", "Tiny line" ) );
+ assertEquals( "attempt to use a closed file", err.getMessage() );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java b/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java
new file mode 100644
index 000000000..c084065cd
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java
@@ -0,0 +1,108 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.filesystem;
+
+import com.google.common.io.ByteStreams;
+import dan200.computercraft.api.filesystem.IMount;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class JarMountTest
+{
+ private static final File ZIP_FILE = new File( "test-files/jar-mount.zip" );
+
+ private static final FileTime MODIFY_TIME = FileTime.from( Instant.EPOCH.plus( 2, ChronoUnit.DAYS ) );
+
+ @BeforeAll
+ public static void before() throws IOException
+ {
+ ZIP_FILE.getParentFile().mkdirs();
+
+ try( ZipOutputStream stream = new ZipOutputStream( new FileOutputStream( ZIP_FILE ) ) )
+ {
+ stream.putNextEntry( new ZipEntry( "dir/" ) );
+ stream.closeEntry();
+
+ stream.putNextEntry( new ZipEntry( "dir/file.lua" ).setLastModifiedTime( MODIFY_TIME ) );
+ stream.write( "print('testing')".getBytes( StandardCharsets.UTF_8 ) );
+ stream.closeEntry();
+ }
+ }
+
+ @Test
+ public void mountsDir() throws IOException
+ {
+ IMount mount = new JarMount( ZIP_FILE, "dir" );
+ assertTrue( mount.isDirectory( "" ), "Root should be directory" );
+ assertTrue( mount.exists( "file.lua" ), "File should exist" );
+ }
+
+ @Test
+ public void mountsFile() throws IOException
+ {
+ IMount mount = new JarMount( ZIP_FILE, "dir/file.lua" );
+ assertTrue( mount.exists( "" ), "Root should exist" );
+ assertFalse( mount.isDirectory( "" ), "Root should be a file" );
+ }
+
+ @Test
+ public void opensFileFromFile() throws IOException
+ {
+ IMount mount = new JarMount( ZIP_FILE, "dir/file.lua" );
+ byte[] contents;
+ try( ReadableByteChannel stream = mount.openForRead( "" ) )
+ {
+ contents = ByteStreams.toByteArray( Channels.newInputStream( stream ) );
+ }
+
+ assertEquals( new String( contents, StandardCharsets.UTF_8 ), "print('testing')" );
+ }
+
+ @Test
+ public void opensFileFromDir() throws IOException
+ {
+ IMount mount = new JarMount( ZIP_FILE, "dir" );
+ byte[] contents;
+ try( ReadableByteChannel stream = mount.openForRead( "file.lua" ) )
+ {
+ contents = ByteStreams.toByteArray( Channels.newInputStream( stream ) );
+ }
+
+ assertEquals( new String( contents, StandardCharsets.UTF_8 ), "print('testing')" );
+ }
+
+ @Test
+ public void fileAttributes() throws IOException
+ {
+ BasicFileAttributes attributes = new JarMount( ZIP_FILE, "dir" ).getAttributes( "file.lua" );
+ assertFalse( attributes.isDirectory() );
+ assertEquals( "print('testing')".length(), attributes.size() );
+ assertEquals( MODIFY_TIME, attributes.lastModifiedTime() );
+ }
+
+ @Test
+ public void directoryAttributes() throws IOException
+ {
+ BasicFileAttributes attributes = new JarMount( ZIP_FILE, "dir" ).getAttributes( "" );
+ assertTrue( attributes.isDirectory() );
+ assertEquals( 0, attributes.size() );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/filesystem/MemoryMount.java b/src/test/java/dan200/computercraft/core/filesystem/MemoryMount.java
new file mode 100644
index 000000000..dc785de3a
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/filesystem/MemoryMount.java
@@ -0,0 +1,148 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.filesystem;
+
+import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.core.apis.handles.ArrayByteChannel;
+
+import javax.annotation.Nonnull;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.*;
+
+/**
+ * In-memory file mounts.
+ */
+public class MemoryMount implements IWritableMount
+{
+ private final Map files = new HashMap<>();
+ private final Set directories = new HashSet<>();
+
+ public MemoryMount()
+ {
+ directories.add( "" );
+ }
+
+
+ @Override
+ public void makeDirectory( @Nonnull String path )
+ {
+ File file = new File( path );
+ while( file != null )
+ {
+ directories.add( file.getPath() );
+ file = file.getParentFile();
+ }
+ }
+
+ @Override
+ public void delete( @Nonnull String path )
+ {
+ if( files.containsKey( path ) )
+ {
+ files.remove( path );
+ }
+ else
+ {
+ directories.remove( path );
+ for( String file : files.keySet().toArray( new String[0] ) )
+ {
+ if( file.startsWith( path ) )
+ {
+ files.remove( file );
+ }
+ }
+
+ File parent = new File( path ).getParentFile();
+ if( parent != null ) delete( parent.getPath() );
+ }
+ }
+
+ @Nonnull
+ @Override
+ public WritableByteChannel openForWrite( @Nonnull final String path )
+ {
+ return Channels.newChannel( new ByteArrayOutputStream()
+ {
+ @Override
+ public void close() throws IOException
+ {
+ super.close();
+ files.put( path, toByteArray() );
+ }
+ } );
+ }
+
+ @Nonnull
+ @Override
+ public WritableByteChannel openForAppend( @Nonnull final String path ) throws IOException
+ {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream()
+ {
+ @Override
+ public void close() throws IOException
+ {
+ super.close();
+ files.put( path, toByteArray() );
+ }
+ };
+
+ byte[] current = files.get( path );
+ if( current != null ) stream.write( current );
+
+ return Channels.newChannel( stream );
+ }
+
+ @Override
+ public long getRemainingSpace()
+ {
+ return 1000000L;
+ }
+
+ @Override
+ public boolean exists( @Nonnull String path )
+ {
+ return files.containsKey( path ) || directories.contains( path );
+ }
+
+ @Override
+ public boolean isDirectory( @Nonnull String path )
+ {
+ return directories.contains( path );
+ }
+
+ @Override
+ public void list( @Nonnull String path, @Nonnull List files )
+ {
+ for( String file : this.files.keySet() )
+ {
+ if( file.startsWith( path ) ) files.add( file.substring( path.length() + 1 ) );
+ }
+ }
+
+ @Override
+ public long getSize( @Nonnull String path )
+ {
+ throw new RuntimeException( "Not implemented" );
+ }
+
+ @Nonnull
+ @Override
+ public ReadableByteChannel openForRead( @Nonnull String path )
+ {
+ return new ArrayByteChannel( files.get( path ) );
+ }
+
+ public MemoryMount addFile( String file, String contents )
+ {
+ files.put( file, contents.getBytes() );
+ return this;
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/filesystem/ResourceMountTest.java b/src/test/java/dan200/computercraft/core/filesystem/ResourceMountTest.java
new file mode 100644
index 000000000..daf93b599
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/filesystem/ResourceMountTest.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.filesystem;
+
+import dan200.computercraft.api.filesystem.IMount;
+import net.minecraft.Util;
+import net.minecraft.server.packs.FolderPackResources;
+import net.minecraft.server.packs.PackType;
+import net.minecraft.server.packs.resources.ReloadableResourceManager;
+import net.minecraft.util.Unit;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ResourceMountTest
+{
+ private IMount mount;
+
+ @BeforeEach
+ public void before()
+ {
+ ReloadableResourceManager manager = new ReloadableResourceManager( PackType.SERVER_DATA );
+ CompletableFuture done = new CompletableFuture<>();
+ manager.createReload( Util.backgroundExecutor(), Util.backgroundExecutor(), done, List.of(
+ new FolderPackResources( new File( "src/main/resources" ) )
+ ) );
+
+ mount = ResourceMount.get( "computercraft", "lua/rom", manager );
+ }
+
+ @Test
+ public void testList() throws IOException
+ {
+ List files = new ArrayList<>();
+ mount.list( "", files );
+ files.sort( Comparator.naturalOrder() );
+
+ assertEquals(
+ Arrays.asList( "apis", "autorun", "help", "modules", "motd.txt", "programs", "startup.lua" ),
+ files
+ );
+ }
+
+ @Test
+ public void testExists() throws IOException
+ {
+ assertTrue( mount.exists( "" ) );
+ assertTrue( mount.exists( "startup.lua" ) );
+ assertTrue( mount.exists( "programs/fun/advanced/paint.lua" ) );
+
+ assertFalse( mount.exists( "programs/fun/advance/paint.lua" ) );
+ assertFalse( mount.exists( "programs/fun/advanced/paint.lu" ) );
+ }
+
+ @Test
+ public void testIsDir() throws IOException
+ {
+ assertTrue( mount.isDirectory( "" ) );
+ }
+
+ @Test
+ public void testIsFile() throws IOException
+ {
+ assertFalse( mount.isDirectory( "startup.lua" ) );
+ }
+
+ @Test
+ public void testSize() throws IOException
+ {
+ assertNotEquals( mount.getSize( "startup.lua" ), 0 );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/terminal/TerminalMatchers.java b/src/test/java/dan200/computercraft/core/terminal/TerminalMatchers.java
new file mode 100644
index 000000000..e72932472
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/terminal/TerminalMatchers.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.terminal;
+
+import dan200.computercraft.ContramapMatcher;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+
+import java.util.Arrays;
+
+public class TerminalMatchers
+{
+ public static Matcher textColourMatches( String[] x )
+ {
+ return linesMatch( "text colour", Terminal::getTextColourLine, x );
+ }
+
+ public static Matcher backgroundColourMatches( String[] x )
+ {
+ return linesMatch( "background colour", Terminal::getBackgroundColourLine, x );
+ }
+
+ public static Matcher textMatches( String[] x )
+ {
+ return linesMatch( "text", Terminal::getLine, x );
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public static Matcher linesMatch( String kind, LineProvider getLine, String[] lines )
+ {
+ return linesMatchWith( kind, getLine, Arrays.stream( lines ).map( Matchers::equalTo ).toArray( Matcher[]::new ) );
+ }
+
+ public static Matcher linesMatchWith( String kind, LineProvider getLine, Matcher[] lines )
+ {
+ return new ContramapMatcher<>( kind, terminal -> {
+ String[] termLines = new String[terminal.getHeight()];
+ for( int i = 0; i < termLines.length; i++ ) termLines[i] = getLine.getLine( terminal, i ).toString();
+ return termLines;
+ }, Matchers.array( lines ) );
+ }
+
+ @FunctionalInterface
+ public interface LineProvider
+ {
+ TextBuffer getLine( Terminal terminal, int line );
+ }
+
+}
diff --git a/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java b/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java
new file mode 100644
index 000000000..cc787b743
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/terminal/TerminalTest.java
@@ -0,0 +1,717 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.terminal;
+
+import dan200.computercraft.shared.util.Colour;
+import dan200.computercraft.utils.CallCounter;
+import io.netty.buffer.Unpooled;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.FriendlyByteBuf;
+import org.hamcrest.Matcher;
+import org.junit.jupiter.api.Test;
+
+import static dan200.computercraft.core.terminal.TerminalMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.allOf;
+import static org.junit.jupiter.api.Assertions.*;
+
+class TerminalTest
+{
+ @Test
+ void testCreation()
+ {
+ Terminal terminal = new Terminal( 16, 9 );
+ assertEquals( 16, terminal.getWidth() );
+ assertEquals( 9, terminal.getHeight() );
+ }
+
+ @Test
+ void testSetAndGetLine()
+ {
+ Terminal terminal = new Terminal( 16, 9 );
+ terminal.setLine( 1, "ABCDEFGHIJKLMNOP", "0123456789abcdef", "fedcba9876543210" );
+ assertEquals( "ABCDEFGHIJKLMNOP", terminal.getLine( 1 ).toString() );
+ assertEquals( "0123456789abcdef", terminal.getTextColourLine( 1 ).toString() );
+ assertEquals( "fedcba9876543210", terminal.getBackgroundColourLine( 1 ).toString() );
+ }
+
+ @Test
+ void testGetLineOutOfBounds()
+ {
+ Terminal terminal = new Terminal( 16, 9 );
+
+ assertNull( terminal.getLine( -5 ) );
+ assertNull( terminal.getLine( 12 ) );
+
+ assertNull( terminal.getTextColourLine( -5 ) );
+ assertNull( terminal.getTextColourLine( 12 ) );
+
+ assertNull( terminal.getBackgroundColourLine( -5 ) );
+ assertNull( terminal.getBackgroundColourLine( 12 ) );
+ }
+
+ @Test
+ void testDefaults()
+ {
+ Terminal terminal = new Terminal( 16, 9 );
+ assertEquals( 0, terminal.getCursorX() );
+ assertEquals( 0, terminal.getCursorY() );
+ assertFalse( terminal.getCursorBlink() );
+ assertEquals( 0, terminal.getTextColour() );
+ assertEquals( 15, terminal.getBackgroundColour() );
+ }
+
+ @Test
+ void testDefaultTextBuffer()
+ {
+ assertThat( new Terminal( 4, 3 ), textMatches( new String[] {
+ " ",
+ " ",
+ " ",
+ } ) );
+ }
+
+ @Test
+ void testDefaultTextColourBuffer()
+ {
+ assertThat( new Terminal( 4, 3 ), textColourMatches( new String[] {
+ "0000",
+ "0000",
+ "0000",
+ } ) );
+ }
+
+ @Test
+ void testDefaultBackgroundColourBuffer()
+ {
+ assertThat( new Terminal( 4, 3 ), TerminalMatchers.backgroundColourMatches( new String[] {
+ "ffff",
+ "ffff",
+ "ffff",
+ } ) );
+ }
+
+ @Test
+ void testZeroSizeBuffers()
+ {
+ String[] x = new String[0];
+ assertThat( new Terminal( 0, 0 ), allOf(
+ textMatches( new String[0] ),
+ textColourMatches( x ),
+ TerminalMatchers.backgroundColourMatches( x )
+ ) );
+ }
+
+ @Test
+ void testResizeWidthAndHeight()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ terminal.setLine( 0, "test", "aaaa", "eeee" );
+ callCounter.reset();
+
+ terminal.resize( 5, 4 );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ "test ",
+ " ",
+ " ",
+ " ",
+ } ),
+ textColourMatches( new String[] {
+ "aaaa0",
+ "00000",
+ "00000",
+ "00000",
+ } ), TerminalMatchers.backgroundColourMatches( new String[] {
+ "eeeef",
+ "fffff",
+ "fffff",
+ "fffff",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testResizeSmaller()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ terminal.setLine( 0, "test", "aaaa", "eeee" );
+ terminal.setLine( 1, "smol", "aaaa", "eeee" );
+ terminal.setLine( 2, "term", "aaaa", "eeee" );
+ callCounter.reset();
+
+ terminal.resize( 2, 2 );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ "te",
+ "sm",
+ } ),
+ textColourMatches( new String[] {
+ "aa",
+ "aa",
+ } ),
+ TerminalMatchers.backgroundColourMatches( new String[] {
+ "ee",
+ "ee",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testResizeWithSameDimensions()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ TerminalBufferSnapshot old = new TerminalBufferSnapshot( terminal );
+ terminal.resize( 4, 3 );
+
+ assertThat( "Terminal should be unchanged", terminal, old.matches() );
+
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testSetAndGetCursorPos()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setCursorPos( 2, 1 );
+
+ assertEquals( 2, terminal.getCursorX() );
+ assertEquals( 1, terminal.getCursorY() );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testSetCursorPosUnchanged()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setCursorPos( 2, 1 );
+ callCounter.reset();
+ terminal.setCursorPos( 2, 1 );
+
+ assertEquals( 2, terminal.getCursorX() );
+ assertEquals( 1, terminal.getCursorY() );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testSetCursorBlink()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setCursorBlink( true );
+
+ assertTrue( terminal.getCursorBlink() );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testSetCursorBlinkUnchanged()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setCursorBlink( true );
+ callCounter.reset();
+ terminal.setCursorBlink( true );
+
+ assertTrue( terminal.getCursorBlink() );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testSetTextColour()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setTextColour( 5 );
+
+ assertEquals( terminal.getTextColour(), 5 );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testSetTextColourUnchanged()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setTextColour( 5 );
+ callCounter.reset();
+ terminal.setTextColour( 5 );
+
+ assertEquals( terminal.getTextColour(), 5 );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testSetBackgroundColour()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setBackgroundColour( 5 );
+
+ assertEquals( terminal.getBackgroundColour(), 5 );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testSetBackgroundColourUnchanged()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setBackgroundColour( 5 );
+ callCounter.reset();
+ terminal.setBackgroundColour( 5 );
+
+ assertEquals( terminal.getBackgroundColour(), 5 );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testBlitFromOrigin()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.blit( "test", "1234", "abcd" );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ "test",
+ " ",
+ " ",
+ } ), textColourMatches( new String[] {
+ "1234",
+ "0000",
+ "0000",
+ } ), backgroundColourMatches( new String[] {
+ "abcd",
+ "ffff",
+ "ffff",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testBlitWithOffset()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setCursorPos( 2, 1 );
+ callCounter.reset();
+ terminal.blit( "hi", "11", "ee" );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ " ",
+ " hi",
+ " ",
+ } ),
+ textColourMatches( new String[] {
+ "0000",
+ "0011",
+ "0000",
+ } ),
+ backgroundColourMatches( new String[] {
+ "ffff",
+ "ffee",
+ "ffff",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testBlitOutOfBounds()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ TerminalBufferSnapshot old = new TerminalBufferSnapshot( terminal );
+
+ terminal.setCursorPos( 2, -5 );
+ callCounter.reset();
+ terminal.blit( "hi", "11", "ee" );
+ assertThat( terminal, old.matches() );
+ callCounter.assertNotCalled();
+
+ terminal.setCursorPos( 2, 5 );
+ callCounter.reset();
+ terminal.blit( "hi", "11", "ee" );
+ assertThat( terminal, old.matches() );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testWriteFromOrigin()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.write( "test" );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ "test",
+ " ",
+ " ",
+ } ), textColourMatches( new String[] {
+ "0000",
+ "0000",
+ "0000",
+ } ), backgroundColourMatches( new String[] {
+ "ffff",
+ "ffff",
+ "ffff",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testWriteWithOffset()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setCursorPos( 2, 1 );
+ callCounter.reset();
+ terminal.write( "hi" );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ " ",
+ " hi",
+ " ",
+ } ),
+ textColourMatches( new String[] {
+ "0000",
+ "0000",
+ "0000",
+ } ),
+ backgroundColourMatches( new String[] {
+ "ffff",
+ "ffff",
+ "ffff",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testWriteOutOfBounds()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ TerminalBufferSnapshot old = new TerminalBufferSnapshot( terminal );
+
+ terminal.setCursorPos( 2, -5 );
+ callCounter.reset();
+ terminal.write( "hi" );
+
+ assertThat( terminal, old.matches() );
+ callCounter.assertNotCalled();
+
+ terminal.setCursorPos( 2, 5 );
+ callCounter.reset();
+ terminal.write( "hi" );
+ assertThat( terminal, old.matches() );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testScrollUp()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ callCounter.reset();
+ terminal.scroll( 1 );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ "test",
+ " ",
+ " ",
+ } ),
+ textColourMatches( new String[] {
+ "1111",
+ "0000",
+ "0000",
+ } ),
+ backgroundColourMatches( new String[] {
+ "eeee",
+ "ffff",
+ "ffff",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testScrollDown()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ callCounter.reset();
+ terminal.scroll( -1 );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] {
+ " ",
+ " ",
+ "test",
+ } ),
+ textColourMatches( new String[] {
+ "0000",
+ "0000",
+ "1111",
+ } ),
+ backgroundColourMatches( new String[] {
+ "ffff",
+ "ffff",
+ "eeee",
+ } )
+ ) );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testScrollZeroLinesUnchanged()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ TerminalBufferSnapshot old = new TerminalBufferSnapshot( terminal );
+ callCounter.reset();
+ terminal.scroll( 0 );
+
+ assertThat( terminal, old.matches() );
+
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testClear()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ TerminalBufferSnapshot old = new TerminalBufferSnapshot( terminal );
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ callCounter.reset();
+ terminal.clear();
+
+ assertThat( terminal, old.matches() );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testClearLine()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ TerminalBufferSnapshot old = new TerminalBufferSnapshot( terminal );
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ terminal.setCursorPos( 0, 1 );
+ callCounter.reset();
+ terminal.clearLine();
+
+ assertThat( terminal, old.matches() );
+
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testClearLineOutOfBounds()
+ {
+ CallCounter callCounter = new CallCounter();
+ Terminal terminal = new Terminal( 4, 3, callCounter );
+ TerminalBufferSnapshot old;
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ old = new TerminalBufferSnapshot( terminal );
+ terminal.setCursorPos( 0, -5 );
+ callCounter.reset();
+ terminal.clearLine();
+ assertThat( terminal, old.matches() );
+ callCounter.assertNotCalled();
+
+ terminal.setLine( 1, "test", "1111", "eeee" );
+ old = new TerminalBufferSnapshot( terminal );
+ terminal.setCursorPos( 0, 5 );
+ callCounter.reset();
+ terminal.clearLine();
+ assertThat( terminal, old.matches() );
+ callCounter.assertNotCalled();
+ }
+
+ @Test
+ void testPacketBufferRoundtrip()
+ {
+ Terminal writeTerminal = new Terminal( 2, 1 );
+
+ writeTerminal.blit( "hi", "11", "ee" );
+ writeTerminal.setCursorPos( 2, 5 );
+ writeTerminal.setTextColour( 3 );
+ writeTerminal.setBackgroundColour( 5 );
+
+ FriendlyByteBuf packetBuffer = new FriendlyByteBuf( Unpooled.buffer() );
+ writeTerminal.write( packetBuffer );
+
+ CallCounter callCounter = new CallCounter();
+ Terminal readTerminal = new Terminal( 2, 1, callCounter );
+ packetBuffer.writeBytes( packetBuffer );
+ readTerminal.read( packetBuffer );
+
+ assertThat( readTerminal, allOf(
+ textMatches( new String[] { "hi", } ),
+ textColourMatches( new String[] { "11", } ),
+ backgroundColourMatches( new String[] { "ee", } )
+ ) );
+
+ assertEquals( 2, readTerminal.getCursorX() );
+ assertEquals( 5, readTerminal.getCursorY() );
+ assertEquals( 3, readTerminal.getTextColour() );
+ assertEquals( 5, readTerminal.getBackgroundColour() );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testNbtRoundtrip()
+ {
+ Terminal writeTerminal = new Terminal( 10, 5 );
+ writeTerminal.blit( "hi", "11", "ee" );
+ writeTerminal.setCursorPos( 2, 5 );
+ writeTerminal.setTextColour( 3 );
+ writeTerminal.setBackgroundColour( 5 );
+
+ CompoundTag nbt = new CompoundTag();
+ writeTerminal.writeToNBT( nbt );
+
+ CallCounter callCounter = new CallCounter();
+ Terminal readTerminal = new Terminal( 2, 1, callCounter );
+
+ readTerminal.readFromNBT( nbt );
+
+ assertThat( readTerminal, allOf(
+ textMatches( new String[] { "hi", } ),
+ textColourMatches( new String[] { "11", } ),
+ backgroundColourMatches( new String[] { "ee", } )
+ ) );
+
+ assertEquals( 2, readTerminal.getCursorX() );
+ assertEquals( 5, readTerminal.getCursorY() );
+ assertEquals( 3, readTerminal.getTextColour() );
+ assertEquals( 5, readTerminal.getBackgroundColour() );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testReadWriteNBTEmpty()
+ {
+ Terminal terminal = new Terminal( 0, 0 );
+
+ CompoundTag nbt = new CompoundTag();
+ terminal.writeToNBT( nbt );
+
+ CallCounter callCounter = new CallCounter();
+ terminal = new Terminal( 0, 1, callCounter );
+ terminal.readFromNBT( nbt );
+
+ assertThat( terminal, allOf(
+ textMatches( new String[] { "", } ),
+ textColourMatches( new String[] { "", } ),
+ backgroundColourMatches( new String[] { "", } )
+ ) );
+
+ assertEquals( 0, terminal.getCursorX() );
+ assertEquals( 0, terminal.getCursorY() );
+ assertEquals( 0, terminal.getTextColour() );
+ assertEquals( 15, terminal.getBackgroundColour() );
+ callCounter.assertCalledTimes( 1 );
+ }
+
+ @Test
+ void testGetColour()
+ {
+ // 0 - 9
+ assertEquals( 0, Terminal.getColour( '0', Colour.WHITE ) );
+ assertEquals( 1, Terminal.getColour( '1', Colour.WHITE ) );
+ assertEquals( 8, Terminal.getColour( '8', Colour.WHITE ) );
+ assertEquals( 9, Terminal.getColour( '9', Colour.WHITE ) );
+
+ // a - f
+ assertEquals( 10, Terminal.getColour( 'a', Colour.WHITE ) );
+ assertEquals( 11, Terminal.getColour( 'b', Colour.WHITE ) );
+ assertEquals( 14, Terminal.getColour( 'e', Colour.WHITE ) );
+ assertEquals( 15, Terminal.getColour( 'f', Colour.WHITE ) );
+
+ // char out of bounds -> use colour enum ordinal
+ assertEquals( 0, Terminal.getColour( 'z', Colour.WHITE ) );
+ assertEquals( 0, Terminal.getColour( '!', Colour.WHITE ) );
+ assertEquals( 0, Terminal.getColour( 'Z', Colour.WHITE ) );
+ assertEquals( 5, Terminal.getColour( 'Z', Colour.LIME ) );
+ }
+
+ private static final class TerminalBufferSnapshot
+ {
+ final String[] textLines;
+ final String[] textColourLines;
+ final String[] backgroundColourLines;
+
+ private TerminalBufferSnapshot( Terminal terminal )
+ {
+ textLines = new String[terminal.getHeight()];
+ textColourLines = new String[terminal.getHeight()];
+ backgroundColourLines = new String[terminal.getHeight()];
+
+ for( int i = 0; i < terminal.getHeight(); i++ )
+ {
+ textLines[i] = terminal.getLine( i ).toString();
+ textColourLines[i] = terminal.getTextColourLine( i ).toString();
+ backgroundColourLines[i] = terminal.getBackgroundColourLine( i ).toString();
+ }
+ }
+
+ public Matcher matches()
+ {
+ return allOf(
+ textMatches( textLines ), textColourMatches( textColourLines ), TerminalMatchers.backgroundColourMatches( backgroundColourLines )
+ );
+ }
+ }
+}
diff --git a/src/test/java/dan200/computercraft/core/terminal/TextBufferTest.java b/src/test/java/dan200/computercraft/core/terminal/TextBufferTest.java
new file mode 100644
index 000000000..e5544ccdc
--- /dev/null
+++ b/src/test/java/dan200/computercraft/core/terminal/TextBufferTest.java
@@ -0,0 +1,150 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.core.terminal;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class TextBufferTest
+{
+ @Test
+ void testStringConstructor()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ assertEquals( "test", textBuffer.toString() );
+ }
+
+ @Test
+ void testCharRepetitionConstructor()
+ {
+ TextBuffer textBuffer = new TextBuffer( 'a', 5 );
+ assertEquals( "aaaaa", textBuffer.toString() );
+ }
+
+ @Test
+ void testLength()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ assertEquals( 4, textBuffer.length() );
+ }
+
+ @Test
+ void testWrite()
+ {
+ TextBuffer textBuffer = new TextBuffer( ' ', 4 );
+ textBuffer.write( "test" );
+ assertEquals( "test", textBuffer.toString() );
+ }
+
+ @Test
+ void testWriteTextBuffer()
+ {
+ TextBuffer source = new TextBuffer( "test" );
+ TextBuffer target = new TextBuffer( " " );
+ target.write( source );
+ assertEquals( "test", target.toString() );
+ }
+
+ @Test
+ void testWriteFromPos()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.write( "il", 1 );
+ assertEquals( "tilt", textBuffer.toString() );
+ }
+
+ @Test
+ void testWriteOutOfBounds()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.write( "abcdefghijklmnop", -5 );
+ assertEquals( "fghi", textBuffer.toString() );
+ }
+
+ @Test
+ void testWriteOutOfBounds2()
+ {
+ TextBuffer textBuffer = new TextBuffer( " " );
+ textBuffer.write( "Hello, world!", -3 );
+ assertEquals( "lo, world! ", textBuffer.toString() );
+ }
+
+ @Test
+ void testFill()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.fill( 'c' );
+ assertEquals( "cccc", textBuffer.toString() );
+ }
+
+ @Test
+ void testFillSubstring()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.fill( 'c', 1, 3 );
+ assertEquals( "tcct", textBuffer.toString() );
+ }
+
+ @Test
+ void testFillOutOfBounds()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.fill( 'c', -5, 5 );
+ assertEquals( "cccc", textBuffer.toString() );
+ }
+
+ @Test
+ void testCharAt()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ assertEquals( 'e', textBuffer.charAt( 1 ) );
+ }
+
+ @Test
+ void testSetChar()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.setChar( 2, 'n' );
+ assertEquals( "tent", textBuffer.toString() );
+ }
+
+ @Test
+ void testSetCharWithNegativeIndex()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.setChar( -5, 'n' );
+ assertEquals( "test", textBuffer.toString(), "Buffer should not change after setting char with negative index." );
+ }
+
+ @Test
+ void testSetCharWithIndexBeyondBufferEnd()
+ {
+ TextBuffer textBuffer = new TextBuffer( "test" );
+ textBuffer.setChar( 10, 'n' );
+ assertEquals( "test", textBuffer.toString(), "Buffer should not change after setting char beyond buffer end." );
+ }
+
+ @Test
+ void testMultipleOperations()
+ {
+ TextBuffer textBuffer = new TextBuffer( ' ', 5 );
+ textBuffer.setChar( 0, 'H' );
+ textBuffer.setChar( 1, 'e' );
+ textBuffer.setChar( 2, 'l' );
+ textBuffer.write( "lo", 3 );
+ assertEquals( "Hello", textBuffer.toString(), "TextBuffer failed to persist over multiple operations." );
+ }
+
+ @Test
+ void testEmptyBuffer()
+ {
+ TextBuffer textBuffer = new TextBuffer( "" );
+ // exception on writing to empty buffer would fail the test
+ textBuffer.write( "test" );
+ assertEquals( "", textBuffer.toString() );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/shared/network/client/TerminalStateTest.java b/src/test/java/dan200/computercraft/shared/network/client/TerminalStateTest.java
new file mode 100644
index 000000000..ac99068f7
--- /dev/null
+++ b/src/test/java/dan200/computercraft/shared/network/client/TerminalStateTest.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.shared.network.client;
+
+import dan200.computercraft.core.terminal.Terminal;
+import dan200.computercraft.core.terminal.TextBuffer;
+import io.netty.buffer.Unpooled;
+import net.minecraft.network.FriendlyByteBuf;
+import org.junit.jupiter.api.RepeatedTest;
+
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests {@link TerminalState} round tripping works as expected.
+ */
+public class TerminalStateTest
+{
+ @RepeatedTest( 5 )
+ public void testCompressed()
+ {
+ Terminal terminal = randomTerminal();
+
+ FriendlyByteBuf buffer = new FriendlyByteBuf( Unpooled.directBuffer() );
+ new TerminalState( true, terminal, true ).write( buffer );
+
+ checkEqual( terminal, read( buffer ) );
+ assertEquals( 0, buffer.readableBytes() );
+ }
+
+ @RepeatedTest( 5 )
+ public void testUncompressed()
+ {
+ Terminal terminal = randomTerminal();
+
+ FriendlyByteBuf buffer = new FriendlyByteBuf( Unpooled.directBuffer() );
+ new TerminalState( true, terminal, false ).write( buffer );
+
+ checkEqual( terminal, read( buffer ) );
+ assertEquals( 0, buffer.readableBytes() );
+ }
+
+ private static Terminal randomTerminal()
+ {
+ Random random = new Random();
+ Terminal terminal = new Terminal( 10, 5 );
+ for( int y = 0; y < terminal.getHeight(); y++ )
+ {
+ TextBuffer buffer = terminal.getLine( y );
+ for( int x = 0; x < buffer.length(); x++ ) buffer.setChar( x, (char) (random.nextInt( 26 ) + 65) );
+ }
+
+ return terminal;
+ }
+
+ private static void checkEqual( Terminal expected, Terminal actual )
+ {
+ assertNotNull( expected, "Expected cannot be null" );
+ assertNotNull( actual, "Actual cannot be null" );
+ assertEquals( expected.getHeight(), actual.getHeight(), "Heights must match" );
+ assertEquals( expected.getWidth(), actual.getWidth(), "Widths must match" );
+
+ for( int y = 0; y < expected.getHeight(); y++ )
+ {
+ assertEquals( expected.getLine( y ).toString(), actual.getLine( y ).toString() );
+ }
+ }
+
+ private static Terminal read( FriendlyByteBuf buffer )
+ {
+ TerminalState state = new TerminalState( buffer );
+ assertTrue( state.colour );
+
+ if( !state.hasTerminal() ) return null;
+
+ Terminal other = new Terminal( state.width, state.height );
+ state.apply( other );
+ return other;
+ }
+}
diff --git a/src/test/java/dan200/computercraft/shared/peripheral/speaker/DfpwmStateTest.java b/src/test/java/dan200/computercraft/shared/peripheral/speaker/DfpwmStateTest.java
new file mode 100644
index 000000000..cc3325514
--- /dev/null
+++ b/src/test/java/dan200/computercraft/shared/peripheral/speaker/DfpwmStateTest.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.shared.peripheral.speaker;
+
+import dan200.computercraft.api.lua.LuaException;
+import dan200.computercraft.api.lua.ObjectLuaTable;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+class DfpwmStateTest
+{
+ @Test
+ public void testEncoder() throws LuaException
+ {
+ int[] input = new int[] { 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3 };
+ Map inputTbl = new HashMap<>();
+ for( int i = 0; i < input.length; i++ ) inputTbl.put( (double) (i + 1), input[i] );
+
+ DfpwmState state = new DfpwmState();
+ state.pushBuffer( new ObjectLuaTable( inputTbl ), input.length, Optional.empty() );
+ ByteBuffer result = state.pullPending( 0 );
+ byte[] contents = new byte[result.remaining()];
+ result.get( contents );
+
+ assertArrayEquals(
+ new byte[] { 87, 74, 42, -91, -92, -108, 84, -87, -86, 86, -83, 90, -83, -43, 90, -85, -42, 106, -43, -86, 106, -107, 42, -107, 74, -87, 74, -91, 74, -91, -86, -86, 106, 85, 107, -83, 106, -83, -83, 86, -75, -86, 42, 85, -107, 82, 41, -91, 82, 74, 41, -107, -86, -44, -86, 86, -75, 106, -83, -75, -86, -75, 90, -83, -86, -86, -86, 82, -91, 74, -107, -86, 82, -87, 82, 85, 85, 85, -83, 86, -75, -86, -43, 90, -83, 90, 85, 85, -107, 42, -91, 82, -86, 82, 74, 41, 85, -87, -86, -86, 106, -75, 90, -83, 86, -85, 106, -43, 106, 85, 85, 85, 85, -107, 42, 85, -86, 42, -107, -86, -86, -86, -86, 106, -75, -86, 86, -85 },
+ contents
+ );
+ }
+}
diff --git a/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java b/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java
new file mode 100644
index 000000000..9d490b5d6
--- /dev/null
+++ b/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java
@@ -0,0 +1,463 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.shared.wired;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.network.wired.IWiredElement;
+import dan200.computercraft.api.network.wired.IWiredNetwork;
+import dan200.computercraft.api.network.wired.IWiredNetworkChange;
+import dan200.computercraft.api.network.wired.IWiredNode;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.shared.util.DirectionUtil;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.Vec3;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class NetworkTest
+{
+ @Test
+ public void testConnect()
+ {
+ NetworkElement
+ aE = new NetworkElement( null, null, "a" ),
+ bE = new NetworkElement( null, null, "b" ),
+ cE = new NetworkElement( null, null, "c" );
+
+ IWiredNode
+ aN = aE.getNode(),
+ bN = bE.getNode(),
+ cN = cE.getNode();
+
+ assertNotEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must be different" );
+ assertNotEquals( aN.getNetwork(), cN.getNetwork(), "A's and C's network must be different" );
+ assertNotEquals( bN.getNetwork(), cN.getNetwork(), "B's and C's network must be different" );
+
+ assertTrue( aN.getNetwork().connect( aN, bN ), "Must be able to add connection" );
+ assertFalse( aN.getNetwork().connect( aN, bN ), "Cannot add connection twice" );
+
+ assertEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal" );
+ assertEquals( Sets.newHashSet( aN, bN ), nodes( aN.getNetwork() ), "A's network should be A and B" );
+
+ assertEquals( Sets.newHashSet( "a", "b" ), aE.allPeripherals().keySet(), "A's peripheral set should be A, B" );
+ assertEquals( Sets.newHashSet( "a", "b" ), bE.allPeripherals().keySet(), "B's peripheral set should be A, B" );
+
+ aN.getNetwork().connect( aN, cN );
+
+ assertEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal" );
+ assertEquals( aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal" );
+ assertEquals( Sets.newHashSet( aN, bN, cN ), nodes( aN.getNetwork() ), "A's network should be A, B and C" );
+
+ assertEquals( Sets.newHashSet( bN, cN ), neighbours( aN ), "A's neighbour set should be B, C" );
+ assertEquals( Sets.newHashSet( aN ), neighbours( bN ), "B's neighbour set should be A" );
+ assertEquals( Sets.newHashSet( aN ), neighbours( cN ), "C's neighbour set should be A" );
+
+ assertEquals( Sets.newHashSet( "a", "b", "c" ), aE.allPeripherals().keySet(), "A's peripheral set should be A, B, C" );
+ assertEquals( Sets.newHashSet( "a", "b", "c" ), bE.allPeripherals().keySet(), "B's peripheral set should be A, B, C" );
+ assertEquals( Sets.newHashSet( "a", "b", "c" ), cE.allPeripherals().keySet(), "C's peripheral set should be A, B, C" );
+ }
+
+ @Test
+ public void testDisconnectNoChange()
+ {
+ NetworkElement
+ aE = new NetworkElement( null, null, "a" ),
+ bE = new NetworkElement( null, null, "b" ),
+ cE = new NetworkElement( null, null, "c" );
+
+ IWiredNode
+ aN = aE.getNode(),
+ bN = bE.getNode(),
+ cN = cE.getNode();
+
+ aN.getNetwork().connect( aN, bN );
+ aN.getNetwork().connect( aN, cN );
+ aN.getNetwork().connect( bN, cN );
+
+ aN.getNetwork().disconnect( aN, bN );
+
+ assertEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must be equal" );
+ assertEquals( aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal" );
+ assertEquals( Sets.newHashSet( aN, bN, cN ), nodes( aN.getNetwork() ), "A's network should be A, B and C" );
+
+ assertEquals( Sets.newHashSet( "a", "b", "c" ), aE.allPeripherals().keySet(), "A's peripheral set should be A, B, C" );
+ assertEquals( Sets.newHashSet( "a", "b", "c" ), bE.allPeripherals().keySet(), "B's peripheral set should be A, B, C" );
+ assertEquals( Sets.newHashSet( "a", "b", "c" ), cE.allPeripherals().keySet(), "C's peripheral set should be A, B, C" );
+ }
+
+ @Test
+ public void testDisconnectLeaf()
+ {
+ NetworkElement
+ aE = new NetworkElement( null, null, "a" ),
+ bE = new NetworkElement( null, null, "b" ),
+ cE = new NetworkElement( null, null, "c" );
+
+ IWiredNode
+ aN = aE.getNode(),
+ bN = bE.getNode(),
+ cN = cE.getNode();
+
+ aN.getNetwork().connect( aN, bN );
+ aN.getNetwork().connect( aN, cN );
+
+ aN.getNetwork().disconnect( aN, bN );
+
+ assertNotEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal" );
+ assertEquals( aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal" );
+ assertEquals( Sets.newHashSet( aN, cN ), nodes( aN.getNetwork() ), "A's network should be A and C" );
+ assertEquals( Sets.newHashSet( bN ), nodes( bN.getNetwork() ), "B's network should be B" );
+
+ assertEquals( Sets.newHashSet( "a", "c" ), aE.allPeripherals().keySet(), "A's peripheral set should be A, C" );
+ assertEquals( Sets.newHashSet( "b" ), bE.allPeripherals().keySet(), "B's peripheral set should be B" );
+ assertEquals( Sets.newHashSet( "a", "c" ), cE.allPeripherals().keySet(), "C's peripheral set should be A, C" );
+ }
+
+ @Test
+ public void testDisconnectSplit()
+ {
+ NetworkElement
+ aE = new NetworkElement( null, null, "a" ),
+ aaE = new NetworkElement( null, null, "a_" ),
+ bE = new NetworkElement( null, null, "b" ),
+ bbE = new NetworkElement( null, null, "b_" );
+
+ IWiredNode
+ aN = aE.getNode(),
+ aaN = aaE.getNode(),
+ bN = bE.getNode(),
+ bbN = bbE.getNode();
+
+ aN.getNetwork().connect( aN, aaN );
+ bN.getNetwork().connect( bN, bbN );
+
+ aN.getNetwork().connect( aN, bN );
+
+ aN.getNetwork().disconnect( aN, bN );
+
+ assertNotEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal" );
+ assertEquals( aN.getNetwork(), aaN.getNetwork(), "A's and A_'s network must be equal" );
+ assertEquals( bN.getNetwork(), bbN.getNetwork(), "B's and B_'s network must be equal" );
+
+ assertEquals( Sets.newHashSet( aN, aaN ), nodes( aN.getNetwork() ), "A's network should be A and A_" );
+ assertEquals( Sets.newHashSet( bN, bbN ), nodes( bN.getNetwork() ), "B's network should be B and B_" );
+
+ assertEquals( Sets.newHashSet( "a", "a_" ), aE.allPeripherals().keySet(), "A's peripheral set should be A and A_" );
+ assertEquals( Sets.newHashSet( "b", "b_" ), bE.allPeripherals().keySet(), "B's peripheral set should be B and B_" );
+ }
+
+ @Test
+ public void testRemoveSingle()
+ {
+ NetworkElement aE = new NetworkElement( null, null, "a" );
+ IWiredNode aN = aE.getNode();
+
+ IWiredNetwork network = aN.getNetwork();
+ assertFalse( aN.remove(), "Cannot remove node from an empty network" );
+ assertEquals( network, aN.getNetwork(), "Networks are same before and after" );
+ }
+
+ @Test
+ public void testRemoveLeaf()
+ {
+ NetworkElement
+ aE = new NetworkElement( null, null, "a" ),
+ bE = new NetworkElement( null, null, "b" ),
+ cE = new NetworkElement( null, null, "c" );
+
+ IWiredNode
+ aN = aE.getNode(),
+ bN = bE.getNode(),
+ cN = cE.getNode();
+
+ aN.getNetwork().connect( aN, bN );
+ aN.getNetwork().connect( aN, cN );
+
+ assertTrue( aN.getNetwork().remove( bN ), "Must be able to remove node" );
+ assertFalse( aN.getNetwork().remove( bN ), "Cannot remove a second time" );
+
+ assertNotEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal" );
+ assertEquals( aN.getNetwork(), cN.getNetwork(), "A's and C's network must be equal" );
+
+ assertEquals( Sets.newHashSet( aN, cN ), nodes( aN.getNetwork() ), "A's network should be A and C" );
+ assertEquals( Sets.newHashSet( bN ), nodes( bN.getNetwork() ), "B's network should be B" );
+
+ assertEquals( Sets.newHashSet( "a", "c" ), aE.allPeripherals().keySet(), "A's peripheral set should be A, C" );
+ assertEquals( Sets.newHashSet(), bE.allPeripherals().keySet(), "B's peripheral set should be empty" );
+ assertEquals( Sets.newHashSet( "a", "c" ), cE.allPeripherals().keySet(), "C's peripheral set should be A, C" );
+ }
+
+ @Test
+ public void testRemoveSplit()
+ {
+ NetworkElement
+ aE = new NetworkElement( null, null, "a" ),
+ aaE = new NetworkElement( null, null, "a_" ),
+ bE = new NetworkElement( null, null, "b" ),
+ bbE = new NetworkElement( null, null, "b_" ),
+ cE = new NetworkElement( null, null, "c" );
+
+ IWiredNode
+ aN = aE.getNode(),
+ aaN = aaE.getNode(),
+ bN = bE.getNode(),
+ bbN = bbE.getNode(),
+ cN = cE.getNode();
+
+ aN.getNetwork().connect( aN, aaN );
+ bN.getNetwork().connect( bN, bbN );
+
+ cN.getNetwork().connect( aN, cN );
+ cN.getNetwork().connect( bN, cN );
+
+ cN.getNetwork().remove( cN );
+
+ assertNotEquals( aN.getNetwork(), bN.getNetwork(), "A's and B's network must not be equal" );
+ assertEquals( aN.getNetwork(), aaN.getNetwork(), "A's and A_'s network must be equal" );
+ assertEquals( bN.getNetwork(), bbN.getNetwork(), "B's and B_'s network must be equal" );
+
+ assertEquals( Sets.newHashSet( aN, aaN ), nodes( aN.getNetwork() ), "A's network should be A and A_" );
+ assertEquals( Sets.newHashSet( bN, bbN ), nodes( bN.getNetwork() ), "B's network should be B and B_" );
+ assertEquals( Sets.newHashSet( cN ), nodes( cN.getNetwork() ), "C's network should be C" );
+
+ assertEquals( Sets.newHashSet( "a", "a_" ), aE.allPeripherals().keySet(), "A's peripheral set should be A and A_" );
+ assertEquals( Sets.newHashSet( "b", "b_" ), bE.allPeripherals().keySet(), "B's peripheral set should be B and B_" );
+ assertEquals( Sets.newHashSet(), cE.allPeripherals().keySet(), "C's peripheral set should be empty" );
+ }
+
+ private static final int BRUTE_SIZE = 16;
+ private static final int TOGGLE_CONNECTION_TIMES = 5;
+ private static final int TOGGLE_NODE_TIMES = 5;
+
+ @Test
+ @Disabled( "Takes a long time to run, mostly for stress testing" )
+ public void testLarge()
+ {
+ Grid grid = new Grid<>( BRUTE_SIZE );
+ grid.map( ( existing, pos ) -> new NetworkElement( null, null, "n_" + pos ).getNode() );
+
+ // Test connecting
+ {
+ long start = System.nanoTime();
+
+ grid.forEach( ( existing, pos ) -> {
+ for( Direction facing : DirectionUtil.FACINGS )
+ {
+ BlockPos offset = pos.relative( facing );
+ if( offset.getX() > BRUTE_SIZE / 2 == pos.getX() > BRUTE_SIZE / 2 )
+ {
+ IWiredNode other = grid.get( offset );
+ if( other != null ) existing.getNetwork().connect( existing, other );
+ }
+ }
+ } );
+
+ long end = System.nanoTime();
+
+ System.out.printf( "Connecting %s³ nodes took %s seconds\n", BRUTE_SIZE, (end - start) * 1e-9 );
+ }
+
+ // Test toggling
+ {
+ IWiredNode left = grid.get( new BlockPos( BRUTE_SIZE / 2, 0, 0 ) );
+ IWiredNode right = grid.get( new BlockPos( BRUTE_SIZE / 2 + 1, 0, 0 ) );
+ assertNotEquals( left.getNetwork(), right.getNetwork() );
+
+ long start = System.nanoTime();
+ for( int i = 0; i < TOGGLE_CONNECTION_TIMES; i++ )
+ {
+ left.getNetwork().connect( left, right );
+ left.getNetwork().disconnect( left, right );
+ }
+
+ long end = System.nanoTime();
+
+ System.out.printf( "Toggling connection %s times took %s seconds\n", TOGGLE_CONNECTION_TIMES, (end - start) * 1e-9 );
+ }
+
+ {
+ IWiredNode left = grid.get( new BlockPos( BRUTE_SIZE / 2, 0, 0 ) );
+ IWiredNode right = grid.get( new BlockPos( BRUTE_SIZE / 2 + 1, 0, 0 ) );
+ IWiredNode centre = new NetworkElement( null, null, "c" ).getNode();
+ assertNotEquals( left.getNetwork(), right.getNetwork() );
+
+ long start = System.nanoTime();
+ for( int i = 0; i < TOGGLE_NODE_TIMES; i++ )
+ {
+ left.getNetwork().connect( left, centre );
+ right.getNetwork().connect( right, centre );
+
+ left.getNetwork().remove( centre );
+ }
+
+ long end = System.nanoTime();
+
+ System.out.printf( "Toggling node %s times took %s seconds\n", TOGGLE_NODE_TIMES, (end - start) * 1e-9 );
+ }
+ }
+
+ private static final class NetworkElement implements IWiredElement
+ {
+ private final Level world;
+ private final Vec3 position;
+ private final String id;
+ private final IWiredNode node;
+ private final Map localPeripherals = Maps.newHashMap();
+ private final Map remotePeripherals = Maps.newHashMap();
+
+ private NetworkElement( Level world, Vec3 position, String id )
+ {
+ this.world = world;
+ this.position = position;
+ this.id = id;
+ this.node = ComputerCraftAPI.createWiredNodeForElement( this );
+ this.addPeripheral( id );
+ }
+
+ @Nonnull
+ @Override
+ public Level getLevel()
+ {
+ return world;
+ }
+
+ @Nonnull
+ @Override
+ public Vec3 getPosition()
+ {
+ return position;
+ }
+
+ @Nonnull
+ @Override
+ public String getSenderID()
+ {
+ return id;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "NetworkElement{" + id + "}";
+ }
+
+ @Nonnull
+ @Override
+ public IWiredNode getNode()
+ {
+ return node;
+ }
+
+ @Override
+ public void networkChanged( @Nonnull IWiredNetworkChange change )
+ {
+ remotePeripherals.keySet().removeAll( change.peripheralsRemoved().keySet() );
+ remotePeripherals.putAll( change.peripheralsAdded() );
+ }
+
+ public NetworkElement addPeripheral( String name )
+ {
+ localPeripherals.put( name, new NetworkPeripheral() );
+ getNode().updatePeripherals( localPeripherals );
+ return this;
+ }
+
+ @Nonnull
+ public Map allPeripherals()
+ {
+ return remotePeripherals;
+ }
+ }
+
+ private static class NetworkPeripheral implements IPeripheral
+ {
+ @Nonnull
+ @Override
+ public String getType()
+ {
+ return "test";
+ }
+
+ @Override
+ public boolean equals( @Nullable IPeripheral other )
+ {
+ return this == other;
+ }
+ }
+
+ private static class Grid
+ {
+ private final int size;
+ private final T[] box;
+
+ @SuppressWarnings( "unchecked" )
+ Grid( int size )
+ {
+ this.size = size;
+ this.box = (T[]) new Object[size * size * size];
+ }
+
+ public T get( BlockPos pos )
+ {
+ int x = pos.getX(), y = pos.getY(), z = pos.getZ();
+
+ return x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size
+ ? box[x * size * size + y * size + z]
+ : null;
+ }
+
+ public void forEach( BiConsumer transform )
+ {
+ for( int x = 0; x < size; x++ )
+ {
+ for( int y = 0; y < size; y++ )
+ {
+ for( int z = 0; z < size; z++ )
+ {
+ transform.accept( box[x * size * size + y * size + z], new BlockPos( x, y, z ) );
+ }
+ }
+ }
+ }
+
+ public void map( BiFunction transform )
+ {
+ for( int x = 0; x < size; x++ )
+ {
+ for( int y = 0; y < size; y++ )
+ {
+ for( int z = 0; z < size; z++ )
+ {
+ box[x * size * size + y * size + z] = transform.apply( box[x * size * size + y * size + z], new BlockPos( x, y, z ) );
+ }
+ }
+ }
+ }
+ }
+
+ private static Set nodes( IWiredNetwork network )
+ {
+ return ((WiredNetwork) network).nodes;
+ }
+
+ private static Set neighbours( IWiredNode node )
+ {
+ return ((WiredNode) node).neighbours;
+ }
+}
diff --git a/src/test/java/dan200/computercraft/utils/CallCounter.java b/src/test/java/dan200/computercraft/utils/CallCounter.java
new file mode 100644
index 000000000..5dbae63f7
--- /dev/null
+++ b/src/test/java/dan200/computercraft/utils/CallCounter.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CallCounter implements Runnable
+{
+ private int timesCalled = 0;
+
+ @Override
+ public void run()
+ {
+ timesCalled++;
+ }
+
+ public void assertCalledTimes( int expectedTimesCalled )
+ {
+ assertEquals( expectedTimesCalled, timesCalled, "Callback was not called the correct number of times" );
+ }
+
+ public void assertNotCalled()
+ {
+ assertEquals( 0, timesCalled, "Should never have been called." );
+ }
+
+ public void reset()
+ {
+ this.timesCalled = 0;
+ }
+}
diff --git a/src/test/kotlin/dan200/computercraft/client/sound/DfpwmStreamTest.java b/src/test/kotlin/dan200/computercraft/client/sound/DfpwmStreamTest.java
new file mode 100644
index 000000000..52a4958a9
--- /dev/null
+++ b/src/test/kotlin/dan200/computercraft/client/sound/DfpwmStreamTest.java
@@ -0,0 +1,40 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.client.sound;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DfpwmStreamTest
+{
+ @Test
+ public void testDecodesBytes()
+ {
+ DfpwmStream stream = new DfpwmStream();
+
+ ByteBuf input = ByteBufAllocator.DEFAULT.buffer();
+ input.writeBytes( new byte[] { 43, -31, 33, 44, 30, -16, -85, 23, -3, -55, 46, -70, 68, -67, 74, -96, -68, 16, 94, -87, -5, 87, 11, -16, 19, 92, 85, -71, 126, 5, -84, 64, 17, -6, 85, -11, -1, -87, -12, 1, 85, -56, 33, -80, 82, 104, -93, 17, 126, 23, 91, -30, 37, -32, 117, -72, -58, 11, -76, 19, -108, 86, -65, -10, -1, -68, -25, 10, -46, 85, 124, -54, 15, -24, 43, -94, 117, 63, -36, 15, -6, 88, 87, -26, -83, 106, 41, 13, -28, -113, -10, -66, 119, -87, -113, 68, -55, 40, -107, 62, 20, 72, 3, -96, 114, -87, -2, 39, -104, 30, 20, 42, 84, 24, 47, 64, 43, 61, -35, 95, -65, 42, 61, 42, -50, 4, -9, 81 } );
+ stream.push( input );
+
+ byte[] values = new byte[1024];
+ ByteBuffer buffer = stream.read( 2048 );
+ assertEquals( 1024, buffer.remaining(), "Must have read 1024 bytes" );
+ buffer.get( values );
+ assertEquals( 0, buffer.remaining() );
+
+ assertArrayEquals(
+ new byte[] { -127, -126, -126, -126, -126, -126, -126, -127, -127, -127, -128, 127, 126, 126, 127, -128, -127, -128, 127, 125, 123, 123, 123, 121, 119, 117, 117, 119, 119, 119, 119, 118, 116, 116, 118, 120, 122, 122, 120, 118, 116, 114, 112, 110, 111, 113, 116, 119, 122, 125, 126, 126, 126, 126, 126, 126, -128, -125, -122, -121, -121, -121, -124, -127, -127, -127, -127, -125, -123, -121, -119, -116, -113, -113, -116, -116, -116, -119, -119, -117, -116, -116, -114, -112, -111, -111, -111, -114, -117, -117, -117, -118, -116, -114, -114, -115, -115, -118, -119, -119, -121, -123, -124, -124, -124, -124, -124, -122, -120, -118, -118, -118, -118, -118, -118, -118, -119, -120, -120, -120, -121, -122, -124, -126, -128, -128, -128, -128, -128, 127, 127, -128, -127, -125, -125, -125, -125, -126, -128, 126, 126, 126, 125, 123, 121, 121, 123, 125, 127, 127, 127, 127, 127, 127, 126, 126, 127, 127, 127, 127, -128, -127, -127, -127, -126, -125, -124, -123, -122, -121, -119, -119, -119, -119, -119, -119, -119, -118, -118, -118, -118, -119, -120, -121, -122, -124, -126, -128, -128, -126, -124, -122, -120, -118, -118, -120, -121, -121, -123, -125, -127, 127, -128, -126, -124, -123, -123, -123, -124, -124, -124, -124, -124, -124, -124, -124, -124, -124, -125, -125, -124, -123, -123, -123, -123, -123, -122, -121, -120, -119, -118, -119, -119, -119, -119, -119, -120, -121, -122, -123, -125, -127, -127, -125, -125, -125, -125, -125, -125, -126, -127, -128, 127, 125, 125, 125, 125, 126, 125, 124, 124, 125, 124, 123, 122, 122, 123, 123, 124, 125, 126, -128, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -125, -124, -123, -122, -121, -120, -118, -116, -114, -112, -110, -108, -108, -111, -112, -112, -113, -113, -113, -113, -115, -115, -115, -115, -114, -113, -112, -110, -110, -112, -114, -116, -118, -120, -123, -123, -123, -124, -124, -124, -124, -124, -124, -126, -128, 126, 126, 126, 124, 124, 126, -128, -128, 126, 124, 122, 122, 122, 120, 118, 116, 114, 112, 113, 115, 116, 117, 117, 117, 117, 115, 115, 115, 115, 115, 114, 112, 110, 110, 110, 110, 112, 112, 112, 114, 115, 114, 113, 113, 114, 114, 116, 117, 116, 115, 115, 116, 115, 114, 113, 113, 115, 117, 119, 121, 123, 123, 123, 125, 127, 127, 127, 127, 125, 123, 123, 125, 125, 125, 127, 127, 127, 127, 125, 125, 125, 124, 122, 122, 124, 126, -128, -128, -128, -128, 126, 126, 126, 125, 123, 121, 119, 117, 115, 115, 117, 119, 121, 122, 122, 122, 122, 124, 126, 126, 124, 122, 120, 121, 123, 125, 126, 126, 126, 126, -128, -128, 126, 124, 124, 126, -128, -126, -126, -127, -127, 127, 125, 123, 121, 118, 118, 118, 118, 120, 121, 121, 123, 125, 126, 124, 124, 124, 122, 120, 118, 116, 116, 116, 116, 116, 114, 115, 115, 115, 117, 117, 117, 117, 117, 117, 117, 119, 121, 123, 125, 127, 127, 127, 127, 127, -127, -127, -127, -126, -124, -122, -120, -118, -116, -114, -112, -110, -108, -106, -106, -109, -110, -108, -106, -104, -105, -106, -104, -102, -100, -101, -104, -105, -103, -100, -100, -100, -101, -102, -102, -105, -108, -111, -114, -114, -114, -117, -117, -117, -117, -115, -113, -112, -112, -112, -113, -113, -114, -114, -116, -118, -119, -117, -115, -113, -111, -111, -114, -115, -115, -116, -116, -118, -119, -117, -115, -113, -111, -109, -109, -112, -115, -118, -121, -124, -127, -127, -126, -126, -124, -121, -118, -115, -115, -115, -116, -116, -116, -119, -122, -122, -122, -125, -128, -128, -128, -128, -126, -125, -125, -125, -125, -123, -121, -121, -121, -119, -117, -115, -113, -110, -110, -113, -116, -119, -120, -118, -115, -115, -115, -113, -110, -107, -104, -101, -101, -105, -109, -113, -117, -118, -119, -119, -116, -112, -109, -106, -105, -109, -114, -115, -112, -112, -113, -113, -114, -111, -108, -108, -109, -109, -110, -111, -114, -115, -113, -113, -116, -117, -115, -112, -109, -109, -110, -108, -108, -109, -110, -110, -111, -111, -112, -112, -112, -113, -111, -111, -112, -112, -115, -116, -116, -117, -117, -119, -119, -119, -119, -117, -117, -119, -121, -123, -125, -127, -127, -127, 127, 127, -127, -125, -123, -121, -119, -117, -116, -119, -122, -122, -122, -122, -120, -120, -121, -119, -117, -115, -115, -116, -114, -112, -110, -108, -108, -108, -106, -104, -102, -103, -103, -101, -99, -100, -101, -102, -105, -106, -106, -107, -107, -108, -106, -104, -102, -100, -101, -104, -107, -107, -107, -110, -111, -111, -114, -117, -117, -117, -118, -118, -121, -122, -122, -124, -125, -123, -123, -125, -127, -127, -127, -127, -127, 127, 127, 127, 127, 127, 127, -128, 127, 127, -128, -128, -127, -126, -125, -124, -125, -127, 127, 125, 125, 125, 125, 126, 125, 124, 122, 120, 118, 118, 118, 116, 116, 116, 116, 118, 118, 117, 116, 114, 112, 110, 108, 106, 104, 102, 100, 101, 101, 102, 102, 103, 103, 101, 102, 104, 106, 106, 106, 106, 104, 104, 104, 104, 105, 105, 106, 106, 107, 108, 109, 111, 113, 115, 117, 119, 121, 121, 119, 119, 119, 117, 115, 113, 111, 112, 114, 115, 113, 114, 114, 114, 116, 118, 120, 121, 119, 117, 115, 113, 114, 114, 115, 115, 113, 111, 109, 110, 110, 111, 111, 112, 112, 110, 108, 106, 107, 107, 107, 107, 107, 108, 107, 106, 104, 104, 106, 106, 104, 102, 103, 105, 107, 109, 110, 111, 111, 109, 107, 105, 103, 101, 99, 97, 98, 99, 100, 102, 103, 104, 104, 105, 105, 103, 104, 104, 104, 106, 108, 110, 110, 108, 108, 108, 108, 110, 112, 112, 112, 114, 116, 118, 120, 122, 124, 124, 124, 124, 124, 126, -128, -126, -124, -122, -122, -123, -123, -123, -123, -123, -123, -123, -123, -125, -125, -125, -125, -124, -123, -122, -123, -125, -127, -127, -127, -127, -127, -127, -127, -128, 127, 127, -128, -127, -127, -128, -128, -127, -127, -128, -128, -128, 127, 126, 125, 124, 124, 126, -128, -128, -128, -127, -125, -123, -121, -121, -123, -125, -125, -125, -125, -125 },
+ values,
+ "Decoded values must match."
+ );
+ }
+}
diff --git a/src/test/resources/benchmark.lua b/src/test/resources/benchmark.lua
new file mode 100644
index 000000000..dbbef23a1
--- /dev/null
+++ b/src/test/resources/benchmark.lua
@@ -0,0 +1,24 @@
+local function log(msg)
+ print(msg)
+ if assertion then assertion.log(msg) end
+end
+
+local function run(name, n, f, ...)
+ sleep(0)
+ local s = os.epoch("utc")
+ for _ = 1, n do f(...) end
+ local e = os.epoch("utc") - s
+ log(("%10s %.2fs %.fop/s"):format(name, e*1e-3, n/e))
+end
+
+local function run5(...) for _ = 1, 5 do run(...) end end
+
+local native = term.native()
+local x, y = native.getCursorPos()
+
+log("Starting the benchmark")
+run5("redstone.getAnalogInput", 1e7, redstone.getAnalogInput, "top")
+run5("term.getCursorPos", 2e7, native.getCursorPos)
+run5("term.setCursorPos", 2e7, native.setCursorPos, x, y)
+
+if assertion then assertion.assert(true) end
diff --git a/src/test/resources/test-rom/data/dump-args.lua b/src/test/resources/test-rom/data/dump-args.lua
new file mode 100644
index 000000000..3772a43ec
--- /dev/null
+++ b/src/test/resources/test-rom/data/dump-args.lua
@@ -0,0 +1 @@
+_G.__arg = _ENV.arg
diff --git a/src/test/resources/test-rom/data/json-parsing/LICENSE b/src/test/resources/test-rom/data/json-parsing/LICENSE
new file mode 100644
index 000000000..c4b3621d5
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Nicolas Seriot
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/test/resources/test-rom/data/json-parsing/README.md b/src/test/resources/test-rom/data/json-parsing/README.md
new file mode 100644
index 000000000..d0d801887
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/README.md
@@ -0,0 +1,9 @@
+# JSON Parsing Test Suite
+
+This is a collection of JSON test cases from [nst/JSONTestSuite][gh]. We simply
+determine whether an object is succesfully parsed or not, and do not check the
+contents.
+
+See `LICENSE` for copyright information.
+
+[gh]: https://github.com/nst/JSONTestSuite
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json
new file mode 100644
index 000000000..ae4c7b71f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json
@@ -0,0 +1 @@
+[123.456e-789]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json
new file mode 100644
index 000000000..9b5efa236
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json
@@ -0,0 +1 @@
+[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json
new file mode 100644
index 000000000..3abd58a5c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json
@@ -0,0 +1 @@
+[-1e+9999]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json
new file mode 100644
index 000000000..e10a7eb62
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json
@@ -0,0 +1 @@
+[1.5e+9999]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json b/src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json
new file mode 100644
index 000000000..3d628a994
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json
@@ -0,0 +1 @@
+[-123123e100000]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json b/src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json
new file mode 100644
index 000000000..54d7d3dcd
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json
@@ -0,0 +1 @@
+[123123e100000]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json b/src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json
new file mode 100644
index 000000000..c5236eb26
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json
@@ -0,0 +1 @@
+[123e-10000000]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json
new file mode 100644
index 000000000..dfa384619
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json
@@ -0,0 +1 @@
+[-123123123123123123123123123123]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json
new file mode 100644
index 000000000..338a8c3c0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json
@@ -0,0 +1 @@
+[100000000000000000000]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json b/src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json
new file mode 100644
index 000000000..e2d9738c2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json
@@ -0,0 +1 @@
+[-237462374673276894279832749832423479823246327846]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json
new file mode 100644
index 000000000..5be7ebaf9
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json
@@ -0,0 +1 @@
+{"\uDFAA":0}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json b/src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json
new file mode 100644
index 000000000..3b9e37c67
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json
@@ -0,0 +1 @@
+["\uDADA"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json b/src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json
new file mode 100644
index 000000000..487592832
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json
@@ -0,0 +1 @@
+["\uD888\u1234"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_UTF-16LE_with_BOM.json b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-16LE_with_BOM.json
new file mode 100644
index 000000000..2a79c0629
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-16LE_with_BOM.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json
new file mode 100644
index 000000000..e2a968a15
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json
@@ -0,0 +1 @@
+["日шú"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json b/src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json
new file mode 100644
index 000000000..916bff920
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json
@@ -0,0 +1 @@
+["í €"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json
new file mode 100644
index 000000000..3cb11d229
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json
@@ -0,0 +1 @@
+["\uD800\n"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json
new file mode 100644
index 000000000..38ec23bb0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json
@@ -0,0 +1 @@
+["\uDd1ea"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json
new file mode 100644
index 000000000..c9cd6f6c3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json
@@ -0,0 +1 @@
+["\uD800\uD800\n"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json
new file mode 100644
index 000000000..3abbd8d8d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json
@@ -0,0 +1 @@
+["\ud800"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json
new file mode 100644
index 000000000..ffddc04f5
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json
@@ -0,0 +1 @@
+["\ud800abc"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json
new file mode 100644
index 000000000..8e45a7eca
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json
@@ -0,0 +1 @@
+["ÿ"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json b/src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json
new file mode 100644
index 000000000..0d5456cc3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json
@@ -0,0 +1 @@
+["\uDd1e\uD834"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json b/src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json
new file mode 100644
index 000000000..9389c9823
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json
@@ -0,0 +1 @@
+["é"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json
new file mode 100644
index 000000000..1dbd397f3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json
@@ -0,0 +1 @@
+["\uDFAA"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json b/src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json
new file mode 100644
index 000000000..729337c0a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json
@@ -0,0 +1 @@
+[""]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json b/src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json
new file mode 100644
index 000000000..df90a2916
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json
@@ -0,0 +1 @@
+["ô¿¿¿"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json
new file mode 100644
index 000000000..c8cee5e0a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json
@@ -0,0 +1 @@
+["À¯"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json
new file mode 100644
index 000000000..9a91da791
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json
@@ -0,0 +1 @@
+["üƒ¿¿¿¿"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json
new file mode 100644
index 000000000..d24fffdd9
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json
@@ -0,0 +1 @@
+["ü€€€€€"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json b/src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json
new file mode 100644
index 000000000..63c7777fb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json
@@ -0,0 +1 @@
+["àÿ"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_utf16BE_no_BOM.json b/src/test/resources/test-rom/data/json-parsing/i_string_utf16BE_no_BOM.json
new file mode 100644
index 000000000..57e5392ff
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/i_string_utf16BE_no_BOM.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_utf16LE_no_BOM.json b/src/test/resources/test-rom/data/json-parsing/i_string_utf16LE_no_BOM.json
new file mode 100644
index 000000000..c49c1b25d
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/i_string_utf16LE_no_BOM.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json b/src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json
new file mode 100644
index 000000000..711840589
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json b/src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json
new file mode 100644
index 000000000..22fdca1b2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json
new file mode 100644
index 000000000..c14e3f6b1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json
@@ -0,0 +1 @@
+[1 true]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json b/src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json
new file mode 100644
index 000000000..38a86e2e6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json
@@ -0,0 +1 @@
+[aå]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json
new file mode 100644
index 000000000..0d02ad448
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json
@@ -0,0 +1 @@
+["": 1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json b/src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json
new file mode 100644
index 000000000..2ccba8d95
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json
@@ -0,0 +1 @@
+[""],
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json b/src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json
new file mode 100644
index 000000000..d2c84e374
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json
@@ -0,0 +1 @@
+[,1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json
new file mode 100644
index 000000000..0431712bc
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json
@@ -0,0 +1 @@
+[1,,2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json
new file mode 100644
index 000000000..3f01d3129
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json
@@ -0,0 +1 @@
+["x",,]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json b/src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json
new file mode 100644
index 000000000..c12f9fae1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json
@@ -0,0 +1 @@
+["x"]]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json
new file mode 100644
index 000000000..5f8ce18e4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json
@@ -0,0 +1 @@
+["",]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json
new file mode 100644
index 000000000..cc65b0b51
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json
@@ -0,0 +1 @@
+["x"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json
new file mode 100644
index 000000000..c21a8f6cf
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json
@@ -0,0 +1 @@
+[x
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json
new file mode 100644
index 000000000..c70b71647
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json
@@ -0,0 +1 @@
+[3[4]]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json b/src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json
new file mode 100644
index 000000000..6099d3441
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json
@@ -0,0 +1 @@
+[ÿ]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json b/src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json
new file mode 100644
index 000000000..d4bd7314c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json
@@ -0,0 +1 @@
+[1:2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json
new file mode 100644
index 000000000..9d7077c68
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json
@@ -0,0 +1 @@
+[,]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json b/src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json
new file mode 100644
index 000000000..29501c6ca
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json
@@ -0,0 +1 @@
+[-]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json b/src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json
new file mode 100644
index 000000000..3a6ba86f3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json
@@ -0,0 +1 @@
+[ , ""]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json b/src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json
new file mode 100644
index 000000000..646680065
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json
@@ -0,0 +1,3 @@
+["a",
+4
+,1,
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json
new file mode 100644
index 000000000..13f6f1d18
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json
@@ -0,0 +1 @@
+[1,]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json
new file mode 100644
index 000000000..0ac408cb8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json
@@ -0,0 +1 @@
+[1,,]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json b/src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json
new file mode 100644
index 000000000..6cd7cf585
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json
@@ -0,0 +1 @@
+["a"\f]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json b/src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json
new file mode 100644
index 000000000..5a5194647
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json
@@ -0,0 +1 @@
+[*]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json
new file mode 100644
index 000000000..060733059
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json
@@ -0,0 +1 @@
+[""
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json
new file mode 100644
index 000000000..6604698ff
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json
@@ -0,0 +1 @@
+[1,
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json
new file mode 100644
index 000000000..4f61de3fb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json
@@ -0,0 +1,3 @@
+[1,
+1
+,1
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json
new file mode 100644
index 000000000..043a87e2d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json
@@ -0,0 +1 @@
+[{}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json b/src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json
new file mode 100644
index 000000000..eb18c6a14
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json
@@ -0,0 +1 @@
+[fals]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json b/src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json
new file mode 100644
index 000000000..c18ef5385
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json
@@ -0,0 +1 @@
+[nul]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json b/src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json
new file mode 100644
index 000000000..f451ac6d2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json
@@ -0,0 +1 @@
+[tru]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_multidigit_number_then_00.json b/src/test/resources/test-rom/data/json-parsing/n_multidigit_number_then_00.json
new file mode 100644
index 000000000..c22507b86
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/n_multidigit_number_then_00.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_++.json b/src/test/resources/test-rom/data/json-parsing/n_number_++.json
new file mode 100644
index 000000000..bdb62aaf4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_++.json
@@ -0,0 +1 @@
+[++1234]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_+1.json b/src/test/resources/test-rom/data/json-parsing/n_number_+1.json
new file mode 100644
index 000000000..3cbe58c92
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_+1.json
@@ -0,0 +1 @@
+[+1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json b/src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json
new file mode 100644
index 000000000..871ae14d5
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json
@@ -0,0 +1 @@
+[+Inf]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-01.json b/src/test/resources/test-rom/data/json-parsing/n_number_-01.json
new file mode 100644
index 000000000..0df32bac8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_-01.json
@@ -0,0 +1 @@
+[-01]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json b/src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json
new file mode 100644
index 000000000..7cf55a85a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json
@@ -0,0 +1 @@
+[-1.0.]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-2..json b/src/test/resources/test-rom/data/json-parsing/n_number_-2..json
new file mode 100644
index 000000000..9be84365d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_-2..json
@@ -0,0 +1 @@
+[-2.]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json b/src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json
new file mode 100644
index 000000000..f61615d40
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json
@@ -0,0 +1 @@
+[-NaN]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_.-1.json b/src/test/resources/test-rom/data/json-parsing/n_number_.-1.json
new file mode 100644
index 000000000..1c9f2dd1b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_.-1.json
@@ -0,0 +1 @@
+[.-1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json b/src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json
new file mode 100644
index 000000000..c6c976f25
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json
@@ -0,0 +1 @@
+[.2e-3]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json
new file mode 100644
index 000000000..c83a25621
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json
@@ -0,0 +1 @@
+[0.1.2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json
new file mode 100644
index 000000000..a55a1bfef
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json
@@ -0,0 +1 @@
+[0.3e+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json
new file mode 100644
index 000000000..3dd5df4b3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json
@@ -0,0 +1 @@
+[0.3e]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json
new file mode 100644
index 000000000..c92c71ccb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json
@@ -0,0 +1 @@
+[0.e1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json
new file mode 100644
index 000000000..3ba2c7d6d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json
@@ -0,0 +1 @@
+[0E+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json
new file mode 100644
index 000000000..5301840d1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json
@@ -0,0 +1 @@
+[0E]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_0e+.json
new file mode 100644
index 000000000..8ab0bc4b8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0e+.json
@@ -0,0 +1 @@
+[0e+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0e.json b/src/test/resources/test-rom/data/json-parsing/n_number_0e.json
new file mode 100644
index 000000000..47ec421bb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_0e.json
@@ -0,0 +1 @@
+[0e]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json
new file mode 100644
index 000000000..cd84b9f69
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json
@@ -0,0 +1 @@
+[1.0e+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json
new file mode 100644
index 000000000..4eb7afa0f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json
@@ -0,0 +1 @@
+[1.0e-]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json
new file mode 100644
index 000000000..21753f4c7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json
@@ -0,0 +1 @@
+[1.0e]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1_000.json b/src/test/resources/test-rom/data/json-parsing/n_number_1_000.json
new file mode 100644
index 000000000..7b18b66b3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_1_000.json
@@ -0,0 +1 @@
+[1 000.0]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json b/src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json
new file mode 100644
index 000000000..4318a341d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json
@@ -0,0 +1 @@
+[1eE2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json b/src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json
new file mode 100644
index 000000000..4442f394d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json
@@ -0,0 +1 @@
+[2.e+3]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json b/src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json
new file mode 100644
index 000000000..a65060edf
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json
@@ -0,0 +1 @@
+[2.e-3]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json b/src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json
new file mode 100644
index 000000000..66f7cf701
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json
@@ -0,0 +1 @@
+[2.e3]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json
new file mode 100644
index 000000000..732a7b11c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json
@@ -0,0 +1 @@
+[9.e+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_Inf.json b/src/test/resources/test-rom/data/json-parsing/n_number_Inf.json
new file mode 100644
index 000000000..c40c734c3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_Inf.json
@@ -0,0 +1 @@
+[Inf]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_NaN.json b/src/test/resources/test-rom/data/json-parsing/n_number_NaN.json
new file mode 100644
index 000000000..499231790
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_NaN.json
@@ -0,0 +1 @@
+[NaN]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json b/src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json
new file mode 100644
index 000000000..b14587e5e
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json
@@ -0,0 +1 @@
+[1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_expression.json b/src/test/resources/test-rom/data/json-parsing/n_number_expression.json
new file mode 100644
index 000000000..76fdbc8a4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_expression.json
@@ -0,0 +1 @@
+[1+2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json b/src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json
new file mode 100644
index 000000000..3b214880c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json
@@ -0,0 +1 @@
+[0x1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json b/src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json
new file mode 100644
index 000000000..83e516ab0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json
@@ -0,0 +1 @@
+[0x42]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_infinity.json b/src/test/resources/test-rom/data/json-parsing/n_number_infinity.json
new file mode 100644
index 000000000..8c2baf783
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_infinity.json
@@ -0,0 +1 @@
+[Infinity]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json
new file mode 100644
index 000000000..1cce602b5
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json
@@ -0,0 +1 @@
+[0e+-1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json
new file mode 100644
index 000000000..5fc3c1efb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json
@@ -0,0 +1 @@
+[-123.123foo]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json
new file mode 100644
index 000000000..3b97e580e
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json
@@ -0,0 +1 @@
+[123å]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json
new file mode 100644
index 000000000..ea35d723c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json
@@ -0,0 +1 @@
+[1e1å]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json
new file mode 100644
index 000000000..371226e4c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json
@@ -0,0 +1 @@
+[0å]
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json b/src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json
new file mode 100644
index 000000000..cf4133d22
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json
@@ -0,0 +1 @@
+[-Infinity]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json
new file mode 100644
index 000000000..a6d8e78e7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json
@@ -0,0 +1 @@
+[-foo]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json b/src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json
new file mode 100644
index 000000000..9a5ebedf6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json
@@ -0,0 +1 @@
+[- 1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json b/src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json
new file mode 100644
index 000000000..67af0960a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json
@@ -0,0 +1 @@
+[-012]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json b/src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json
new file mode 100644
index 000000000..1f2a43496
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json
@@ -0,0 +1 @@
+[-.123]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json b/src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json
new file mode 100644
index 000000000..2aa73119f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json
@@ -0,0 +1 @@
+[-1x]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json b/src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json
new file mode 100644
index 000000000..9213dfca8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json
@@ -0,0 +1 @@
+[1ea]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json b/src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json
new file mode 100644
index 000000000..1e52ef964
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json
@@ -0,0 +1 @@
+[1eå]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json b/src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json
new file mode 100644
index 000000000..1de287cf8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json
@@ -0,0 +1 @@
+[1.]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json b/src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json
new file mode 100644
index 000000000..f682dbdce
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json
@@ -0,0 +1 @@
+[.123]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json
new file mode 100644
index 000000000..1e42d8182
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json
@@ -0,0 +1 @@
+[1.2a-3]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json
new file mode 100644
index 000000000..b79daccb8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json
@@ -0,0 +1 @@
+[1.8011670033376514H-308]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json b/src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json
new file mode 100644
index 000000000..7106da1f3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json
@@ -0,0 +1 @@
+[012]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json b/src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json
new file mode 100644
index 000000000..a03a8c03b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json
@@ -0,0 +1 @@
+["x", truth]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json
new file mode 100644
index 000000000..cc443b483
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json
@@ -0,0 +1 @@
+{[: "x"}
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json
new file mode 100644
index 000000000..8d5637708
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json
@@ -0,0 +1 @@
+{"x", null}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json
new file mode 100644
index 000000000..80e8c7b89
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json
@@ -0,0 +1 @@
+{"x"::"b"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_emoji.json b/src/test/resources/test-rom/data/json-parsing/n_object_emoji.json
new file mode 100644
index 000000000..cb4078eaa
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_emoji.json
@@ -0,0 +1 @@
+{🇨ðŸ‡}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json b/src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json
new file mode 100644
index 000000000..80c42cbad
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json
@@ -0,0 +1 @@
+{"a":"a" 123}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json b/src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json
new file mode 100644
index 000000000..77c327599
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json
@@ -0,0 +1 @@
+{key: 'value'}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json b/src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json
new file mode 100644
index 000000000..aa2cb637c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json
@@ -0,0 +1 @@
+{"¹":"0",}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json
new file mode 100644
index 000000000..b98eff62d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json
@@ -0,0 +1 @@
+{"a" b}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json
new file mode 100644
index 000000000..b4fb0f528
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json
@@ -0,0 +1 @@
+{:"b"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json
new file mode 100644
index 000000000..e3451384f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json
@@ -0,0 +1 @@
+{"a" "b"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json
new file mode 100644
index 000000000..3ef538a60
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json
@@ -0,0 +1 @@
+{"a":
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json
new file mode 100644
index 000000000..f3797b357
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json
@@ -0,0 +1 @@
+{"a"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json
new file mode 100644
index 000000000..b9945b34b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json
@@ -0,0 +1 @@
+{1:1}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json
new file mode 100644
index 000000000..b37fa86c0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json
@@ -0,0 +1 @@
+{9999E9999:1}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json b/src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json
new file mode 100644
index 000000000..f7d2959d0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json
@@ -0,0 +1 @@
+{null:null,null:null}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json b/src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json
new file mode 100644
index 000000000..3c9afe8dc
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json
@@ -0,0 +1 @@
+{"id":0,,,,,}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json b/src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json
new file mode 100644
index 000000000..e5cdf976a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json
@@ -0,0 +1 @@
+{'a':0}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json
new file mode 100644
index 000000000..a4b025094
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json
@@ -0,0 +1 @@
+{"id":0,}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json
new file mode 100644
index 000000000..a372c6553
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json
@@ -0,0 +1 @@
+{"a":"b"}/**/
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json
new file mode 100644
index 000000000..d557f41ca
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json
@@ -0,0 +1 @@
+{"a":"b"}/**//
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json
new file mode 100644
index 000000000..e335136c0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json
@@ -0,0 +1 @@
+{"a":"b"}//
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json
new file mode 100644
index 000000000..d892e49f1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json
@@ -0,0 +1 @@
+{"a":"b"}/
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json b/src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json
new file mode 100644
index 000000000..7c639ae64
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json
@@ -0,0 +1 @@
+{"a":"b",,"c":"d"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json
new file mode 100644
index 000000000..8ba137293
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json
@@ -0,0 +1 @@
+{a: "b"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json b/src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json
new file mode 100644
index 000000000..7fe699a6a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json
@@ -0,0 +1 @@
+{"a":"a
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json b/src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json
new file mode 100644
index 000000000..d63f7fbb7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json
@@ -0,0 +1 @@
+{ "foo" : "bar", "a" }
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json
new file mode 100644
index 000000000..787c8f0a8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json
@@ -0,0 +1 @@
+{"a":"b"}#
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_single_space.json b/src/test/resources/test-rom/data/json-parsing/n_single_space.json
new file mode 100644
index 000000000..0519ecba6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_single_space.json
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json
new file mode 100644
index 000000000..acec66d8f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json
@@ -0,0 +1 @@
+["\uD800\"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json
new file mode 100644
index 000000000..e834b05e9
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json
@@ -0,0 +1 @@
+["\uD800\u"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json
new file mode 100644
index 000000000..a04cd3489
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json
@@ -0,0 +1 @@
+["\uD800\u1"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json
new file mode 100644
index 000000000..bfbd23409
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json
@@ -0,0 +1 @@
+["\uD800\u1x"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json b/src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json
new file mode 100644
index 000000000..fd6895693
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json
@@ -0,0 +1 @@
+[é]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_backslash_00.json b/src/test/resources/test-rom/data/json-parsing/n_string_backslash_00.json
new file mode 100644
index 000000000..b5bf267b5
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/n_string_backslash_00.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json b/src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json
new file mode 100644
index 000000000..fae291938
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json
@@ -0,0 +1 @@
+["\x00"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json
new file mode 100644
index 000000000..016fcb47e
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json
@@ -0,0 +1 @@
+["\\\"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json
new file mode 100644
index 000000000..f35ea382b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json
@@ -0,0 +1 @@
+["\ "]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json
new file mode 100644
index 000000000..a27775421
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json
@@ -0,0 +1 @@
+["\🌀"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json
new file mode 100644
index 000000000..3415c33ca
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json
@@ -0,0 +1 @@
+["\"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json
new file mode 100644
index 000000000..0f2197ea2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json
@@ -0,0 +1 @@
+["\u00A"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json
new file mode 100644
index 000000000..75504a656
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json
@@ -0,0 +1 @@
+["\uD834\uDd"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json
new file mode 100644
index 000000000..bd9656060
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json
@@ -0,0 +1 @@
+["\uD800\uD800\x"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json
new file mode 100644
index 000000000..0c4300643
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json
@@ -0,0 +1 @@
+["\uå"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json
new file mode 100644
index 000000000..d1eb60921
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json
@@ -0,0 +1 @@
+["\a"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json
new file mode 100644
index 000000000..7608cb6ba
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json
@@ -0,0 +1 @@
+["\uqqqq"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json
new file mode 100644
index 000000000..2f757a25b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json
@@ -0,0 +1 @@
+["\å"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json b/src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json
new file mode 100644
index 000000000..7b297c636
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json
@@ -0,0 +1 @@
+[\u0020"asd"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json
new file mode 100644
index 000000000..01bc70aba
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json
@@ -0,0 +1 @@
+[\n]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json b/src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json
new file mode 100644
index 000000000..9d68933c4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json
@@ -0,0 +1 @@
+"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json b/src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json
new file mode 100644
index 000000000..caff239bf
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json
@@ -0,0 +1 @@
+['single quote']
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json b/src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json
new file mode 100644
index 000000000..f2ba8f84a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json
@@ -0,0 +1 @@
+abc
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json b/src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json
new file mode 100644
index 000000000..db62a46fc
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json
@@ -0,0 +1 @@
+["\
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_crtl_char.json b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_crtl_char.json
new file mode 100644
index 000000000..9f2134807
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_crtl_char.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json
new file mode 100644
index 000000000..700d36086
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json
@@ -0,0 +1,2 @@
+["new
+line"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json
new file mode 100644
index 000000000..160264a2d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json
@@ -0,0 +1 @@
+[" "]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json b/src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json
new file mode 100644
index 000000000..17332bb17
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json
@@ -0,0 +1 @@
+"\UA66D"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json
new file mode 100644
index 000000000..efe3bd272
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json
@@ -0,0 +1 @@
+""x
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json b/src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json
new file mode 100644
index 000000000..a4823eecc
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json b/src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json
new file mode 100644
index 000000000..81156a699
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json
@@ -0,0 +1 @@
+[â ]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json b/src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json
new file mode 100644
index 000000000..5f282702b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json
new file mode 100644
index 000000000..a56fef0b0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json
@@ -0,0 +1 @@
+<.>
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json
new file mode 100644
index 000000000..617f26254
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json
new file mode 100644
index 000000000..5a745e6f3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json
@@ -0,0 +1 @@
+[1]x
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json
new file mode 100644
index 000000000..6cfb1398d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json
@@ -0,0 +1 @@
+[1]]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json
new file mode 100644
index 000000000..ba6b1788b
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json
@@ -0,0 +1 @@
+["asd]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json b/src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json
new file mode 100644
index 000000000..ef2ab62fe
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json
@@ -0,0 +1 @@
+aå
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json b/src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json
new file mode 100644
index 000000000..7cd88469a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json
@@ -0,0 +1 @@
+[True]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json
new file mode 100644
index 000000000..d2af0c646
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json
@@ -0,0 +1 @@
+1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json b/src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json
new file mode 100644
index 000000000..ac61b8200
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json
@@ -0,0 +1 @@
+{"x": true,
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json
new file mode 100644
index 000000000..058d1626e
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json
@@ -0,0 +1 @@
+[][]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json
new file mode 100644
index 000000000..54caf60b1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json
@@ -0,0 +1 @@
+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json b/src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json
new file mode 100644
index 000000000..bfcdd514f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json
@@ -0,0 +1 @@
+ï»{}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json
new file mode 100644
index 000000000..8b1296cad
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json
@@ -0,0 +1 @@
+å
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json
new file mode 100644
index 000000000..8e2f0bef1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json
@@ -0,0 +1 @@
+[
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_no_data.json b/src/test/resources/test-rom/data/json-parsing/n_structure_no_data.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_null-byte-outside-string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_null-byte-outside-string.json
new file mode 100644
index 000000000..326db1442
Binary files /dev/null and b/src/test/resources/test-rom/data/json-parsing/n_structure_null-byte-outside-string.json differ
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json
new file mode 100644
index 000000000..0746539d2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json
@@ -0,0 +1 @@
+2@
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json
new file mode 100644
index 000000000..aa9ebaec5
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json
@@ -0,0 +1 @@
+{}}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json
new file mode 100644
index 000000000..17d045147
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json
@@ -0,0 +1 @@
+{"":
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json
new file mode 100644
index 000000000..ed1b569b7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json
@@ -0,0 +1 @@
+{"a":/*comment*/"b"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json
new file mode 100644
index 000000000..9ca2336d7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json
@@ -0,0 +1 @@
+{"a": true} "x"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json
new file mode 100644
index 000000000..8bebe3af0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json
@@ -0,0 +1 @@
+['
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json
new file mode 100644
index 000000000..6295fdc36
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json
@@ -0,0 +1 @@
+[,
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json
new file mode 100644
index 000000000..e870445b2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json
@@ -0,0 +1 @@
+[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json
new file mode 100644
index 000000000..7a63c8c57
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json
@@ -0,0 +1 @@
+[{
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json
new file mode 100644
index 000000000..9822a6baf
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json
@@ -0,0 +1 @@
+["a
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json
new file mode 100644
index 000000000..42a619362
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json
@@ -0,0 +1 @@
+["a"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json
new file mode 100644
index 000000000..81750b96f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json
@@ -0,0 +1 @@
+{
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json
new file mode 100644
index 000000000..eebc700a1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json
@@ -0,0 +1 @@
+{]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json
new file mode 100644
index 000000000..47bc9106f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json
@@ -0,0 +1 @@
+{,
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json
new file mode 100644
index 000000000..381ede5de
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json
@@ -0,0 +1 @@
+{[
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json
new file mode 100644
index 000000000..328c30cd7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json
@@ -0,0 +1 @@
+{"a
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json
new file mode 100644
index 000000000..9dba17090
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json
@@ -0,0 +1 @@
+{'a'
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json
new file mode 100644
index 000000000..841fd5f86
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json
@@ -0,0 +1 @@
+["\{["\{["\{["\{
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json b/src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json
new file mode 100644
index 000000000..92a39f398
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json
@@ -0,0 +1 @@
+é
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json b/src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json
new file mode 100644
index 000000000..f59ec20aa
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json
@@ -0,0 +1 @@
+*
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json b/src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json
new file mode 100644
index 000000000..898611087
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json
@@ -0,0 +1 @@
+{"a":"b"}#{}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json
new file mode 100644
index 000000000..df2f0f242
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json
@@ -0,0 +1 @@
+[\u000A""]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json
new file mode 100644
index 000000000..11209515c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json
@@ -0,0 +1 @@
+[1
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json
new file mode 100644
index 000000000..0d591762c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json
@@ -0,0 +1 @@
+[ false, nul
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json
new file mode 100644
index 000000000..a2ff8504a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json
@@ -0,0 +1 @@
+[ true, fals
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json
new file mode 100644
index 000000000..3149e8f5a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json
@@ -0,0 +1 @@
+[ false, tru
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json
new file mode 100644
index 000000000..694d69dbd
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json
@@ -0,0 +1 @@
+{"asd":"asd"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json
new file mode 100644
index 000000000..7284aea33
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json
@@ -0,0 +1 @@
+Ã¥
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json
new file mode 100644
index 000000000..81156a699
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json
@@ -0,0 +1 @@
+[â ]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json
new file mode 100644
index 000000000..a9ea535d1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/skip.lua b/src/test/resources/test-rom/data/json-parsing/skip.lua
new file mode 100644
index 000000000..3b147c7b4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/skip.lua
@@ -0,0 +1,21 @@
+local skip = {
+ -- Should fail, but pass.
+ -- We're too flexible on number formatting, but it's not a major concern.
+ "n_number_-01",
+ "n_number_-2.",
+ "n_number_0.e1",
+ "n_number_2.e3",
+ "n_number_2.e+3",
+ "n_number_2.e-3",
+ "n_number_neg_int_starting_with_zero",
+ "n_number_real_without_fractional_part",
+ "n_number_with_leading_zero",
+
+ -- Should pass, but fail.
+ -- These two are due to stack overflows within the parser.
+ "n_structure_open_array_object",
+ "n_structure_100000_opening_arrays",
+}
+
+for _, k in pairs(skip) do skip[k] = true end
+return skip
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json b/src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json
new file mode 100644
index 000000000..582290798
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json
@@ -0,0 +1 @@
+[[] ]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json b/src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json
new file mode 100644
index 000000000..93b6be2bc
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json
@@ -0,0 +1 @@
+[""]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_empty.json b/src/test/resources/test-rom/data/json-parsing/y_array_empty.json
new file mode 100644
index 000000000..0637a088a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_empty.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json b/src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json
new file mode 100644
index 000000000..eac5f7b46
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json
@@ -0,0 +1 @@
+["a"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_false.json b/src/test/resources/test-rom/data/json-parsing/y_array_false.json
new file mode 100644
index 000000000..67b2f0760
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_false.json
@@ -0,0 +1 @@
+[false]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json b/src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json
new file mode 100644
index 000000000..d3c1e2648
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json
@@ -0,0 +1 @@
+[null, 1, "1", {}]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_null.json b/src/test/resources/test-rom/data/json-parsing/y_array_null.json
new file mode 100644
index 000000000..500db4a86
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_null.json
@@ -0,0 +1 @@
+[null]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json
new file mode 100644
index 000000000..994825500
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json
@@ -0,0 +1,2 @@
+[1
+]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json
new file mode 100644
index 000000000..18bfe6422
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json
@@ -0,0 +1 @@
+ [1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json
new file mode 100644
index 000000000..99f6c5d1d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json
@@ -0,0 +1 @@
+[1,null,null,null,2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json
new file mode 100644
index 000000000..de9e7a944
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json
@@ -0,0 +1 @@
+[2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number.json b/src/test/resources/test-rom/data/json-parsing/y_number.json
new file mode 100644
index 000000000..e5f5cc334
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number.json
@@ -0,0 +1 @@
+[123e65]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json b/src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json
new file mode 100644
index 000000000..d1d396706
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json
@@ -0,0 +1 @@
+[0e+1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_0e1.json b/src/test/resources/test-rom/data/json-parsing/y_number_0e1.json
new file mode 100644
index 000000000..3283a7936
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_0e1.json
@@ -0,0 +1 @@
+[0e1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_after_space.json b/src/test/resources/test-rom/data/json-parsing/y_number_after_space.json
new file mode 100644
index 000000000..623570d96
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_after_space.json
@@ -0,0 +1 @@
+[ 4]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json b/src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json
new file mode 100644
index 000000000..96555ff78
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json
@@ -0,0 +1 @@
+[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json
new file mode 100644
index 000000000..a4ca9e754
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json
@@ -0,0 +1 @@
+[20e1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json b/src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json
new file mode 100644
index 000000000..37af1312a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json
@@ -0,0 +1 @@
+[-0]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json b/src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json
new file mode 100644
index 000000000..8e30f8bd9
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json
@@ -0,0 +1 @@
+[-123]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json b/src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json
new file mode 100644
index 000000000..99d21a2a0
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json
@@ -0,0 +1 @@
+[-1]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json b/src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json
new file mode 100644
index 000000000..37af1312a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json
@@ -0,0 +1 @@
+[-0]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json
new file mode 100644
index 000000000..6edbdfcb1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json
@@ -0,0 +1 @@
+[1E22]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json
new file mode 100644
index 000000000..0a01bd3ef
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json
@@ -0,0 +1 @@
+[1E-2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json
new file mode 100644
index 000000000..5a8fc0972
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json
@@ -0,0 +1 @@
+[1E+2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json
new file mode 100644
index 000000000..da2522d61
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json
@@ -0,0 +1 @@
+[123e45]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json
new file mode 100644
index 000000000..3944a7a45
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json
@@ -0,0 +1 @@
+[123.456e78]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json
new file mode 100644
index 000000000..ca40d3c25
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json
@@ -0,0 +1 @@
+[1e-2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json
new file mode 100644
index 000000000..343601d51
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json
@@ -0,0 +1 @@
+[1e+2]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json b/src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json
new file mode 100644
index 000000000..e47f69afc
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json
@@ -0,0 +1 @@
+[123]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json b/src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json
new file mode 100644
index 000000000..b02878e5f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json
@@ -0,0 +1 @@
+[123.456789]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object.json b/src/test/resources/test-rom/data/json-parsing/y_object.json
new file mode 100644
index 000000000..78262eda3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object.json
@@ -0,0 +1 @@
+{"asd":"sdf", "dfg":"fgh"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_basic.json b/src/test/resources/test-rom/data/json-parsing/y_object_basic.json
new file mode 100644
index 000000000..646bbe7bb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_basic.json
@@ -0,0 +1 @@
+{"asd":"sdf"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json
new file mode 100644
index 000000000..bbc2e1ce4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json
@@ -0,0 +1 @@
+{"a":"b","a":"c"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json
new file mode 100644
index 000000000..211581c20
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json
@@ -0,0 +1 @@
+{"a":"b","a":"b"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_empty.json b/src/test/resources/test-rom/data/json-parsing/y_object_empty.json
new file mode 100644
index 000000000..9e26dfeeb
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_empty.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json b/src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json
new file mode 100644
index 000000000..c0013d3b8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json
@@ -0,0 +1 @@
+{"":0}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json b/src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json
new file mode 100644
index 000000000..593f0f67f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json
@@ -0,0 +1 @@
+{"foo\u0000bar": 42}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json b/src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json
new file mode 100644
index 000000000..a0d3531c3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json
@@ -0,0 +1 @@
+{ "min": -1.0e+28, "max": 1.0e+28 }
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json b/src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json
new file mode 100644
index 000000000..bdc4a0871
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json
@@ -0,0 +1 @@
+{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_simple.json b/src/test/resources/test-rom/data/json-parsing/y_object_simple.json
new file mode 100644
index 000000000..dacac917f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_simple.json
@@ -0,0 +1 @@
+{"a":[]}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json b/src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json
new file mode 100644
index 000000000..8effdb297
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json
@@ -0,0 +1 @@
+{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json b/src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json
new file mode 100644
index 000000000..246ec6b34
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json
@@ -0,0 +1,3 @@
+{
+"a": "b"
+}
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json b/src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json
new file mode 100644
index 000000000..9967ddeb8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json
@@ -0,0 +1 @@
+["\u0060\u012a\u12AB"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json
new file mode 100644
index 000000000..996875cc8
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json
@@ -0,0 +1 @@
+["\uD801\udc37"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json
new file mode 100644
index 000000000..3401021ec
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json
@@ -0,0 +1 @@
+["\ud83d\ude39\ud83d\udc8d"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json b/src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json
new file mode 100644
index 000000000..7f495532f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json
@@ -0,0 +1 @@
+["\"\\\/\b\f\n\r\t"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json
new file mode 100644
index 000000000..d4439eda7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json
@@ -0,0 +1 @@
+["\\u0000"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json
new file mode 100644
index 000000000..ae03243b6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json
@@ -0,0 +1 @@
+["\""]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_comments.json b/src/test/resources/test-rom/data/json-parsing/y_string_comments.json
new file mode 100644
index 000000000..2260c20c2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_comments.json
@@ -0,0 +1 @@
+["a/*b*/c/*d//e"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json
new file mode 100644
index 000000000..6715d6f40
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json
@@ -0,0 +1 @@
+["\\a"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json
new file mode 100644
index 000000000..44ca56c4d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json
@@ -0,0 +1 @@
+["\\n"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json
new file mode 100644
index 000000000..5b014a9c2
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json
@@ -0,0 +1 @@
+["\u0012"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json
new file mode 100644
index 000000000..2ff52e2c5
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json
@@ -0,0 +1 @@
+["\uFFFF"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_in_array.json b/src/test/resources/test-rom/data/json-parsing/y_string_in_array.json
new file mode 100644
index 000000000..21d7ae4cd
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_in_array.json
@@ -0,0 +1 @@
+["asd"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json b/src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json
new file mode 100644
index 000000000..9e1887c1e
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json
@@ -0,0 +1 @@
+[ "asd"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json b/src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json
new file mode 100644
index 000000000..3919cef76
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json
@@ -0,0 +1 @@
+["\uDBFF\uDFFF"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json b/src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json
new file mode 100644
index 000000000..2085ab1a1
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json
@@ -0,0 +1 @@
+["new\u00A0line"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json
new file mode 100644
index 000000000..059e4d9dd
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json
@@ -0,0 +1 @@
+["ô¿¿"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json
new file mode 100644
index 000000000..4c913bd41
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json
@@ -0,0 +1 @@
+["ï¿¿"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json b/src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json
new file mode 100644
index 000000000..c1ad84404
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json
@@ -0,0 +1 @@
+["\u0000"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json b/src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json
new file mode 100644
index 000000000..157185923
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json
@@ -0,0 +1 @@
+["\u002c"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_pi.json b/src/test/resources/test-rom/data/json-parsing/y_string_pi.json
new file mode 100644
index 000000000..9df11ae88
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_pi.json
@@ -0,0 +1 @@
+["Ï€"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json b/src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json
new file mode 100644
index 000000000..10a33a171
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json
@@ -0,0 +1 @@
+["𛿿"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json b/src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json
new file mode 100644
index 000000000..8cadf7d05
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json
@@ -0,0 +1 @@
+["asd "]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_space.json b/src/test/resources/test-rom/data/json-parsing/y_string_space.json
new file mode 100644
index 000000000..efd782cc3
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_space.json
@@ -0,0 +1 @@
+" "
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json b/src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json
new file mode 100644
index 000000000..7620b6655
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json
@@ -0,0 +1 @@
+["\uD834\uDd1e"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json b/src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json
new file mode 100644
index 000000000..108f1d67d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json
@@ -0,0 +1 @@
+["\u0821"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json b/src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json
new file mode 100644
index 000000000..461503c31
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json
@@ -0,0 +1 @@
+["\u0123"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json b/src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json
new file mode 100644
index 000000000..897b6021a
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json
@@ -0,0 +1 @@
+["
"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json b/src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json
new file mode 100644
index 000000000..8cd998c89
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json
@@ -0,0 +1 @@
+["
"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json b/src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json
new file mode 100644
index 000000000..f7b41a02f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json
@@ -0,0 +1 @@
+["\u0061\u30af\u30EA\u30b9"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json b/src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json
new file mode 100644
index 000000000..3a5a220b6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json
@@ -0,0 +1 @@
+["new\u000Aline"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json b/src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json
new file mode 100644
index 000000000..7d064f498
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json
@@ -0,0 +1 @@
+[""]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode.json
new file mode 100644
index 000000000..3598095b7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode.json
@@ -0,0 +1 @@
+["\uA66D"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json
new file mode 100644
index 000000000..0bb3b51e7
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json
@@ -0,0 +1 @@
+["\u005C"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json
new file mode 100644
index 000000000..a7dcb9768
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json
@@ -0,0 +1 @@
+["â‚㈴â‚"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json
new file mode 100644
index 000000000..9a8370b96
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json
@@ -0,0 +1 @@
+["\uDBFF\uDFFE"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json
new file mode 100644
index 000000000..c51f8ae45
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json
@@ -0,0 +1 @@
+["\uD83F\uDFFE"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json
new file mode 100644
index 000000000..626d5f815
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json
@@ -0,0 +1 @@
+["\u200B"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json
new file mode 100644
index 000000000..1e23972c6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json
@@ -0,0 +1 @@
+["\u2064"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json
new file mode 100644
index 000000000..18ef151b4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json
@@ -0,0 +1 @@
+["\uFDD0"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json
new file mode 100644
index 000000000..13d261fda
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json
@@ -0,0 +1 @@
+["\uFFFE"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json
new file mode 100644
index 000000000..4e6257856
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json
@@ -0,0 +1 @@
+["\u0022"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_utf8.json b/src/test/resources/test-rom/data/json-parsing/y_string_utf8.json
new file mode 100644
index 000000000..40878435f
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_utf8.json
@@ -0,0 +1 @@
+["€ð„ž"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json b/src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json
new file mode 100644
index 000000000..8bd24907d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json
@@ -0,0 +1 @@
+["aa"]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json
new file mode 100644
index 000000000..02e4a84d6
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json
@@ -0,0 +1 @@
+false
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json
new file mode 100644
index 000000000..f70d7bba4
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json
@@ -0,0 +1 @@
+42
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json
new file mode 100644
index 000000000..b5135a207
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json
@@ -0,0 +1 @@
+-0.1
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json
new file mode 100644
index 000000000..ec747fa47
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json
@@ -0,0 +1 @@
+null
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json
new file mode 100644
index 000000000..b6e982ca9
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json
@@ -0,0 +1 @@
+"asd"
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json
new file mode 100644
index 000000000..f32a5804e
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json
@@ -0,0 +1 @@
+true
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json b/src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json
new file mode 100644
index 000000000..3cc762b55
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json
@@ -0,0 +1 @@
+""
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json b/src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json
new file mode 100644
index 000000000..0c3426d4c
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json
@@ -0,0 +1 @@
+["a"]
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json b/src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json
new file mode 100644
index 000000000..de601e305
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json
@@ -0,0 +1 @@
+[true]
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json b/src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json
new file mode 100644
index 000000000..2bedf7f2d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json
@@ -0,0 +1 @@
+ []
\ No newline at end of file
diff --git a/src/test/resources/test-rom/data/json-parsing/y_tab_whitespace.json b/src/test/resources/test-rom/data/json-parsing/y_tab_whitespace.json
new file mode 100644
index 000000000..d9a9c8b4d
--- /dev/null
+++ b/src/test/resources/test-rom/data/json-parsing/y_tab_whitespace.json
@@ -0,0 +1 @@
+[ ]
diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua
new file mode 100644
index 000000000..a5caec7a8
--- /dev/null
+++ b/src/test/resources/test-rom/mcfly.lua
@@ -0,0 +1,718 @@
+--- A very basic test framework for ComputerCraft
+--
+-- Like Busted (http://olivinelabs.com/busted/), but more memorable.
+--
+-- @usage
+-- describe("something to test", function()
+-- it("some property", function()
+-- expect(some_function()):equals("What it should equal")
+-- end)
+-- end)
+
+--- Assert an argument to the given function has the specified type.
+--
+-- @tparam string func The function's name
+-- @tparam int idx The argument index to this function
+-- @tparam string ty The type this argument should have. May be 'value' for
+-- any non-nil value.
+-- @param val val The value to check
+-- @throws If this value doesn't match the expected type.
+local function check(func, idx, ty, val)
+ if ty == 'value' then
+ if val == nil then
+ error(('%s: bad argument #%d (got nil)'):format(func, idx), 3)
+ end
+ elseif type(val) ~= ty then
+ return error(('%s: bad argument #%d (expected %s, got %s)'):format(func, idx, ty, type(val)), 3)
+ end
+end
+
+--- A stub - wraps a value within a a table,
+local stub_mt = {}
+stub_mt.__index = stub_mt
+
+--- Revert this stub, restoring the previous value.
+--
+-- Note, a stub can only be reverted once.
+function stub_mt:revert()
+ if not self.active then return end
+
+ self.active = false
+ rawset(self.stubbed_in, self.key, self.original)
+end
+
+local active_stubs = {}
+
+local function default_stub() end
+
+--- Stub a table entry with a new value.
+--
+-- @tparam table tbl The table whose field should be stubbed.
+-- @tparam string key The variable to stub
+-- @param[opt] value The value to stub it with. If this is a function, one can
+-- use the various stub expectation methods to determine what it was called
+-- with. Defaults to an empty function - pass @{nil} in explicitly to set the
+-- value to nil.
+-- @treturn Stub The resulting stub
+local function stub(tbl, key, ...)
+ check('stub', 1, 'table', tbl)
+ check('stub', 2, 'string', key)
+
+ local stub = setmetatable({
+ active = true,
+ stubbed_in = tbl,
+ key = key,
+ original = rawget(tbl, key),
+ }, stub_mt)
+
+ local value = ...
+ if select('#', ...) == 0 then value = default_stub end
+ if type(value) == "function" then
+ local arguments, delegate = {}, value
+ stub.arguments = arguments
+ value = function(...)
+ arguments[#arguments + 1] = table.pack(...)
+ return delegate(...)
+ end
+ end
+
+ table.insert(active_stubs, stub)
+ rawset(tbl, key, value)
+ return stub
+end
+
+--- Capture the current global state of the computer
+local function push_state()
+ local stubs = active_stubs
+ active_stubs = {}
+ return {
+ term = term.current(),
+ input = io.input(),
+ output = io.output(),
+ dir = shell.dir(),
+ path = shell.path(),
+ aliases = shell.aliases(),
+ stubs = stubs,
+ }
+end
+
+--- Restore the global state of the computer to a previous version
+local function pop_state(state)
+ for i = #active_stubs, 1, -1 do active_stubs[i]:revert() end
+
+ active_stubs = state.stubs
+
+ term.redirect(state.term)
+ io.input(state.input)
+ io.output(state.output)
+ shell.setDir(state.dir)
+ shell.setPath(state.path)
+
+ local aliases = shell.aliases()
+ for k in pairs(aliases) do
+ if not state.aliases[k] then shell.clearAlias(k) end
+ end
+ for k, v in pairs(state.aliases) do
+ if aliases[k] ~= v then shell.setAlias(k, v) end
+ end
+end
+
+local error_mt = { __tostring = function(self) return self.message end }
+
+--- Attempt to execute the provided function, gathering a stack trace when it
+-- errors.
+--
+-- @tparam function() fn The function to run
+-- @return[1] true
+-- @return[2] false
+-- @return[2] The error object
+local function try(fn)
+ if not debug or not debug.traceback then
+ local ok, err = pcall(fn)
+ if ok or getmetatable(err) == error_mt then
+ return ok, err
+ else
+ return ok, setmetatable({ message = tostring(err) }, error_mt)
+ end
+ end
+
+ local ok, err = xpcall(fn, function(err)
+ return { message = err, trace = debug.traceback(nil, 2) }
+ end)
+
+ -- If we succeeded, propagate it
+ if ok then return ok, err end
+
+ -- Error handling failed for some reason - just return a simpler error
+ if type(err) ~= "table" then
+ return ok, setmetatable({ message = tostring(err) }, error_mt)
+ end
+
+ -- Find the common substring the errors' trace and the current one. Then
+ -- eliminate it.
+ local trace = debug.traceback()
+ for i = 1, #trace do
+ if trace:sub(-i) ~= err.trace:sub(-i) then
+ err.trace = err.trace:sub(1, -i)
+ break
+ end
+ end
+
+ -- If we've received a rethrown error, copy
+ if getmetatable(err.message) == error_mt then
+ for k, v in pairs(err.message) do err[k] = v end
+ return ok, err
+ end
+
+ return ok, setmetatable(err, error_mt)
+end
+
+--- Fail a test with the given message
+--
+-- @tparam string message The message to fail with
+-- @throws An error with the given message
+local function fail(message)
+ check('fail', 1, 'string', message)
+ error(setmetatable({ message = message, fail = true }, error_mt))
+end
+
+--- Format an object in order to make it more readable
+--
+-- @param value The value to format
+-- @treturn string The formatted value
+local function format(value)
+ -- TODO: Look into something like mbs's pretty printer.
+ local ok, res = pcall(textutils.serialise, value)
+ if ok then return res else return tostring(value) end
+end
+
+local expect_mt = {}
+expect_mt.__index = expect_mt
+
+function expect_mt:_fail(message)
+ if self._extra then message = self._extra .. "\n" .. message end
+ fail(message)
+end
+
+--- Assert that this expectation has the provided value
+--
+-- @param value The value to require this expectation to be equal to
+-- @throws If the values are not equal
+function expect_mt:equals(value)
+ if value ~= self.value then
+ self:_fail(("Expected %s\n but got %s"):format(format(value), format(self.value)))
+ end
+
+ return self
+end
+expect_mt.equal = expect_mt.equals
+expect_mt.eq = expect_mt.equals
+
+--- Assert that this expectation does not equal the provided value
+--
+-- @param value The value to require this expectation to not be equal to
+-- @throws If the values are equal
+function expect_mt:not_equals(value)
+ if value == self.value then
+ self:_fail(("Expected any value but %s"):format(format(value)))
+ end
+
+ return self
+end
+expect_mt.not_equal = expect_mt.not_equals
+expect_mt.ne = expect_mt.not_equals
+
+--- Assert that this expectation has something of the provided type
+--
+-- @tparam string exp_type The type to require this expectation to have
+-- @throws If it does not have that thpe
+function expect_mt:type(exp_type)
+ local actual_type = type(self.value)
+ if exp_type ~= actual_type then
+ self:_fail(("Expected value of type %s\nbut got %s"):format(exp_type, actual_type))
+ end
+
+ return self
+end
+
+local function matches(eq, exact, left, right)
+ if left == right then return true end
+
+ local ty = type(left)
+ if ty ~= type(right) or ty ~= "table" then return false end
+
+ -- If we've already explored/are exploring the left and right then return
+ if eq[left] and eq[left][right] then return true end
+ if not eq[left] then eq[left] = { [right] = true } else eq[left][right] = true end
+ if not eq[right] then eq[right] = { [left] = true } else eq[right][left] = true end
+
+ -- Verify all pairs in left are equal to those in right
+ for k, v in pairs(left) do
+ if not matches(eq, exact, v, right[k]) then return false end
+ end
+
+ if exact then
+ -- And verify all pairs in right are present in left
+ for k in pairs(right) do
+ if left[k] == nil then return false end
+ end
+ end
+
+ return true
+end
+
+local function pairwise_equal(left, right)
+ if left.n ~= right.n then return false end
+
+ for i = 1, left.n do
+ if left[i] ~= right[i] then return false end
+ end
+
+ return true
+end
+
+--- Assert that this expectation is structurally equivalent to
+-- the provided object.
+--
+-- @param value The value to check for structural equivalence
+-- @throws If they are not equivalent
+function expect_mt:same(value)
+ if not matches({}, true, self.value, value) then
+ self:_fail(("Expected %s\nbut got %s"):format(format(value), format(self.value)))
+ end
+
+ return self
+end
+
+--- Assert that this expectation contains all fields mentioned
+-- in the provided object.
+--
+-- @param value The value to check against
+-- @throws If this does not match the provided value
+function expect_mt:matches(value)
+ if not matches({}, false, value, self.value) then
+ self:_fail(("Expected %s\nto match %s"):format(format(self.value), format(value)))
+ end
+
+ return self
+end
+
+--- Assert that this stub was called a specific number of times.
+--
+-- @tparam[opt] number The exact number of times the function must be called.
+-- If not given just require the function to be called at least once.
+-- @throws If this function was not called the expected number of times.
+function expect_mt:called(times)
+ if getmetatable(self.value) ~= stub_mt or self.value.arguments == nil then
+ self:_fail(("Expected stubbed function, got %s"):format(type(self.value)))
+ end
+
+ local called = #self.value.arguments
+
+ if times == nil then
+ if called == 0 then
+ self:_fail("Expected stub to be called\nbut it was not.")
+ end
+ else
+ check('stub', 1, 'number', times)
+ if called ~= times then
+ self:_fail(("Expected stub to be called %d times\nbut was called %d times."):format(times, called))
+ end
+ end
+
+ return self
+end
+
+local function called_with_check(eq, self, ...)
+ if getmetatable(self.value) ~= stub_mt or self.value.arguments == nil then
+ self:_fail(("Expected stubbed function, got %s"):format(type(self.value)))
+ end
+
+ local exp_args = table.pack(...)
+ local actual_args = self.value.arguments
+ for i = 1, #actual_args do
+ if eq(actual_args[i], exp_args) then return self end
+ end
+
+ local head = ("Expected stub to be called with %s\nbut was"):format(format(exp_args))
+ if #actual_args == 0 then
+ self:_fail(head .. " not called at all")
+ elseif #actual_args == 1 then
+ self:_fail(("%s called with %s."):format(head, format(actual_args[1])))
+ else
+ local lines = { head .. " called with:" }
+ for i = 1, #actual_args do lines[i + 1] = " - " .. format(actual_args[i]) end
+
+ self:_fail(table.concat(lines, "\n"))
+ end
+end
+
+--- Assert that this stub was called with a set of arguments
+--
+-- Arguments are compared using exact equality.
+function expect_mt:called_with(...)
+ return called_with_check(pairwise_equal, self, ...)
+end
+
+--- Assert that this stub was called with a set of arguments
+--
+-- Arguments are compared using matching.
+function expect_mt:called_with_matching(...)
+ return called_with_check(matches, self, ...)
+end
+
+--- Assert that this expectation matches a Lua pattern
+--
+-- @tparam string pattern The pattern to match against
+-- @throws If it does not match this pattern.
+function expect_mt:str_match(pattern)
+ local actual_type = type(self.value)
+ if actual_type ~= "string" then
+ self:_fail(("Expected value of type string\nbut got %s"):format(actual_type))
+ end
+ if not self.value:find(pattern) then
+ self:_fail(("Expected %q\n to match pattern %q"):format(self.value, pattern))
+ end
+
+ return self
+end
+
+--- Add extra information to this error message.
+--
+-- @tparam string message Additional message to prepend in the case of failures.
+-- @return The current
+function expect_mt:describe(message)
+ self._extra = tostring(message)
+ return self
+end
+
+local expect = {}
+setmetatable(expect, expect)
+
+--- Construct an expectation on the error message calling this function
+-- produces
+--
+-- @tparam fun The function to call
+-- @param ... The function arguments
+-- @return The new expectation
+function expect.error(fun, ...)
+ local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2)
+ if ok then fail("expected function to error") end
+ if res:sub(1, #line) == line then
+ res = res:sub(#line + 1)
+ elseif res:sub(1, 7) == "pcall: " then
+ res = res:sub(8)
+ end
+ return setmetatable({ value = res }, expect_mt)
+end
+
+--- Construct a new expectation from the provided value
+--
+-- @param value The value to apply assertions to
+-- @return The new expectation
+function expect:__call(value)
+ return setmetatable({ value = value }, expect_mt)
+end
+
+--- The stack of "describe"s.
+local test_stack = { n = 0 }
+
+--- Whether we're now running tests, and so cannot run any more.
+local tests_locked = false
+
+--- The list of tests that we'll run
+local test_list = {}
+local test_map, test_count = {}, 0
+
+--- Add a new test to our queue.
+--
+-- @param test The descriptor of this test
+local function do_test(test)
+ -- Set the name if it doesn't already exist
+ if not test.name then test.name = table.concat(test_stack, "\0", 1, test_stack.n) end
+ test_count = test_count + 1
+ test_list[test_count] = test
+ test_map[test.name] = test_count
+end
+
+--- Get the "friendly" name of this test.
+--
+-- @treturn string This test's friendly name
+local function test_name(test) return (test.name:gsub("\0", " \26 ")) end
+
+--- Describe something which will be tested, such as a function or situation
+--
+-- @tparam string name The name of the object to test
+-- @tparam function body A function which describes the tests for this object.
+local function describe(name, body)
+ check('describe', 1, 'string', name)
+ check('describe', 2, 'function', body)
+ if tests_locked then error("Cannot describe something while running tests", 2) end
+
+ -- Push our name onto the stack, eval and pop it
+ local n = test_stack.n + 1
+ test_stack[n], test_stack.n = name, n
+
+ local ok, err = try(body)
+
+ -- We count errors as a (failing) test.
+ if not ok then do_test { error = err } end
+
+ test_stack.n = n - 1
+end
+
+--- Declare a single test within a context
+--
+-- @tparam string name What you are testing
+-- @tparam function body A function which runs the test, failing if it does
+-- the assertions are not met.
+local function it(name, body)
+ check('it', 1, 'string', name)
+ check('it', 2, 'function', body)
+ if tests_locked then error("Cannot create test while running tests", 2) end
+
+ -- Push name onto the stack
+ local n = test_stack.n + 1
+ test_stack[n], test_stack.n, tests_locked = name, n, true
+
+ do_test { action = body }
+
+ -- Pop the test from the stack
+ test_stack.n, tests_locked = n - 1, false
+end
+
+--- Declare a single not-yet-implemented test
+--
+-- @tparam string name What you really should be testing but aren't
+local function pending(name)
+ check('it', 1, 'string', name)
+ if tests_locked then error("Cannot create test while running tests", 2) end
+
+ local _, loc = pcall(error, "", 3)
+ loc = loc:gsub(":%s*$", "")
+
+ local n = test_stack.n + 1
+ test_stack[n], test_stack.n = name, n
+ do_test { pending = true, trace = loc }
+ test_stack.n = n - 1
+end
+
+local native_co_create, native_loadfile = coroutine.create, loadfile
+local line_counts = {}
+if cct_test then
+ local string_sub, debug_getinfo = string.sub, debug.getinfo
+ local function debug_hook(_, line_nr)
+ local name = debug_getinfo(2, "S").source
+ if string_sub(name, 1, 1) ~= "@" then return end
+ name = string_sub(name, 2)
+
+ local file = line_counts[name]
+ if not file then file = {} line_counts[name] = file end
+ file[line_nr] = (file[line_nr] or 0) + 1
+ end
+
+ coroutine.create = function(...)
+ local co = native_co_create(...)
+ debug.sethook(co, debug_hook, "l")
+ return co
+ end
+
+ local expect = require "cc.expect".expect
+ _G.native_loadfile = native_loadfile
+ _G.loadfile = function(filename, mode, env)
+ -- Support the previous `loadfile(filename, env)` form instead.
+ if type(mode) == "table" and env == nil then
+ mode, env = nil, mode
+ end
+
+ expect(1, filename, "string")
+ expect(2, mode, "string", "nil")
+ expect(3, env, "table", "nil")
+
+ local file = fs.open(filename, "r")
+ if not file then return nil, "File not found" end
+
+ local func, err = load(file.readAll(), "@/" .. fs.combine(filename, ""), mode, env)
+ file.close()
+ return func, err
+ end
+
+ debug.sethook(debug_hook, "l")
+end
+
+local arg = ...
+if arg == "--help" or arg == "-h" then
+ io.write("Usage: mcfly [DIR]\n")
+ io.write("\n")
+ io.write("Run tests in the provided DIRectory, or `spec` if not given.")
+ return
+end
+
+local root_dir = shell.resolve(arg or "spec")
+if not fs.isDir(root_dir) then
+ io.stderr:write(("%q is not a directory.\n"):format(root_dir))
+ error()
+end
+
+-- Ensure the test folder is also on the package path
+package.path = ("/%s/?.lua;/%s/?/init.lua;%s"):format(root_dir, root_dir, package.path)
+
+do
+ -- Load in the tests from all our files
+ local env = setmetatable({}, { __index = _ENV })
+
+ local function set_env(tbl)
+ for k in pairs(env) do env[k] = nil end
+ for k, v in pairs(tbl) do env[k] = v end
+ end
+
+ -- When declaring tests, you shouldn't be able to use test methods
+ set_env { describe = describe, it = it, pending = pending }
+
+ local suffix = "_spec.lua"
+ local function run_in(sub_dir)
+ for _, name in ipairs(fs.list(sub_dir)) do
+ local file = fs.combine(sub_dir, name)
+ if fs.isDir(file) then
+ run_in(file)
+ elseif file:sub(-#suffix) == suffix then
+ local fun, err = loadfile(file, nil, env)
+ if not fun then
+ do_test { name = file:sub(#root_dir + 2), error = { message = err } }
+ else
+ local ok, err = try(fun)
+ if not ok then do_test { name = file:sub(#root_dir + 2), error = err } end
+ end
+ end
+ end
+ end
+
+ run_in(root_dir)
+
+ -- When running tests, you shouldn't be able to declare new ones.
+ set_env { expect = expect, fail = fail, stub = stub }
+end
+
+-- Error if we've found no tests
+if test_count == 0 then
+ io.stderr:write(("Could not find any tests in %q\n"):format(root_dir))
+ error()
+end
+
+-- The results of each test, as well as how many passed and the count.
+local test_results, test_status, tests_run = { n = 0 }, {}, 0
+
+-- All possible test statuses
+local statuses = {
+ pass = { desc = "Pass", col = colours.green, dot = "\7" }, -- Circle
+ fail = { desc = "Failed", col = colours.red, dot = "\4" }, -- Diamond
+ error = { desc = "Error", col = colours.magenta, dot = "\4" },
+ pending = { desc = "Pending", col = colours.yellow, dot = "\186" }, -- Hollow circle
+}
+
+-- Set up each test status count.
+for k in pairs(statuses) do test_status[k] = 0 end
+
+--- Do the actual running of our test
+local function do_run(test)
+ -- If we're a pre-computed test, determine our status message. Otherwise,
+ -- skip.
+ local status, err
+ if test.pending then
+ status = "pending"
+ elseif test.error then
+ err = test.error
+ status = "error"
+ elseif test.action then
+ local state = push_state()
+
+ -- Flush the event queue and ensure we're running with 0 timeout.
+ os.queueEvent("start_test") os.pullEvent("start_test")
+
+ local ok
+ ok, err = try(test.action)
+ status = ok and "pass" or (err.fail and "fail" or "error")
+
+ pop_state(state)
+ end
+
+ -- If we've a boolean status, then convert it into a string
+ if status == true then status = "pass"
+ elseif status == false then status = err.fail and "fail" or "error"
+ end
+
+ tests_run = tests_run + 1
+ test_status[status] = test_status[status] + 1
+ test_results[tests_run] = {
+ status = status, name = test.name,
+ message = test.message or err and err.message,
+ trace = test.trace or err and err.trace,
+ }
+
+ -- If we're running under howlci, then log some info.
+ if howlci then howlci.status(status, test_name(test)) end
+ if cct_test then cct_test.submit(test_results[tests_run]) end
+
+ -- Print our progress dot
+ local data = statuses[status]
+ term.setTextColour(data.col) io.write(data.dot)
+ term.setTextColour(colours.white)
+end
+
+-- Loop over all our tests, running them as required.
+if cct_test then
+ -- If we're within a cct_test environment, then submit them and wait on tests
+ -- to be run.
+ cct_test.start(test_map)
+ while true do
+ local _, name = os.pullEvent("cct_test_run")
+ if not name then break end
+ do_run(test_list[test_map[name]])
+ end
+else
+ for _, test in pairs(test_list) do do_run(test) end
+end
+
+-- Otherwise, display the results of each failure
+io.write("\n\n")
+for i = 1, tests_run do
+ local test = test_results[i]
+ if test.status ~= "pass" then
+ local status_data = statuses[test.status]
+
+ term.setTextColour(status_data.col)
+ io.write(status_data.desc)
+ term.setTextColour(colours.white)
+ io.write(" \26 " .. test_name(test) .. "\n")
+
+ if test.message then
+ io.write(" " .. test.message:gsub("\n", "\n ") .. "\n")
+ end
+
+ if test.trace then
+ term.setTextColour(colours.lightGrey)
+ io.write(" " .. test.trace:gsub("\n", "\n ") .. "\n")
+ end
+
+ io.write("\n")
+ end
+end
+
+-- And some summary statistics
+local actual_count = tests_run - test_status.pending
+local info = ("Ran %s test(s), of which %s passed (%g%%).")
+ :format(actual_count, test_status.pass, test_status.pass / actual_count * 100)
+
+if test_status.pending > 0 then
+ info = info .. (" Skipped %d pending test(s)."):format(test_status.pending)
+end
+
+term.setTextColour(colours.white) io.write(info .. "\n")
+
+-- Restore hook stubs
+debug.sethook(nil, "l")
+coroutine.create = native_co_create
+_G.loadfile = native_loadfile
+
+if cct_test then cct_test.finish(line_counts) end
+if howlci then howlci.log("debug", info) sleep(3) end
diff --git a/src/test/resources/test-rom/spec/apis/colors_spec.lua b/src/test/resources/test-rom/spec/apis/colors_spec.lua
new file mode 100644
index 000000000..c7505ca5a
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/colors_spec.lua
@@ -0,0 +1,92 @@
+describe("The colors library", function()
+ describe("colors.combine", function()
+ it("validates arguments", function()
+ expect.error(colors.combine, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(colors.combine, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ end)
+
+ it("combines colours", function()
+ expect(colors.combine()):eq(0)
+ expect(colors.combine(colors.red, colors.brown, colors.green)):eq(0x7000)
+ end)
+ end)
+
+ describe("colors.subtract", function()
+ it("validates arguments", function()
+ expect.error(colors.subtract, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(colors.subtract, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(colors.subtract, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ end)
+
+ it("subtracts colours", function()
+ expect(colors.subtract(0x7000, colors.green)):equals(0x5000)
+ expect(colors.subtract(0x5000, colors.red)):equals(0x1000)
+ end)
+ it("does nothing when color is not present", function()
+ expect(colors.subtract(0x1000, colors.red)):equals(0x1000)
+ end)
+ it("accepts multiple arguments", function()
+ expect(colors.subtract(0x7000, colors.red, colors.green, colors.red)):equals(0x1000)
+ end)
+ end)
+
+ describe("colors.test", function()
+ it("validates arguments", function()
+ expect.error(colors.test, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(colors.test, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ end)
+
+ it("returns true when present", function()
+ expect(colors.test(0x7000, colors.green)):equals(true)
+ end)
+ it("returns false when absent", function()
+ expect(colors.test(0x5000, colors.green)):equals(false)
+ end)
+ it("allows multiple colors", function()
+ expect(colors.test(0x7000, 0x5000)):equals(true)
+ end)
+ end)
+
+ describe("colors.packRGB", function()
+ it("validates arguments", function()
+ expect.error(colors.packRGB, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(colors.packRGB, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(colors.packRGB, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ end)
+
+ it("packs colours", function()
+ expect(colors.packRGB(0.3, 0.5, 0.6)):equals(0x4c7f99)
+ end)
+ end)
+
+ describe("colors.unpackRGB", function()
+ it("validates arguments", function()
+ expect.error(colors.unpackRGB, nil):eq("bad argument #1 (expected number, got nil)")
+ end)
+
+ it("unpacks colours", function()
+ expect({ colors.unpackRGB(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 }
+ end)
+ end)
+
+ it("colors.rgb8", function()
+ expect(colors.rgb8(0.3, 0.5, 0.6)):equals(0x4c7f99)
+ expect({ colors.rgb8(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 }
+ end)
+
+ describe("colors.toBlit", function()
+ it("validates arguments", function()
+ expect.error(colors.toBlit, nil):eq("bad argument #1 (expected number, got nil)")
+ end)
+
+ it("converts all colors", function()
+ for i = 0, 15 do
+ expect(colors.toBlit(2 ^ i)):eq(string.format("%x", i))
+ end
+ end)
+
+ it("floors colors", function()
+ expect(colors.toBlit(16385)):eq("e")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/fs_spec.lua b/src/test/resources/test-rom/spec/apis/fs_spec.lua
new file mode 100644
index 000000000..32503598e
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/fs_spec.lua
@@ -0,0 +1,222 @@
+describe("The fs library", function()
+ describe("fs.complete", function()
+ it("validates arguments", function()
+ fs.complete("", "")
+ fs.complete("", "", true)
+ fs.complete("", "", nil, true)
+
+ expect.error(fs.complete, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(fs.complete, "", nil):eq("bad argument #2 (expected string, got nil)")
+ expect.error(fs.complete, "", "", 1):eq("bad argument #3 (expected boolean, got number)")
+ expect.error(fs.complete, "", "", true, 1):eq("bad argument #4 (expected boolean, got number)")
+ end)
+ end)
+
+ describe("fs.isDriveRoot", function()
+ it("validates arguments", function()
+ fs.isDriveRoot("")
+
+ expect.error(fs.isDriveRoot, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("correctly identifies drive roots", function()
+ expect(fs.isDriveRoot("/rom")):eq(true)
+ expect(fs.isDriveRoot("/")):eq(true)
+ expect(fs.isDriveRoot("/rom/startup.lua")):eq(false)
+ expect(fs.isDriveRoot("/rom/programs/delete.lua")):eq(false)
+ end)
+ end)
+
+ describe("fs.list", function()
+ it("fails on files", function()
+ expect.error(fs.list, "rom/startup.lua"):eq("/rom/startup.lua: Not a directory")
+ expect.error(fs.list, "startup.lua"):eq("/startup.lua: Not a directory")
+ end)
+
+ it("fails on non-existent nodes", function()
+ expect.error(fs.list, "rom/x"):eq("/rom/x: Not a directory")
+ expect.error(fs.list, "x"):eq("/x: Not a directory")
+ end)
+ end)
+
+ describe("fs.combine", function()
+ it("removes . and ..", function()
+ expect(fs.combine("./a/b")):eq("a/b")
+ expect(fs.combine("a/b", "../c")):eq("a/c")
+ expect(fs.combine("a", "../c")):eq("c")
+ expect(fs.combine("a", "../../c")):eq("../c")
+ end)
+
+ it("combines empty paths", function()
+ expect(fs.combine("a")):eq("a")
+ expect(fs.combine("a", "")):eq("a")
+ expect(fs.combine("", "a")):eq("a")
+ expect(fs.combine("a", "", "b", "c")):eq("a/b/c")
+ end)
+ end)
+
+ describe("fs.getSize", function()
+ it("fails on non-existent nodes", function()
+ expect.error(fs.getSize, "rom/x"):eq("/rom/x: No such file")
+ expect.error(fs.getSize, "x"):eq("/x: No such file")
+ end)
+ end)
+
+ describe("fs.open", function()
+ describe("reading", function()
+ it("fails on directories", function()
+ expect { fs.open("rom", "r") }:same { nil, "/rom: No such file" }
+ expect { fs.open("", "r") }:same { nil, "/: No such file" }
+ end)
+
+ it("fails on non-existent nodes", function()
+ expect { fs.open("rom/x", "r") }:same { nil, "/rom/x: No such file" }
+ expect { fs.open("x", "r") }:same { nil, "/x: No such file" }
+ end)
+
+ it("errors when closing twice", function()
+ local handle = fs.open("rom/startup.lua", "r")
+ handle.close()
+ expect.error(handle.close):eq("attempt to use a closed file")
+ end)
+ end)
+
+ describe("reading in binary mode", function()
+ it("errors when closing twice", function()
+ local handle = fs.open("rom/startup.lua", "rb")
+ handle.close()
+ expect.error(handle.close):eq("attempt to use a closed file")
+ end)
+ end)
+
+ describe("writing", function()
+ it("fails on directories", function()
+ expect { fs.open("", "w") }:same { nil, "/: Cannot write to directory" }
+ end)
+
+ it("fails on read-only mounts", function()
+ expect { fs.open("rom/x", "w") }:same { nil, "/rom/x: Access denied" }
+ end)
+
+ it("errors when closing twice", function()
+ local handle = fs.open("test-files/out.txt", "w")
+ handle.close()
+ expect.error(handle.close):eq("attempt to use a closed file")
+ end)
+
+ it("fails gracefully when opening 'CON' on Windows", function()
+ local ok, err = fs.open("test-files/con", "w")
+ if ok then fs.delete("test-files/con") return end
+
+ -- On my Windows/Java version the message appears to be "Incorrect function.". It may not be
+ -- consistent though, and honestly doesn't matter too much.
+ expect(err):str_match("^/test%-files/con: .*")
+ end)
+ end)
+
+ describe("writing in binary mode", function()
+ it("errors when closing twice", function()
+ local handle = fs.open("test-files/out.txt", "wb")
+ handle.close()
+ expect.error(handle.close):eq("attempt to use a closed file")
+ end)
+ end)
+
+ describe("appending", function()
+ it("fails on directories", function()
+ expect { fs.open("", "a") }:same { nil, "/: Cannot write to directory" }
+ end)
+
+ it("fails on read-only mounts", function()
+ expect { fs.open("rom/x", "a") }:same { nil, "/rom/x: Access denied" }
+ end)
+ end)
+ end)
+
+ describe("fs.makeDir", function()
+ it("fails on files", function()
+ expect.error(fs.makeDir, "startup.lua"):eq("/startup.lua: File exists")
+ end)
+
+ it("fails on read-only mounts", function()
+ expect.error(fs.makeDir, "rom/x"):eq("/rom/x: Access denied")
+ end)
+ end)
+
+ describe("fs.delete", function()
+ it("fails on read-only mounts", function()
+ expect.error(fs.delete, "rom/x"):eq("/rom/x: Access denied")
+ end)
+ end)
+
+ describe("fs.copy", function()
+ it("fails on read-only mounts", function()
+ expect.error(fs.copy, "rom", "rom/startup"):eq("/rom/startup: Access denied")
+ end)
+
+ it("fails to copy a folder inside itself", function()
+ fs.makeDir("some-folder")
+ expect.error(fs.copy, "some-folder", "some-folder/x"):eq("/some-folder: Can't copy a directory inside itself")
+ expect.error(fs.copy, "some-folder", "Some-Folder/x"):eq("/some-folder: Can't copy a directory inside itself")
+ end)
+
+ it("copies folders", function()
+ fs.delete("some-folder")
+ fs.delete("another-folder")
+
+ fs.makeDir("some-folder")
+ fs.copy("some-folder", "another-folder")
+ expect(fs.isDir("another-folder")):eq(true)
+ end)
+ end)
+
+ describe("fs.move", function()
+ it("fails on read-only mounts", function()
+ expect.error(fs.move, "rom", "rom/move"):eq("Access denied")
+ expect.error(fs.move, "test-files", "rom/move"):eq("Access denied")
+ expect.error(fs.move, "rom", "test-files"):eq("Access denied")
+ end)
+ end)
+
+ describe("fs.getCapacity", function()
+ it("returns nil on read-only mounts", function()
+ expect(fs.getCapacity("rom")):eq(nil)
+ end)
+
+ it("returns the capacity on the root mount", function()
+ expect(fs.getCapacity("")):eq(10000000)
+ end)
+ end)
+
+ describe("fs.attributes", function()
+ it("errors on non-existent files", function()
+ expect.error(fs.attributes, "xuxu_nao_existe"):eq("/xuxu_nao_existe: No such file")
+ end)
+
+ it("returns information about read-only mounts", function()
+ expect(fs.attributes("rom")):matches { isDir = true, size = 0, isReadOnly = true }
+ end)
+
+ it("returns information about files", function()
+ local now = os.epoch("utc")
+
+ fs.delete("/tmp/basic-file")
+ local h = fs.open("/tmp/basic-file", "w")
+ h.write("A reasonably sized string")
+ h.close()
+
+ local attributes = fs.attributes("tmp/basic-file")
+ expect(attributes):matches { isDir = false, size = 25, isReadOnly = false }
+
+ if attributes.created - now >= 1000 then
+ fail(("Expected created time (%d) to be within 1000ms of now (%d"):format(attributes.created, now))
+ end
+
+ if attributes.modified - now >= 1000 then
+ fail(("Expected modified time (%d) to be within 1000ms of now (%d"):format(attributes.modified, now))
+ end
+
+ expect(attributes.modification):eq(attributes.modified)
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/gps_spec.lua b/src/test/resources/test-rom/spec/apis/gps_spec.lua
new file mode 100644
index 000000000..1e0a49f87
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/gps_spec.lua
@@ -0,0 +1,15 @@
+describe("The gps library", function()
+ describe("gps.locate", function()
+ it("validates arguments", function()
+ stub(_G, "commands", { getBlockPosition = function()
+ end, })
+
+ gps.locate()
+ gps.locate(1)
+ gps.locate(1, true)
+
+ expect.error(gps.locate, ""):eq("bad argument #1 (expected number, got string)")
+ expect.error(gps.locate, 1, ""):eq("bad argument #2 (expected boolean, got string)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/help_spec.lua b/src/test/resources/test-rom/spec/apis/help_spec.lua
new file mode 100644
index 000000000..f8ebcdb5c
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/help_spec.lua
@@ -0,0 +1,27 @@
+describe("The help library", function()
+ describe("help.setPath", function()
+ it("validates arguments", function()
+ help.setPath(help.path())
+ expect.error(help.setPath, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("help.lookup", function()
+ it("validates arguments", function()
+ help.lookup("")
+ expect.error(help.lookup, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("help.completeTopic", function()
+ it("validates arguments", function()
+ help.completeTopic("")
+ expect.error(help.completeTopic, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("completes topics without extensions", function()
+ expect(help.completeTopic("changel")):same { "og" }
+ expect(help.completeTopic("turt")):same { "le" }
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/http_spec.lua b/src/test/resources/test-rom/spec/apis/http_spec.lua
new file mode 100644
index 000000000..4a6a8b803
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/http_spec.lua
@@ -0,0 +1,21 @@
+describe("The http library", function()
+ describe("http.checkURL", function()
+ it("accepts well formed domains", function()
+ expect({ http.checkURL("https://google.com") }):same({ true })
+ end)
+
+ it("rejects malformed URLs", function()
+ expect({ http.checkURL("google.com") }):same({ false, "Must specify http or https" })
+ expect({ http.checkURL("https:google.com") }):same({ false, "URL malformed" })
+ expect({ http.checkURL("https:/google.com") }):same({ false, "URL malformed" })
+ expect({ http.checkURL("wss://google.com") }):same({ false, "Invalid protocol 'wss'" })
+ end)
+
+ it("rejects local domains", function()
+ -- Note, this is tested more thoroughly in AddressRuleTest. We've just got this here
+ -- to ensure the general control flow works.
+ expect({ http.checkURL("http://localhost") }):same({ false, "Domain not permitted" })
+ expect({ http.checkURL("http://127.0.0.1") }):same({ false, "Domain not permitted" })
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/io_spec.lua b/src/test/resources/test-rom/spec/apis/io_spec.lua
new file mode 100644
index 000000000..b1746093f
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/io_spec.lua
@@ -0,0 +1,328 @@
+--- Tests the io library is (mostly) consistent with PUC Lua.
+--
+-- These tests are based on the tests for Lua 5.1 and 5.3
+
+describe("The io library", function()
+ local file = "/test-files/tmp.txt"
+ local otherfile = "/test-files/tmp2.txt"
+
+ local t = '0123456789'
+ for _ = 1, 12 do t = t .. t end
+ assert(#t == 10 * 2 ^ 12)
+
+ local function read_all(f)
+ local h = fs.open(f, "rb")
+ local contents = h.readAll()
+ h.close()
+ return contents
+ end
+
+ local function write_file(f, contents)
+ local h = fs.open(f, "wb")
+ h.write(contents)
+ h.close()
+ end
+
+ local function setup()
+ write_file(file, "\"�lo\"{a}\nsecond line\nthird line \n�fourth_line\n\n\9\9 3450\n")
+ end
+
+ describe("io.close", function()
+ it("cannot close stdin", function()
+ expect{ io.stdin:close() }:same { nil, "attempt to close standard stream" }
+ end)
+ it("cannot close stdout", function()
+ expect{ io.stdout:close() }:same { nil, "attempt to close standard stream" }
+ end)
+ it("cannot close stdout", function()
+ expect{ io.stdout:close() }:same { nil, "attempt to close standard stream" }
+ end)
+ end)
+
+ it("io.input on a handle returns that handle", function()
+ expect(io.input(io.stdin)):equals(io.stdin)
+ end)
+
+ it("io.output on a handle returns that handle", function()
+ expect(io.output(io.stdout)):equals(io.stdout)
+ end)
+
+ it("defines a __name field", function()
+ expect(getmetatable(io.input()).__name):eq("FILE*")
+ end)
+
+ describe("io.type", function()
+ it("returns file on handles", function()
+ local handle = io.input()
+ expect(handle):type("table")
+ expect(io.type(handle)):equals("file")
+ expect(io.type(io.stdin)):equals("file")
+ end)
+
+ it("returns nil on values", function()
+ expect(io.type(8)):equals(nil)
+ end)
+
+ it("returns nil on tables", function()
+ expect(io.type(setmetatable({}, {}))):equals(nil)
+ end)
+ end)
+
+ describe("io.lines", function()
+ it("validates arguments", function()
+ io.lines(nil)
+ expect.error(io.lines, ""):eq("/: No such file")
+ expect.error(io.lines, false):eq("bad argument #1 (expected string, got boolean)")
+ end)
+
+ it("closes the file", function()
+ setup()
+
+ local n = 0
+ local f = io.lines(file)
+ while f() do n = n + 1 end
+ expect(n):eq(6)
+
+ expect.error(f):eq("file is already closed")
+ expect.error(f):eq("file is already closed")
+ end)
+
+ it("can copy a file", function()
+ setup()
+
+ local n = 0
+ io.output(otherfile)
+ for l in io.lines(file) do
+ io.write(l, "\n")
+ n = n + 1
+ end
+ io.close()
+ expect(n):eq(6)
+
+ io.input(file)
+ local f = io.open(otherfile):lines()
+ local n = 0
+ for l in io.lines() do
+ expect(l):eq(f())
+ n = n + 1
+ end
+ expect(n):eq(6)
+ end)
+
+ it("does not close on a normal file handle", function()
+ setup()
+
+ local f = assert(io.open(file))
+ local n = 0
+ for _ in f:lines() do n = n + 1 end
+ expect(n):eq(6)
+
+ expect(tostring(f):sub(1, 5)):eq("file ")
+ assert(f:close())
+
+ expect(tostring(f)):eq("file (closed)")
+ expect(io.type(f)):eq("closed file")
+ end)
+
+ it("accepts multiple arguments", function()
+ write_file(file, "0123456789\n")
+ for a, b in io.lines(file, 1, 1) do
+ if a == "\n" then
+ expect(b):eq(nil)
+ else
+ expect(tonumber(a)):eq(tonumber(b) - 1)
+ end
+ end
+
+ for a, b, c in io.lines(file, 1, 2, "a") do
+ expect(a):eq("0")
+ expect(b):eq("12")
+ expect(c):eq("3456789\n")
+ end
+
+ for a, b, c in io.lines(file, "a", 0, 1) do
+ if a == "" then break end
+ expect(a):eq("0123456789\n")
+ expect(b):eq(nil)
+ expect(c):eq(nil)
+ end
+
+ write_file(file, "00\n10\n20\n30\n40\n")
+ for a, b in io.lines(file, "n", "n") do
+ if a == 40 then
+ expect(b):eq(nil)
+ else
+ expect(a):eq(b - 10)
+ end
+ end
+ end)
+ end)
+
+ describe("io.open", function()
+ it("validates arguments", function()
+ io.open("")
+ io.open("", "r")
+
+ expect.error(io.open, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(io.open, "", false):eq("bad argument #2 (expected string, got boolean)")
+ end)
+
+ it("checks the mode", function()
+ io.open(file, "w"):close()
+
+ -- This really should be invalid mode, but I'll live.
+ expect.error(io.open, file, "rw"):str_match("Unsupported mode")
+ -- TODO: expect.error(io.open, file, "rb+"):str_match("Unsupported mode")
+ expect.error(io.open, file, "r+bk"):str_match("Unsupported mode")
+ expect.error(io.open, file, ""):str_match("Unsupported mode")
+ expect.error(io.open, file, "+"):str_match("Unsupported mode")
+ expect.error(io.open, file, "b"):str_match("Unsupported mode")
+
+ assert(io.open(file, "r+b")):close()
+ assert(io.open(file, "r+")):close()
+ assert(io.open(file, "rb")):close()
+ end)
+
+ it("returns an error message on non-existent files", function()
+ local a, b = io.open('xuxu_nao_existe')
+ expect(a):equals(nil)
+ expect(b):type("string")
+ end)
+ end)
+
+ describe("a readable handle", function()
+ it("cannot be written to", function()
+ write_file(file, "")
+ io.input(file)
+ expect { io.input():write("xuxu") }:same { nil, "file is not writable" }
+ io.input(io.stdin)
+ end)
+
+ it("supports various modes", function()
+ write_file(file, "alo\n " .. t .. " ;end of file\n")
+
+ io.input(file)
+ expect(io.read()):eq("alo")
+ expect(io.read(1)):eq(' ')
+ expect(io.read(#t)):eq(t)
+ expect(io.read(1)):eq(' ')
+ expect(io.read(0))
+ expect(io.read('*a')):eq(';end of file\n')
+ expect(io.read(0)):eq(nil)
+ expect(io.close(io.input())):eq(true)
+
+ fs.delete(file)
+ end)
+
+ it("support seeking", function()
+ setup()
+ io.input(file)
+
+ expect(io.read(0)):eq("") -- not eof
+ expect(io.read(5, '*l')):eq('"�lo"')
+ expect(io.read(0)):eq("")
+ expect(io.read()):eq("second line")
+ local x = io.input():seek()
+ expect(io.read()):eq("third line ")
+ assert(io.input():seek("set", x))
+ expect(io.read('*l')):eq("third line ")
+ expect(io.read(1)):eq("�")
+ expect(io.read(#"fourth_line")):eq("fourth_line")
+ assert(io.input():seek("cur", -#"fourth_line"))
+ expect(io.read()):eq("fourth_line")
+ expect(io.read()):eq("") -- empty line
+ expect(io.read(8)):eq('\9\9 3450') -- FIXME: Not actually supported
+ expect(io.read(1)):eq('\n')
+ expect(io.read(0)):eq(nil) -- end of file
+ expect(io.read(1)):eq(nil) -- end of file
+ expect(({ io.read(1) })[2]):eq(nil)
+ expect(io.read()):eq(nil) -- end of file
+ expect(({ io.read() })[2]):eq(nil)
+ expect(io.read('*n')):eq(nil) -- end of file
+ expect(({ io.read('*n') })[2]):eq(nil)
+ expect(io.read('*a')):eq('') -- end of file (OK for `*a')
+ expect(io.read('*a')):eq('') -- end of file (OK for `*a')
+
+ io.close(io.input())
+ end)
+
+ it("supports the 'L' mode", function()
+ write_file(file, "\n\nline\nother")
+
+ io.input(file)
+ expect(io.read"L"):eq("\n")
+ expect(io.read"L"):eq("\n")
+ expect(io.read"L"):eq("line\n")
+ expect(io.read"L"):eq("other")
+ expect(io.read"L"):eq(nil)
+ io.input():close()
+
+ local f = assert(io.open(file))
+ local s = ""
+ for l in f:lines("L") do s = s .. l end
+ expect(s):eq("\n\nline\nother")
+ f:close()
+
+ io.input(file)
+ s = ""
+ for l in io.lines(nil, "L") do s = s .. l end
+ expect(s):eq("\n\nline\nother")
+ io.input():close()
+
+ s = ""
+ for l in io.lines(file, "L") do s = s .. l end
+ expect(s):eq("\n\nline\nother")
+
+ s = ""
+ for l in io.lines(file, "l") do s = s .. l end
+ expect(s):eq("lineother")
+
+ write_file(file, "a = 10 + 34\na = 2*a\na = -a\n")
+ local t = {}
+ load(io.lines(file, "L"), nil, nil, t)()
+ expect(t.a):eq(-((10 + 34) * 2))
+ end)
+ end)
+
+ describe("a writable handle", function()
+ it("supports seeking", function()
+ fs.delete(file)
+ io.output(file)
+
+ expect(io.output()):not_equals(io.stdout)
+
+ expect(io.output():seek()):equal(0)
+ assert(io.write("alo alo"))
+ expect(io.output():seek()):equal(#"alo alo")
+ expect(io.output():seek("cur", -3)):equal(#"alo alo" - 3)
+ assert(io.write("joao"))
+ expect(io.output():seek("end")):equal(#"alo joao")
+
+ expect(io.output():seek("set")):equal(0)
+
+ assert(io.write('"�lo"', "{a}\n", "second line\n", "third line \n"))
+ assert(io.write('�fourth_line'))
+
+ io.output(io.stdout)
+ expect(io.output()):equals(io.stdout)
+ end)
+
+ it("supports appending", function()
+ io.output(file)
+ io.write("alo\n")
+ io.close()
+ expect.error(io.write)
+
+ local f = io.open(file, "a")
+ io.output(f)
+
+ assert(io.write(' ' .. t .. ' '))
+ assert(io.write(';', 'end of file\n'))
+ f:flush()
+ io.flush()
+ f:close()
+
+ expect(read_all(file)):eq("alo\n " .. t .. " ;end of file\n")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/keys_spec.lua b/src/test/resources/test-rom/spec/apis/keys_spec.lua
new file mode 100644
index 000000000..8983543bc
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/keys_spec.lua
@@ -0,0 +1,8 @@
+describe("The keys library", function()
+ describe("keys.getName", function()
+ it("validates arguments", function()
+ keys.getName(1)
+ expect.error(keys.getName, nil):eq("bad argument #1 (expected number, got nil)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/os_spec.lua b/src/test/resources/test-rom/spec/apis/os_spec.lua
new file mode 100644
index 000000000..4797c8574
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/os_spec.lua
@@ -0,0 +1,139 @@
+describe("The os library", function()
+ describe("os.date and os.time", function()
+ it("round trips correctly", function()
+ local t = math.floor(os.epoch("local") / 1000)
+ local T = os.date("*t", t)
+
+ expect(os.time(T)):eq(t)
+ end)
+
+ it("dst field is guessed", function()
+ local T = os.date("*t")
+ local t = os.time(T)
+ expect(T.isdst):type("boolean")
+ T.isdst = nil
+ expect(os.time(T)):eq(t) -- if isdst is absent uses correct default
+ end)
+
+ it("has 365 days in a year", function()
+ local T = os.date("*t")
+ local t = os.time(T)
+ T.year = T.year - 1
+ local t1 = os.time(T)
+ local delta = (t - t1) / (24 * 3600) - 365
+ -- allow for leap years
+ assert(math.abs(delta) < 2, ("expected abs(%d )< 2"):format(delta))
+ end)
+
+ it("os.date uses local timezone", function()
+ local epoch = os.epoch("local") / 1000
+ local date = os.time(os.date("*t"))
+ assert(date - epoch <= 2, ("expected %d - %d <= 2, but not the case"):format(date, epoch))
+ end)
+ end)
+
+ describe("os.date", function()
+ it("formats as expected", function()
+ -- From the PUC Lua tests, hence the weird style
+ local t = os.epoch("local")
+ local T = os.date("*t", t)
+
+ _G.T = T
+ loadstring(os.date([[assert(T.year==%Y and T.month==%m and T.day==%d and
+ T.hour==%H and T.min==%M and T.sec==%S and
+ T.wday==%w+1 and T.yday==%j and type(T.isdst) == 'boolean')]], t))()
+
+ T = os.date("!*t", t)
+ _G.T = T
+ loadstring(os.date([[!assert(T.year==%Y and T.month==%m and T.day==%d and
+ T.hour==%H and T.min==%M and T.sec==%S and
+ T.wday==%w+1 and T.yday==%j and type(T.isdst) == 'boolean')]], t))()
+ end)
+
+ describe("produces output consistent with PUC Lua", function()
+ -- Create a separate test for each code, just so it's easier to see what's broken
+ local t1 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 12, sec = 17 }
+ local function exp_code(code, value)
+ it(("for code '%s'"):format(code), function()
+ expect(os.date(code, t1)):eq(value)
+ end)
+ end
+
+ -- TODO: Java 16 apparently no longer treats TextStyle.FULL as full and will render Sun instead of Sunday.
+ exp_code("%a", "Sun")
+ -- exp_code("%A", "Sunday")
+ exp_code("%b", "Oct")
+ -- exp_code("%B", "October")
+ exp_code("%c", "Sun Oct 1 23:12:17 2000")
+ exp_code("%C", "20")
+ exp_code("%d", "01")
+ exp_code("%D", "10/01/00")
+ exp_code("%e", " 1")
+ exp_code("%F", "2000-10-01")
+ exp_code("%g", "00")
+ exp_code("%G", "2000")
+ exp_code("%h", "Oct")
+ exp_code("%H", "23")
+ exp_code("%I", "11")
+ exp_code("%j", "275")
+ exp_code("%m", "10")
+ exp_code("%M", "12")
+ exp_code("%n", "\n")
+ exp_code("%p", "PM")
+ exp_code("%r", "11:12:17 PM")
+ exp_code("%R", "23:12")
+ exp_code("%S", "17")
+ exp_code("%t", "\t")
+ exp_code("%T", "23:12:17")
+ exp_code("%u", "7")
+ exp_code("%U", "40")
+ exp_code("%V", "39")
+ exp_code("%w", "0")
+ exp_code("%W", "39")
+ exp_code("%x", "10/01/00")
+ exp_code("%X", "23:12:17")
+ exp_code("%y", "00")
+ exp_code("%Y", "2000")
+ exp_code("%%", "%")
+
+ it("zones are numbers", function()
+ local zone = os.date("%z", t1)
+ if not zone:match("^[+-]%d%d%d%d$") then
+ error("Invalid zone: " .. zone)
+ end
+ end)
+
+ it("zones id is made of letters", function()
+ local zone = os.date("%Z", t1)
+ if not zone:match("^%a%a+$") then
+ error("Non letter character in zone: " .. zone)
+ end
+ end)
+
+ local t2 = os.time { year = 2000, month = 10, day = 1, hour = 3, min = 12, sec = 17 }
+ it("for code '%I' #2", function()
+ expect(os.date("%I", t2)):eq("03")
+ end)
+ end)
+ end)
+
+ describe("os.time", function()
+ it("maps directly to seconds", function()
+ local t1 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 12, sec = 17 }
+ local t2 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 10, sec = 19 }
+ expect(t1 - t2):eq(60 * 2 - 2)
+ end)
+ end)
+
+ describe("os.loadAPI", function()
+ it("validates arguments", function()
+ expect.error(os.loadAPI, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("os.unloadAPI", function()
+ it("validates arguments", function()
+ expect.error(os.loadAPI, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/paintutils_spec.lua b/src/test/resources/test-rom/spec/apis/paintutils_spec.lua
new file mode 100644
index 000000000..f13bbcfd2
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/paintutils_spec.lua
@@ -0,0 +1,176 @@
+local with_window = require "test_helpers".with_window
+
+describe("The paintutils library", function()
+ -- Verifies that a window's lines are equal to the given table of blit
+ -- strings ({{"text", "fg", "bg"}, {"text", "fg", "bg"}...})
+ local function window_eq(w, state)
+ -- Verification of the size isn't really important in the tests, but
+ -- better safe than sorry.
+ local _, height = w.getSize()
+ expect(#state):eq(height)
+
+ for line = 1, height do
+ expect({ w.getLine(line) }):same(state[line])
+ end
+ end
+
+ describe("paintutils.parseImage", function()
+ it("validates arguments", function()
+ paintutils.parseImage("")
+ expect.error(paintutils.parseImage, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("paintutils.loadImage", function()
+ it("validates arguments", function()
+ expect.error(paintutils.loadImage, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("paintutils.drawPixel", function()
+ it("validates arguments", function()
+ expect.error(paintutils.drawPixel, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(paintutils.drawPixel, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(paintutils.drawPixel, 1, 1, false):eq("bad argument #3 (expected number, got boolean)")
+ end)
+ end)
+
+ describe("paintutils.drawLine", function()
+ it("validates arguments", function()
+ expect.error(paintutils.drawLine, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(paintutils.drawLine, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(paintutils.drawLine, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ expect.error(paintutils.drawLine, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
+ expect.error(paintutils.drawLine, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
+ end)
+
+ it("draws a line going across with custom colour", function()
+ local w = with_window(3, 2, function()
+ paintutils.drawLine(1, 1, 3, 1, colours.red)
+ end)
+
+ window_eq(w, {
+ { " ", "000", "eee" },
+ { " ", "000", "fff" },
+ })
+ end)
+
+ it("draws a line going diagonally with term colour", function()
+ local w = with_window(3, 3, function()
+ term.setBackgroundColour(colours.red)
+ paintutils.drawLine(1, 1, 3, 3)
+ end)
+
+ window_eq(w, {
+ { " ", "000", "eff" },
+ { " ", "000", "fef" },
+ { " ", "000", "ffe" },
+ })
+ end)
+
+ it("draws a line going diagonally from bottom left", function()
+ local w = with_window(3, 3, function()
+ term.setBackgroundColour(colours.red)
+ paintutils.drawLine(1, 3, 3, 1)
+ end)
+
+ window_eq(w, {
+ { " ", "000", "ffe" },
+ { " ", "000", "fef" },
+ { " ", "000", "eff" },
+ })
+ end)
+ end)
+
+ describe("paintutils.drawBox", function()
+ it("validates arguments", function()
+ expect.error(paintutils.drawBox, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(paintutils.drawBox, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(paintutils.drawBox, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ expect.error(paintutils.drawBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
+ expect.error(paintutils.drawBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
+ end)
+
+ it("draws a box with term colour", function()
+ local w = with_window(3, 3, function()
+ term.setBackgroundColour(colours.red)
+ paintutils.drawBox(1, 1, 3, 3)
+ end)
+
+ window_eq(w, {
+ { " ", "eee", "eee" },
+ { " ", "e0e", "efe" },
+ { " ", "eee", "eee" },
+ })
+ end)
+
+ it("draws a box with custom colour", function()
+ local w = with_window(3, 3, function()
+ paintutils.drawBox(1, 1, 3, 3, colours.red)
+ end)
+
+ window_eq(w, {
+ { " ", "eee", "eee" },
+ { " ", "e0e", "efe" },
+ { " ", "eee", "eee" },
+ })
+ end)
+
+ it("draws a box without overwriting existing content", function()
+ local w = with_window(3, 3, function()
+ term.setCursorPos(2, 2)
+ term.write("a")
+ paintutils.drawBox(1, 1, 3, 3, colours.red)
+ end)
+
+ window_eq(w, {
+ { " ", "eee", "eee" },
+ { " a ", "e0e", "efe" },
+ { " ", "eee", "eee" },
+ })
+ end)
+ end)
+
+ describe("paintutils.drawFilledBox", function()
+ it("validates arguments", function()
+ expect.error(paintutils.drawFilledBox, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(paintutils.drawFilledBox, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(paintutils.drawFilledBox, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ expect.error(paintutils.drawFilledBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
+ expect.error(paintutils.drawFilledBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
+ end)
+
+ it("draws a filled box with term colour", function()
+ local w = with_window(3, 3, function()
+ term.setBackgroundColour(colours.red)
+ paintutils.drawFilledBox(1, 1, 3, 3)
+ end)
+
+ window_eq(w, {
+ { " ", "eee", "eee" },
+ { " ", "eee", "eee" },
+ { " ", "eee", "eee" },
+ })
+ end)
+
+ it("draws a filled box with custom colour", function()
+ local w = with_window(3, 3, function()
+ paintutils.drawFilledBox(1, 1, 3, 3, colours.red)
+ end)
+
+ window_eq(w, {
+ { " ", "eee", "eee" },
+ { " ", "eee", "eee" },
+ { " ", "eee", "eee" },
+ })
+ end)
+ end)
+
+ describe("paintutils.drawImage", function()
+ it("validates arguments", function()
+ expect.error(paintutils.drawImage, nil):eq("bad argument #1 (expected table, got nil)")
+ expect.error(paintutils.drawImage, {}, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(paintutils.drawImage, {}, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/parallel_spec.lua b/src/test/resources/test-rom/spec/apis/parallel_spec.lua
new file mode 100644
index 000000000..c8bd73648
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/parallel_spec.lua
@@ -0,0 +1,124 @@
+describe("The parallel library", function()
+ describe("parallel.waitForAny", function()
+ it("validates arguments", function()
+ expect.error(parallel.waitForAny, ""):eq("bad argument #1 (expected function, got string)")
+ expect.error(parallel.waitForAny, function() end, 2):eq("bad argument #2 (expected function, got number)")
+ end)
+
+ it("returns immediately with no arguments", function()
+ expect(parallel.waitForAny()):eq(0)
+ end)
+
+ it("runs functions in parallel", function()
+ local entries = {}
+ local function a()
+ entries[#entries + 1] = "first"
+ local s = coroutine.yield()
+ entries[#entries + 1] = s
+ end
+ local function b()
+ entries[#entries + 1] = "second"
+ local s = coroutine.yield()
+ entries[#entries + 1] = s
+ end
+ os.queueEvent("yield")
+ parallel.waitForAny(a, b)
+ expect(entries):same({ "first", "second", "yield" })
+ end)
+
+ it("accepts an arbitrary number of functions", function()
+ local count = 0
+ local fns = {}
+ for i = 1, 50 do fns[i] = function()
+ count = count + 1
+ coroutine.yield()
+ end end
+ os.queueEvent("dummy")
+ parallel.waitForAny(table.unpack(fns))
+ expect(count):eq(50)
+ end)
+
+ it("passes errors to the caller", function()
+ expect.error(parallel.waitForAny, function() error("Test error") end):str_match("Test error$")
+ end)
+
+ it("returns the number of the function that exited first", function()
+ os.queueEvent("dummy")
+ os.queueEvent("dummy")
+ expect(parallel.waitForAny(function()
+ coroutine.yield()
+ coroutine.yield()
+ end, function()
+ coroutine.yield()
+ return
+ end, function()
+ coroutine.yield()
+ coroutine.yield()
+ end)):eq(2)
+ end)
+ end)
+
+ describe("parallel.waitForAll", function()
+ it("validates arguments", function()
+ expect.error(parallel.waitForAll, ""):eq("bad argument #1 (expected function, got string)")
+ expect.error(parallel.waitForAll, function() end, 2):eq("bad argument #2 (expected function, got number)")
+ end)
+
+ it("returns immediately with no arguments", function()
+ parallel.waitForAll()
+ end)
+
+ it("runs functions in parallel", function()
+ local entries = {}
+ local function a()
+ entries[#entries + 1] = "first"
+ local s = coroutine.yield()
+ entries[#entries + 1] = s
+ end
+ local function b()
+ entries[#entries + 1] = "second"
+ local s = coroutine.yield()
+ entries[#entries + 1] = s
+ end
+ os.queueEvent("yield")
+ parallel.waitForAll(a, b)
+ expect(entries):same({ "first", "second", "yield", "yield" })
+ end)
+
+ it("accepts an arbitrary number of functions", function()
+ local count = 0
+ local fns = {}
+ for i = 1, 50 do fns[i] = function()
+ count = count + 1
+ coroutine.yield()
+ end end
+ os.queueEvent("dummy")
+ parallel.waitForAll(table.unpack(fns))
+ expect(count):eq(50)
+ end)
+
+ it("passes errors to the caller", function()
+ expect.error(parallel.waitForAll, function() error("Test error") end):str_match("Test error$")
+ end)
+
+ it("completes all functions before exiting", function()
+ local exitCount = 0
+ os.queueEvent("dummy")
+ os.queueEvent("dummy")
+ parallel.waitForAll(function()
+ coroutine.yield()
+ coroutine.yield()
+ exitCount = exitCount + 1
+ end, function()
+ coroutine.yield()
+ exitCount = exitCount + 1
+ return
+ end, function()
+ coroutine.yield()
+ coroutine.yield()
+ exitCount = exitCount + 1
+ end)
+ expect(exitCount):eq(3)
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/peripheral_spec.lua b/src/test/resources/test-rom/spec/apis/peripheral_spec.lua
new file mode 100644
index 000000000..c64c042ce
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/peripheral_spec.lua
@@ -0,0 +1,117 @@
+describe("The peripheral library", function()
+ local it_modem = peripheral.getType("top") == "modem" and it or pending
+
+ local multitype_peripheral = setmetatable({}, {
+ __name = "peripheral",
+ name = "top",
+ type = "modem",
+ types = { "modem", "inventory", modem = true, inventory = true },
+ })
+
+ describe("peripheral.isPresent", function()
+ it("validates arguments", function()
+ peripheral.isPresent("")
+ expect.error(peripheral.isPresent, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("peripheral.getName", function()
+ it("validates arguments", function()
+ expect.error(peripheral.getName, nil):eq("bad argument #1 (expected table, got nil)")
+ expect.error(peripheral.getName, {}):eq("bad argument #1 (table is not a peripheral)")
+ end)
+
+ it_modem("can get the name of a wrapped peripheral", function()
+ expect(peripheral.getName(peripheral.wrap("top"))):eq("top")
+ end)
+ end)
+
+ describe("peripheral.getType", function()
+ it("validates arguments", function()
+ peripheral.getType("")
+ expect.error(peripheral.getType, nil):eq("bad argument #1 (expected string or table, got nil)")
+ expect.error(peripheral.getType, {}):eq("bad argument #1 (table is not a peripheral)")
+ end)
+
+ it("returns nil when no peripheral is present", function()
+ expect(peripheral.getType("bottom")):eq(nil)
+ end)
+
+ it_modem("can get the type of a peripheral by side", function()
+ expect(peripheral.getType("top")):eq("modem")
+ end)
+
+ it_modem("can get the type of a wrapped peripheral", function()
+ expect(peripheral.getType(peripheral.wrap("top"))):eq("modem")
+ end)
+
+ it("can return multiple types", function()
+ expect({ peripheral.getType(multitype_peripheral) }):same { "modem", "inventory" }
+ end)
+ end)
+
+ describe("peripheral.hasType", function()
+ it("validates arguments", function()
+ peripheral.getType("")
+ expect.error(peripheral.hasType, nil):eq("bad argument #1 (expected string or table, got nil)")
+ expect.error(peripheral.hasType, {}, ""):eq("bad argument #1 (table is not a peripheral)")
+ expect.error(peripheral.hasType, ""):eq("bad argument #2 (expected string, got nil)")
+ end)
+
+ it("returns nil when no peripherals are present", function()
+ expect(peripheral.hasType("bottom", "modem")):eq(nil)
+ end)
+
+ it_modem("can check type of a peripheral by side", function()
+ expect(peripheral.hasType("top", "modem")):eq(true)
+ expect(peripheral.hasType("top", "not_a_modem")):eq(false)
+ end)
+
+ it_modem("can check the type of a wrapped peripheral (true)", function()
+ expect(peripheral.hasType(peripheral.wrap("top"), "modem")):eq(true)
+ end)
+
+ it("can check the type of a wrapped peripheral (fake)", function()
+ expect(peripheral.hasType(multitype_peripheral, "modem")):eq(true)
+ expect(peripheral.hasType(multitype_peripheral, "inventory")):eq(true)
+ expect(peripheral.hasType(multitype_peripheral, "something else")):eq(false)
+ end)
+ end)
+
+ describe("peripheral.getMethods", function()
+ it("validates arguments", function()
+ peripheral.getMethods("")
+ expect.error(peripheral.getMethods, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("peripheral.call", function()
+ it("validates arguments", function()
+ peripheral.call("", "")
+ expect.error(peripheral.call, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(peripheral.call, "", nil):eq("bad argument #2 (expected string, got nil)")
+ end)
+
+ it_modem("has the correct error location", function()
+ expect.error(function() peripheral.call("top", "isOpen", false) end)
+ :str_match("^[^:]+:%d+: bad argument #1 %(number expected, got boolean%)$")
+ end)
+ end)
+
+ describe("peripheral.wrap", function()
+ it("validates arguments", function()
+ peripheral.wrap("")
+ expect.error(peripheral.wrap, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("peripheral.find", function()
+ it("validates arguments", function()
+ peripheral.find("")
+ peripheral.find("", function()
+ end)
+ expect.error(peripheral.find, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(peripheral.find, "", false):eq("bad argument #2 (expected function, got boolean)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/rednet_spec.lua b/src/test/resources/test-rom/spec/apis/rednet_spec.lua
new file mode 100644
index 000000000..35cb4c1df
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/rednet_spec.lua
@@ -0,0 +1,248 @@
+describe("The rednet library", function()
+ describe("rednet.open", function()
+ it("validates arguments", function()
+ expect.error(rednet.open, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("requires a modem to be present", function()
+ expect.error(rednet.open, "not_there"):eq("No such modem: not_there")
+ end)
+ end)
+
+ describe("rednet.close", function()
+ it("validates arguments", function()
+ rednet.close()
+ expect.error(rednet.close, 1):eq("bad argument #1 (expected string, got number)")
+ expect.error(rednet.close, false):eq("bad argument #1 (expected string, got boolean)")
+ end)
+
+ it("requires a modem to be present", function()
+ expect.error(rednet.close, "not_there"):eq("No such modem: not_there")
+ end)
+ end)
+
+ describe("rednet.isOpen", function()
+ it("validates arguments", function()
+ rednet.isOpen()
+ rednet.isOpen("")
+ expect.error(rednet.isOpen, 1):eq("bad argument #1 (expected string, got number)")
+ expect.error(rednet.isOpen, false):eq("bad argument #1 (expected string, got boolean)")
+ end)
+ end)
+
+ describe("rednet.send", function()
+ it("validates arguments", function()
+ rednet.send(1)
+ rednet.send(1, nil, "")
+ expect.error(rednet.send, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(rednet.send, 1, nil, false):eq("bad argument #3 (expected string, got boolean)")
+ end)
+ end)
+
+ describe("rednet.broadcast", function()
+ it("validates arguments", function()
+ rednet.broadcast(nil)
+ rednet.broadcast(nil, "")
+ expect.error(rednet.broadcast, nil, false):eq("bad argument #2 (expected string, got boolean)")
+ end)
+ end)
+
+ describe("rednet.receive", function()
+ it("validates arguments", function()
+ expect.error(rednet.receive, false):eq("bad argument #1 (expected string, got boolean)")
+ expect.error(rednet.receive, "", false):eq("bad argument #2 (expected number, got boolean)")
+ end)
+ end)
+
+ describe("rednet.host", function()
+ it("validates arguments", function()
+ expect.error(rednet.host, "", "localhost"):eq("Reserved hostname")
+ expect.error(rednet.host, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(rednet.host, "", nil):eq("bad argument #2 (expected string, got nil)")
+ end)
+ end)
+
+ describe("rednet.unhost", function()
+ it("validates arguments", function()
+ rednet.unhost("")
+ expect.error(rednet.unhost, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("rednet.lookup", function()
+ it("validates arguments", function()
+ expect.error(rednet.lookup, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(rednet.lookup, "", false):eq("bad argument #2 (expected string, got boolean)")
+ end)
+
+ it("gets a locally hosted protocol", function()
+ rednet.host("a_protocol", "a_hostname")
+
+ expect(rednet.lookup("a_protocol")):eq(os.getComputerID())
+ expect(rednet.lookup("a_protocol", "localhost")):eq(os.getComputerID())
+ expect(rednet.lookup("a_protocol", "a_hostname")):eq(os.getComputerID())
+ end)
+ end)
+
+ describe("on fake computers", function()
+ local fake_computer = require "support.fake_computer"
+ local debugx = require "support.debug_ext"
+
+ local function dawdle() while true do coroutine.yield() end end
+ local function computer_with_rednet(id, fn, options)
+ local computer = fake_computer.make_computer(id, function(env)
+ local fns = { env.rednet.run }
+ if options and options.rep then
+ fns[#fns + 1] = function() env.dofile("rom/programs/rednet/repeat.lua") end
+ end
+
+ if fn then
+ fns[#fns + 1] = function()
+ if options and options.open then
+ env.rednet.open("back")
+ env.os.queueEvent("x") env.os.pullEvent("x")
+ end
+ return fn(env.rednet, env)
+ end
+ end
+
+ if options and options.host then
+ env.rednet.host("some_protocol", "host_" .. id)
+ end
+
+ return parallel.waitForAny(table.unpack(fns))
+ end)
+ local modem = fake_computer.add_modem(computer, "back")
+ fake_computer.add_api(computer, "rom/apis/rednet.lua")
+ return computer, modem
+ end
+
+ it("opens and closes channels", function()
+ local id = math.random(256)
+ local computer = computer_with_rednet(id, function(rednet)
+ expect(rednet.isOpen()):eq(false)
+
+ rednet.open("back")
+ rednet.open("front")
+
+ expect(rednet.isOpen()):eq(true)
+ expect(rednet.isOpen("back")):eq(true)
+ expect(rednet.isOpen("front")):eq(true)
+
+ rednet.close("back")
+ expect(rednet.isOpen("back")):eq(false)
+ expect(rednet.isOpen("front")):eq(true)
+ expect(rednet.isOpen()):eq(true)
+
+ rednet.close()
+
+ expect(rednet.isOpen("back")):eq(false)
+ expect(rednet.isOpen("front")):eq(false)
+ expect(rednet.isOpen()):eq(false)
+ end)
+ fake_computer.add_modem(computer, "front")
+
+ fake_computer.run_all { computer }
+ end)
+
+ it("sends and receives rednet messages", function()
+ local computer_1, modem_1 = computer_with_rednet(1, function(rednet)
+ rednet.send(2, "Hello")
+ end, { open = true })
+ local computer_2, modem_2 = computer_with_rednet(2, function(rednet)
+ local id, message = rednet.receive()
+ expect(id):eq(1)
+ expect(message):eq("Hello")
+ end, { open = true })
+ fake_computer.add_modem_edge(modem_1, modem_2)
+
+ fake_computer.run_all { computer_1, computer_2 }
+ end)
+
+ it("repeats messages between computers", function()
+ local computer_1, modem_1 = computer_with_rednet(1, function(rednet)
+ rednet.send(3, "Hello")
+ end, { open = true })
+ local computer_2, modem_2 = computer_with_rednet(2, nil, { open = true, rep = true })
+ local computer_3, modem_3 = computer_with_rednet(3, function(rednet)
+ local id, message = rednet.receive()
+ expect(id):eq(1)
+ expect(message):eq("Hello")
+ end, { open = true })
+ fake_computer.add_modem_edge(modem_1, modem_2)
+ fake_computer.add_modem_edge(modem_2, modem_3)
+
+ fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 })
+ end)
+
+ it("repeats messages between computers with massive ids", function()
+ local id_1, id_3 = 24283947, 93428798
+ local computer_1, modem_1 = computer_with_rednet(id_1, function(rednet)
+ rednet.send(id_3, "Hello")
+ local id, message = rednet.receive()
+ expect { id, message }:same { id_3, "World" }
+ end, { open = true })
+ local computer_2, modem_2 = computer_with_rednet(2, nil, { open = true, rep = true })
+ local computer_3, modem_3 = computer_with_rednet(id_3, function(rednet)
+ rednet.send(id_1, "World")
+ local id, message = rednet.receive()
+ expect { id, message }:same { id_1, "Hello" }
+ end, { open = true })
+ fake_computer.add_modem_edge(modem_1, modem_2)
+ fake_computer.add_modem_edge(modem_2, modem_3)
+
+ fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 })
+ end)
+
+ it("ignores duplicate messages", function()
+ local computer_1, modem_1 = computer_with_rednet(1, function(rednet)
+ rednet.send(2, "Hello")
+ end, { open = true })
+ local computer_2, modem_2 = computer_with_rednet(2, function(rednet, env)
+ local id, message = rednet.receive()
+ expect { id, message }:same { 1, "Hello" }
+
+ local id = rednet.receive(nil, 1)
+ expect(id):eq(nil)
+
+ env.sleep(10)
+
+ -- Ensure our pending message store is empty. Bit ugly to prod internals, but there's no other way.
+ expect(debugx.getupvalue(rednet.run, "received_messages")):same({})
+ expect(debugx.getupvalue(rednet.run, "prune_received_timer")):eq(nil)
+ end, { open = true })
+
+ local computer_3, modem_3 = computer_with_rednet(3, nil, { open = true, rep = true })
+ fake_computer.add_modem_edge(modem_1, modem_3)
+ fake_computer.add_modem_edge(modem_3, modem_2)
+
+ local computer_4, modem_4 = computer_with_rednet(4, nil, { open = true, rep = true })
+ fake_computer.add_modem_edge(modem_1, modem_4)
+ fake_computer.add_modem_edge(modem_4, modem_2)
+
+ local computers = { computer_1, computer_2, computer_3, computer_4 }
+ fake_computer.run_all(computers, false)
+ fake_computer.advance_all(computers, 1)
+ fake_computer.run_all(computers, { computer_1 })
+ fake_computer.advance_all(computers, 10)
+ fake_computer.run_all(computers, { computer_1, computer_2 })
+ end)
+
+ it("handles lookups between computers with massive IDs", function()
+ local id_1, id_3 = 24283947, 93428798
+ local computer_1, modem_1 = computer_with_rednet(id_1, function(rednet)
+ local ids = { rednet.lookup("some_protocol") }
+ expect(ids):same { id_3 }
+ end, { open = true })
+ local computer_2, modem_2 = computer_with_rednet(2, nil, { open = true, rep = true })
+ local computer_3, modem_3 = computer_with_rednet(id_3, dawdle, { open = true, host = true })
+ fake_computer.add_modem_edge(modem_1, modem_2)
+ fake_computer.add_modem_edge(modem_2, modem_3)
+
+ local computers = { computer_1, computer_2, computer_3 }
+ fake_computer.run_all(computers, false)
+ fake_computer.advance_all(computers, 3)
+ fake_computer.run_all(computers, { computer_1 })
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/redstone_spec.lua b/src/test/resources/test-rom/spec/apis/redstone_spec.lua
new file mode 100644
index 000000000..148d08700
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/redstone_spec.lua
@@ -0,0 +1,92 @@
+local function it_side(func, ...)
+ local arg = table.pack(...)
+ it("requires a specific side", function()
+ expect.error(func, 0):eq("bad argument #1 (string expected, got number)")
+ expect.error(func, "blah", table.unpack(arg)):eq("bad argument #1 (unknown option blah)")
+
+ func("top", table.unpack(arg))
+ func("Top", table.unpack(arg))
+ func("toP", table.unpack(arg))
+ end)
+end
+
+describe("The redstone library", function()
+ describe("redstone.setOutput", function()
+ it_side(redstone.setOutput, false)
+
+ it("sets the output strength correctly", function()
+ redstone.setOutput("top", false)
+ expect(redstone.getAnalogueOutput("top")):eq(0)
+
+ redstone.setOutput("top", true)
+ expect(redstone.getAnalogueOutput("top")):eq(15)
+ end)
+ end)
+
+ describe("redstone.getOutput", function()
+ it_side(redstone.getOutput)
+
+ it("gets the output strength correctly", function()
+ redstone.setAnalogueOutput("top", 0)
+ expect(redstone.getOutput("top")):eq(false)
+
+ redstone.setAnalogueOutput("top", 1)
+ expect(redstone.getOutput("top")):eq(true)
+
+ redstone.setAnalogueOutput("top", 15)
+ expect(redstone.getOutput("top")):eq(true)
+ end)
+ end)
+
+ describe("redstone.getInput", function()
+ it_side(redstone.getInput)
+ end)
+
+ describe("redstone.setAnalogueOutput", function()
+ it_side(redstone.setAnalogueOutput, 0)
+
+ it("checks the strength parameter", function()
+ expect.error(redstone.setAnalogueOutput, "top", true):eq("bad argument #2 (number expected, got boolean)")
+ expect.error(redstone.setAnalogueOutput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)")
+ expect.error(redstone.setAnalogueOutput, "top", math.huge):eq("bad argument #2 (number expected, got inf)")
+ expect.error(redstone.setAnalogueOutput, "top", -1):eq("Expected number in range 0-15")
+ expect.error(redstone.setAnalogueOutput, "top", 16):eq("Expected number in range 0-15")
+ end)
+ end)
+
+ describe("redstone.getAnalogueOutput", function()
+ it_side(redstone.getAnalogueOutput)
+ end)
+
+ describe("redstone.getAnalogueInput", function()
+ it_side(redstone.getAnalogueInput)
+ end)
+
+ describe("redstone.setBundledOutput", function()
+ it_side(redstone.setBundledOutput, 0)
+
+ it("checks the mask parameter", function()
+ expect.error(redstone.setBundledOutput, "top", true):eq("bad argument #2 (number expected, got boolean)")
+ expect.error(redstone.setBundledOutput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)")
+ expect.error(redstone.setBundledOutput, "top", math.huge):eq("bad argument #2 (number expected, got inf)")
+ end)
+ end)
+
+ describe("redstone.getBundledOutput", function()
+ it_side(redstone.getBundledOutput)
+ end)
+
+ describe("redstone.getBundledInput", function()
+ it_side(redstone.getBundledInput)
+ end)
+
+ describe("redstone.testBundledInput", function()
+ it_side(redstone.testBundledInput, 0)
+
+ it("checks the mask parameter", function()
+ expect.error(redstone.testBundledInput, "top", true):eq("bad argument #2 (number expected, got boolean)")
+ expect.error(redstone.testBundledInput, "top", 0 / 0):eq("bad argument #2 (number expected, got nan)")
+ expect.error(redstone.testBundledInput, "top", math.huge):eq("bad argument #2 (number expected, got inf)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/settings_spec.lua b/src/test/resources/test-rom/spec/apis/settings_spec.lua
new file mode 100644
index 000000000..1551a822a
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/settings_spec.lua
@@ -0,0 +1,213 @@
+describe("The settings library", function()
+ describe("settings.define", function()
+ it("ensures valid type names", function()
+ expect.error(settings.define, "test.defined", { type = "function" })
+ :eq('Unknown type "function". Expected one of number, string, boolean, table.')
+ end)
+ end)
+ describe("settings.undefine", function()
+ it("clears defined settings", function()
+ settings.define("test.unset", { default = 123 })
+ settings.undefine("test.unset")
+ expect(settings.get("test.unset")):eq(nil)
+ end)
+ end)
+
+ describe("settings.set", function()
+ it("validates arguments", function()
+ settings.set("test", 1)
+ settings.set("test", "")
+ settings.set("test", {})
+ settings.set("test", false)
+
+ expect.error(settings.set, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(settings.set, "", nil):eq("bad argument #2 (expected number, string, boolean or table, got nil)")
+ end)
+
+ it("prevents storing unserialisable types", function()
+ expect.error(settings.set, "", { print }):eq("Cannot serialize type function")
+ end)
+
+ it("setting changes the value", function()
+ local random = math.random(1, 0x7FFFFFFF)
+ settings.set("test", random)
+ expect(settings.get("test")):eq(random)
+ end)
+
+ it("checks the type of the value", function()
+ settings.define("test.defined", { default = 123, description = "A description", type = "number" })
+ expect.error(settings.set, "test.defined", "hello")
+ :eq("bad argument #2 (expected number, got string)")
+ settings.set("test.defined", 123)
+ end)
+
+ it("setting fires an event", function()
+ settings.clear()
+
+ local s = stub(os, "queueEvent")
+ settings.set("test", 1)
+ settings.set("test", 2)
+
+ expect(s):called_with("setting_changed", "test", 1, nil)
+ expect(s):called_with("setting_changed", "test", 2, 1)
+ end)
+ end)
+
+ describe("settings.get", function()
+ it("validates arguments", function()
+ settings.get("test")
+ expect.error(settings.get, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("returns the default", function()
+ expect(settings.get("test.undefined")):eq(nil)
+ expect(settings.get("test.undefined", "?")):eq("?")
+
+ settings.define("test.unset", { default = "default" })
+ expect(settings.get("test.unset")):eq("default")
+ expect(settings.get("test.unset", "?")):eq("?")
+ end)
+ end)
+
+ describe("settings.getDetails", function()
+ it("validates arguments", function()
+ expect.error(settings.getDetails, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("works on undefined and unset values", function()
+ expect(settings.getDetails("test.undefined")):same { value = nil, changed = false }
+ end)
+
+ it("works on undefined but set values", function()
+ settings.set("test", 456)
+ expect(settings.getDetails("test")):same { value = 456, changed = true }
+ end)
+
+ it("works on defined but unset values", function()
+ settings.define("test.unset", { default = 123, description = "A description" })
+ expect(settings.getDetails("test.unset")):same
+ { default = 123, value = 123, changed = false, description = "A description" }
+ end)
+
+ it("works on defined and set values", function()
+ settings.define("test.defined", { default = 123, description = "A description", type = "number" })
+ settings.set("test.defined", 456)
+ expect(settings.getDetails("test.defined")):same
+ { default = 123, value = 456, changed = true, description = "A description", type = "number" }
+ end)
+ end)
+
+ describe("settings.unset", function()
+ it("validates arguments", function()
+ settings.unset("test")
+ expect.error(settings.unset, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("unsetting resets the value", function()
+ settings.set("test", true)
+ settings.unset("test")
+ expect(settings.get("test")):eq(nil)
+ end)
+
+ it("unsetting does not touch defaults", function()
+ settings.define("test.defined", { default = 123 })
+ settings.set("test.defined", 456)
+ settings.unset("test.defined")
+ expect(settings.get("test.defined")):eq(123)
+ end)
+
+ it("unsetting fires an event", function()
+ settings.set("test", 1)
+
+ local s = stub(os, "queueEvent")
+ settings.unset("test")
+ expect(s):called_with("setting_changed", "test", nil, 1)
+ end)
+ end)
+
+ describe("settings.clear", function()
+ it("clearing resets all values", function()
+ settings.set("test", true)
+ settings.clear()
+ expect(settings.get("test")):eq(nil)
+ end)
+
+ it("clearing does not touch defaults", function()
+ settings.define("test.defined", { default = 123 })
+ settings.set("test.defined", 456)
+ settings.clear()
+ expect(settings.get("test.defined")):eq(123)
+ end)
+
+ it("clearing fires an event", function()
+ settings.set("test", 1)
+
+ local s = stub(os, "queueEvent")
+ settings.clear()
+ expect(s):called_with("setting_changed", "test", nil, 1)
+ end)
+ end)
+
+ describe("settings.load", function()
+ it("validates arguments", function()
+ expect.error(settings.load, 1):eq("bad argument #1 (expected string, got number)")
+ end)
+
+ local function setup_with(contents)
+ settings.clear()
+ local h = fs.open("/test-files/.settings", "w")
+ h.write(contents)
+ h.close()
+
+ return settings.load("/test-files/.settings")
+ end
+
+ local function setup(contents)
+ return setup_with(textutils.serialize(contents))
+ end
+
+ it("defaults to .settings", function()
+ local s = stub(fs, "open")
+ settings.load()
+ expect(s):called_with(".settings", "r")
+ end)
+
+ it("loads undefined settings", function()
+ expect(setup { ["test"] = 1 }):eq(true)
+ expect(settings.get("test")):eq(1)
+ end)
+
+ it("loads defined settings", function()
+ settings.define("test.defined", { type = "number" })
+ expect(setup { ["test.defined"] = 1 }):eq(true)
+ expect(settings.get("test.defined")):eq(1)
+ end)
+
+ it("skips defined settings with incorrect types", function()
+ settings.define("test.defined", { type = "number" })
+ expect(setup { ["test.defined"] = "abc" }):eq(true)
+ expect(settings.get("test.defined")):eq(nil)
+ end)
+
+ it("skips unserializable values", function()
+ expect(setup_with "{ test = function() end }"):eq(true)
+ expect(settings.get("test")):eq(nil)
+ end)
+
+ it("skips non-table files", function()
+ expect(setup "not a table"):eq(false)
+ end)
+ end)
+
+ describe("settings.save", function()
+ it("validates arguments", function()
+ expect.error(settings.save, 1):eq("bad argument #1 (expected string, got number)")
+ end)
+
+ it("defaults to .settings", function()
+ local s = stub(fs, "open")
+ settings.save()
+ expect(s):called_with(".settings", "w")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/term_spec.lua b/src/test/resources/test-rom/spec/apis/term_spec.lua
new file mode 100644
index 000000000..769fcb867
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/term_spec.lua
@@ -0,0 +1,12 @@
+describe("The term library", function()
+ describe("term.redirect", function()
+ it("validates arguments", function()
+ expect.error(term.redirect, nil):eq("bad argument #1 (expected table, got nil)")
+ end)
+
+ it("prevents redirecting to term", function()
+ expect.error(term.redirect, term)
+ :eq("term is not a recommended redirect target, try term.current() instead")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/textutils_spec.lua b/src/test/resources/test-rom/spec/apis/textutils_spec.lua
new file mode 100644
index 000000000..71f426366
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/textutils_spec.lua
@@ -0,0 +1,252 @@
+local helpers = require "test_helpers"
+
+describe("The textutils library", function()
+ describe("textutils.slowWrite", function()
+ it("validates arguments", function()
+ expect.error(textutils.slowWrite, nil, false):eq("bad argument #2 (expected number, got boolean)")
+ end)
+
+ it("wraps text correctly", function()
+ local count = 0
+ stub(_G, "sleep", function() count = count + 1 end)
+ local w = helpers.with_window(20, 3, function()
+ textutils.slowWrite("This is a long string which one would hope wraps.")
+ end)
+
+ expect(w.getLine(1)):eq "This is a long "
+ expect(w.getLine(2)):eq "string which one "
+ expect(w.getLine(3)):eq "would hope wraps. "
+ expect(count):eq(51)
+ end)
+ end)
+
+ describe("textutils.formatTime", function()
+ it("validates arguments", function()
+ textutils.formatTime(0)
+ textutils.formatTime(0, false)
+ expect.error(textutils.formatTime, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(textutils.formatTime, 1, 1):eq("bad argument #2 (expected boolean, got number)")
+ end)
+
+ it("correctly formats 12 o'clock", function()
+ expect(textutils.formatTime(0, false)):eq("12:00 AM")
+ expect(textutils.formatTime(0.1, false)):eq("12:06 AM")
+
+ expect(textutils.formatTime(0, true)):eq("0:00")
+ expect(textutils.formatTime(0.1, true)):eq("0:06")
+ end)
+ end)
+
+ describe("textutils.pagedPrint", function()
+ it("validates arguments", function()
+ expect.error(textutils.pagedPrint, nil, false):eq("bad argument #2 (expected number, got boolean)")
+ end)
+ end)
+
+ describe("textutils.tabulate", function()
+ it("validates arguments", function()
+ term.redirect(window.create(term.current(), 1, 1, 5, 5, false))
+
+ textutils.tabulate()
+ textutils.tabulate({ "test", 1 })
+ textutils.tabulate(colors.white)
+
+ expect.error(textutils.tabulate, nil):eq("bad argument #1 (expected number or table, got nil)")
+ expect.error(textutils.tabulate, { "test" }, nil):eq("bad argument #2 (expected number or table, got nil)")
+ expect.error(textutils.tabulate, { false }):eq("bad argument #1.1 (expected string, got boolean)")
+ end)
+ end)
+
+ describe("textutils.pagedTabulate", function()
+ it("validates arguments", function()
+ term.redirect(window.create(term.current(), 1, 1, 5, 5, false))
+
+ textutils.pagedTabulate()
+ textutils.pagedTabulate({ "test" })
+ textutils.pagedTabulate(colors.white)
+
+ expect.error(textutils.pagedTabulate, nil):eq("bad argument #1 (expected number or table, got nil)")
+ expect.error(textutils.pagedTabulate, { "test" }, nil):eq("bad argument #2 (expected number or table, got nil)")
+ end)
+ end)
+
+ describe("textutils.empty_json_array", function()
+ it("is immutable", function()
+ expect.error(function() textutils.empty_json_array[1] = true end)
+ :str_match("^[^:]+:%d+: attempt to mutate textutils.empty_json_array$")
+ end)
+ end)
+
+ describe("textutils.serialise", function()
+ it("serialises basic tables", function()
+ expect(textutils.serialise({ 1, 2, 3, a = 1, b = {} }))
+ :eq("{\n 1,\n 2,\n 3,\n a = 1,\n b = {},\n}")
+
+ expect(textutils.serialise({ 0 / 0, 1 / 0, -1 / 0 }))
+ :eq("{\n 0/0,\n 1/0,\n -1/0,\n}")
+ end)
+
+ it("fails on recursive/repeated tables", function()
+ local rep = {}
+ expect.error(textutils.serialise, { rep, rep }):eq("Cannot serialize table with repeated entries")
+
+ local rep2 = { 1 }
+ expect.error(textutils.serialise, { rep2, rep2 }):eq("Cannot serialize table with repeated entries")
+
+ local recurse = {}
+ recurse[1] = recurse
+ expect.error(textutils.serialise, recurse):eq("Cannot serialize table with recursive entries")
+ end)
+
+ it("can allow repeated tables", function()
+ local rep = {}
+ expect(textutils.serialise({ rep, rep }, { allow_repetitions = true })):eq("{\n {},\n {},\n}")
+
+ local rep2 = { 1 }
+ expect(textutils.serialise({ rep2, rep2 }, { allow_repetitions = true })):eq("{\n {\n 1,\n },\n {\n 1,\n },\n}")
+
+ local recurse = {}
+ recurse[1] = recurse
+ expect.error(textutils.serialise, recurse, { allow_repetitions = true }):eq("Cannot serialize table with recursive entries")
+ end)
+
+ it("can emit in a compact form", function()
+ expect(textutils.serialise({ 1, 2, 3, a = 1, [false] = {} }, { compact = true }))
+ :eq("{1,2,3,a=1,[false]={},}")
+ end)
+ end)
+
+ describe("textutils.unserialise", function()
+ it("validates arguments", function()
+ textutils.unserialise("")
+ expect.error(textutils.unserialise, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("textutils.serialiseJSON", function()
+ it("validates arguments", function()
+ textutils.serialiseJSON("")
+ textutils.serialiseJSON(1)
+ textutils.serialiseJSON({})
+ textutils.serialiseJSON(false)
+ textutils.serialiseJSON("", true)
+ expect.error(textutils.serialiseJSON, nil):eq("bad argument #1 (expected table, string, number or boolean, got nil)")
+ expect.error(textutils.serialiseJSON, "", 1):eq("bad argument #2 (expected boolean, got number)")
+ end)
+
+ it("serializes empty arrays", function()
+ expect(textutils.serializeJSON(textutils.empty_json_array)):eq("[]")
+ end)
+
+ it("serializes null", function()
+ expect(textutils.serializeJSON(textutils.json_null)):eq("null")
+ end)
+
+ it("serializes strings", function()
+ expect(textutils.serializeJSON('a')):eq('"a"')
+ expect(textutils.serializeJSON('"')):eq('"\\""')
+ expect(textutils.serializeJSON('\\')):eq('"\\\\"')
+ expect(textutils.serializeJSON('/')):eq('"/"')
+ expect(textutils.serializeJSON('\b')):eq('"\\b"')
+ expect(textutils.serializeJSON('\n')):eq('"\\n"')
+ expect(textutils.serializeJSON(string.char(0))):eq('"\\u0000"')
+ expect(textutils.serializeJSON(string.char(0x0A))):eq('"\\n"')
+ expect(textutils.serializeJSON(string.char(0x1D))):eq('"\\u001D"')
+ expect(textutils.serializeJSON(string.char(0x81))):eq('"\\u0081"')
+ expect(textutils.serializeJSON(string.char(0xFF))):eq('"\\u00FF"')
+ end)
+
+ it("serializes arrays until the last index with content", function()
+ expect(textutils.serializeJSON({ 5, "test", nil, nil, 7 })):eq('[5,"test",null,null,7]')
+ expect(textutils.serializeJSON({ 5, "test", nil, nil, textutils.json_null })):eq('[5,"test",null,null,null]')
+ expect(textutils.serializeJSON({ nil, nil, nil, nil, "text" })):eq('[null,null,null,null,"text"]')
+ end)
+ end)
+
+ describe("textutils.unserializeJSON", function()
+ describe("parses", function()
+ it("a list of primitives", function()
+ expect(textutils.unserializeJSON('[1, true, false, "hello"]')):same { 1, true, false, "hello" }
+ end)
+
+ it("null when parse_null is true", function()
+ expect(textutils.unserializeJSON("null", { parse_null = true })):eq(textutils.json_null)
+ end)
+
+ it("null when parse_null is false", function()
+ expect(textutils.unserializeJSON("null", { parse_null = false })):eq(nil)
+ end)
+
+ it("an empty array", function()
+ expect(textutils.unserializeJSON("[]", { parse_null = false })):eq(textutils.empty_json_array)
+ end)
+
+ it("basic objects", function()
+ expect(textutils.unserializeJSON([[{ "a": 1, "b":2 }]])):same { a = 1, b = 2 }
+ end)
+ end)
+
+ describe("parses using NBT-style syntax", function()
+ local function exp(x)
+ local res, err = textutils.unserializeJSON(x, { nbt_style = true })
+ if not res then error(err, 2) end
+ return expect(res)
+ end
+ it("basic objects", function()
+ exp([[{ a: 1, b:2 }]]):same { a = 1, b = 2 }
+ end)
+
+ it("suffixed numbers", function()
+ exp("1b"):eq(1)
+ exp("1.1d"):eq(1.1)
+ end)
+
+ it("strings", function()
+ exp("'123'"):eq("123")
+ exp("\"123\""):eq("123")
+ end)
+
+ it("typed arrays", function()
+ exp("[B; 1, 2, 3]"):same { 1, 2, 3 }
+ exp("[B;]"):same {}
+ end)
+ end)
+
+ describe("passes nst/JSONTestSuite", function()
+ local search_path = "test-rom/data/json-parsing"
+ local skip = dofile(search_path .. "/skip.lua")
+ for _, file in pairs(fs.find(search_path .. "/*.json")) do
+ local name = fs.getName(file):sub(1, -6);
+ (skip[name] and pending or it)(name, function()
+ local h = io.open(file, "r")
+ local contents = h:read("*a")
+ h:close()
+
+ local res, err = textutils.unserializeJSON(contents)
+ local kind = fs.getName(file):sub(1, 1)
+ if kind == "n" then
+ expect(res):eq(nil)
+ elseif kind == "y" then
+ if err ~= nil then fail("Expected test to pass, but failed with " .. err) end
+ end
+ end)
+ end
+ end)
+ end)
+
+ describe("textutils.urlEncode", function()
+ it("validates arguments", function()
+ textutils.urlEncode("")
+ expect.error(textutils.urlEncode, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("textutils.complete", function()
+ it("validates arguments", function()
+ textutils.complete("pri")
+ textutils.complete("pri", _G)
+ expect.error(textutils.complete, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(textutils.complete, "", false):eq("bad argument #2 (expected table, got boolean)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/apis/window_spec.lua b/src/test/resources/test-rom/spec/apis/window_spec.lua
new file mode 100644
index 000000000..6b5e7fdf7
--- /dev/null
+++ b/src/test/resources/test-rom/spec/apis/window_spec.lua
@@ -0,0 +1,173 @@
+describe("The window library", function()
+ local function mk()
+ return window.create(term.current(), 1, 1, 5, 5, false)
+ end
+
+ describe("window.create", function()
+ it("validates arguments", function()
+ local r = mk()
+ window.create(r, 1, 1, 5, 5)
+ window.create(r, 1, 1, 5, 5, false)
+
+ expect.error(window.create, nil):eq("bad argument #1 (expected table, got nil)")
+ expect.error(window.create, r, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(window.create, r, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ expect.error(window.create, r, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
+ expect.error(window.create, r, 1, 1, 1, nil):eq("bad argument #5 (expected number, got nil)")
+ expect.error(window.create, r, 1, 1, 1, 1, ""):eq("bad argument #6 (expected boolean, got string)")
+ end)
+ end)
+
+ describe("Window.blit", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.blit("a", "a", "a")
+
+ expect.error(w.blit, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(w.blit, "", nil):eq("bad argument #2 (expected string, got nil)")
+ expect.error(w.blit, "", "", nil):eq("bad argument #3 (expected string, got nil)")
+ expect.error(w.blit, "", "", "a"):eq("Arguments must be the same length")
+ end)
+ end)
+
+ describe("Window.setCursorPos", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.setCursorPos(1, 1)
+
+ expect.error(w.setCursorPos, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.setCursorPos, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ end)
+ end)
+
+ describe("Window.setCursorBlink", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.setCursorBlink(false)
+ expect.error(w.setCursorBlink, nil):eq("bad argument #1 (expected boolean, got nil)")
+ end)
+ end)
+
+ describe("Window.setTextColour", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.setTextColour(colors.white)
+
+ expect.error(w.setTextColour, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.setTextColour, -5):eq("Invalid color (got -5)")
+ end)
+ end)
+
+ describe("Window.setPaletteColour", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.setPaletteColour(colors.white, 0, 0, 0)
+ w.setPaletteColour(colors.white, 0x000000)
+
+ expect.error(w.setPaletteColour, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.setPaletteColour, -5):eq("Invalid color (got -5)")
+ expect.error(w.setPaletteColour, colors.white):eq("bad argument #2 (expected number, got nil)")
+ expect.error(w.setPaletteColour, colors.white, 1, false):eq("bad argument #3 (expected number, got boolean)")
+ expect.error(w.setPaletteColour, colors.white, 1, nil, 1):eq("bad argument #3 (expected number, got nil)")
+ expect.error(w.setPaletteColour, colors.white, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
+ end)
+ end)
+
+ describe("Window.getPaletteColour", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.getPaletteColour(colors.white)
+ expect.error(w.getPaletteColour, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.getPaletteColour, -5):eq("Invalid color (got -5)")
+ end)
+ end)
+
+ describe("Window.setBackgroundColour", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.setBackgroundColour(colors.white)
+
+ expect.error(w.setBackgroundColour, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.setBackgroundColour, -5):eq("Invalid color (got -5)")
+ end)
+ end)
+
+ describe("Window.scroll", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.scroll(0)
+ expect.error(w.scroll, nil):eq("bad argument #1 (expected number, got nil)")
+ end)
+ end)
+
+ describe("Window.setVisible", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.setVisible(false)
+ expect.error(w.setVisible, nil):eq("bad argument #1 (expected boolean, got nil)")
+ end)
+ end)
+
+ describe("Window.reposition", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.reposition(1, 1)
+ w.reposition(1, 1, 5, 5)
+ expect.error(w.reposition, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.reposition, 1, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(w.reposition, 1, 1, false, 1):eq("bad argument #3 (expected number, got boolean)")
+ expect.error(w.reposition, 1, 1, nil, 1):eq("bad argument #3 (expected number, got nil)")
+ expect.error(w.reposition, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
+ expect.error(w.reposition, 1, 1, 1, 1, true):eq("bad argument #5 (expected table, got boolean)")
+ end)
+
+ it("can change the buffer", function()
+ local a, b = mk(), mk()
+ local target = window.create(a, 1, 1, a.getSize())
+
+ target.write("Test")
+ expect((a.getLine(1))):equal("Test ")
+ expect({ a.getCursorPos() }):same { 5, 1 }
+
+ target.reposition(1, 1, nil, nil, b)
+
+ target.redraw()
+ expect((a.getLine(1))):equal("Test ")
+ expect({ a.getCursorPos() }):same { 5, 1 }
+
+ target.setCursorPos(1, 1) target.write("More")
+ expect((a.getLine(1))):equal("Test ")
+ expect((b.getLine(1))):equal("More ")
+ end)
+ end)
+
+ describe("Window.getLine", function()
+ it("validates arguments", function()
+ local w = mk()
+ w.getLine(1)
+ local _, y = w.getSize()
+ expect.error(w.getLine, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(w.getLine, 0):eq("Line is out of range.")
+ expect.error(w.getLine, y + 1):eq("Line is out of range.")
+ end)
+
+ it("provides a line's contents", function()
+ local w = mk()
+ w.blit("test", "aaaa", "4444")
+ expect({ w.getLine(1) }):same { "test ", "aaaa0", "4444f" }
+ end)
+ end)
+ describe("Window.setVisible", function()
+ it("validates arguments", function()
+ local w = mk()
+ expect.error(w.setVisible, nil):eq("bad argument #1 (expected boolean, got nil)")
+ end)
+ end)
+ describe("Window.isVisible", function()
+ it("gets window visibility", function()
+ local w = mk()
+ w.setVisible(false)
+ expect(w.isVisible()):same(false)
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/base_spec.lua b/src/test/resources/test-rom/spec/base_spec.lua
new file mode 100644
index 000000000..db7b69033
--- /dev/null
+++ b/src/test/resources/test-rom/spec/base_spec.lua
@@ -0,0 +1,110 @@
+local with_window = require "test_helpers".with_window
+
+describe("The Lua base library", function()
+ describe("sleep", function()
+ it("validates arguments", function()
+ sleep(0)
+ sleep(nil)
+
+ expect.error(sleep, false):eq("bad argument #1 (expected number, got boolean)")
+ end)
+ end)
+
+ describe("write", function()
+ it("validates arguments", function()
+ write("")
+ expect.error(write, nil):eq("bad argument #1 (expected string or number, got nil)")
+ end)
+
+ it("writes numbers", function()
+ local w = with_window(5, 5, function() write(123) end)
+ expect(w.getLine(1)):eq("123 ")
+ end)
+
+ it("writes strings", function()
+ local w = with_window(5, 5, function() write("abc") end)
+ expect(w.getLine(1)):eq("abc ")
+ end)
+ end)
+
+ describe("loadfile", function()
+ local loadfile = _G.native_loadfile or loadfile
+
+ local function make_file()
+ local tmp = fs.open("test-files/out.lua", "w")
+ tmp.write("return _ENV")
+ tmp.close()
+ end
+
+ it("validates arguments", function()
+ loadfile("")
+ loadfile("", "")
+ loadfile("", "", {})
+
+ expect.error(loadfile, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(loadfile, "", false):eq("bad argument #2 (expected string, got boolean)")
+ expect.error(loadfile, "", "", false):eq("bad argument #3 (expected table, got boolean)")
+ end)
+
+ it("prefixes the filename with @", function()
+ local info = debug.getinfo(loadfile("/rom/startup.lua"), "S")
+ expect(info):matches { short_src = "startup.lua", source = "@startup.lua" }
+ end)
+
+ it("loads a file with the global environment", function()
+ make_file()
+ expect(loadfile("test-files/out.lua")()):eq(_G)
+ end)
+
+ it("loads a file with a specific environment", function()
+ make_file()
+ local env = {}
+ expect(loadfile("test-files/out.lua", nil, env)()):eq(env)
+ end)
+
+ it("supports the old-style argument form", function()
+ make_file()
+ local env = {}
+ expect(loadfile("test-files/out.lua", env)()):eq(env)
+ end)
+ end)
+
+ describe("dofile", function()
+ it("validates arguments", function()
+ expect.error(dofile, ""):eq("File not found")
+ expect.error(dofile, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+ end)
+
+ describe("load", function()
+ it("validates arguments", function()
+ load("")
+ load(function()
+ end)
+ load("", "")
+ load("", "", "")
+ load("", "", "", _ENV)
+
+ expect.error(load, nil):eq("bad argument #1 (expected function or string, got nil)")
+ expect.error(load, "", false):eq("bad argument #2 (expected string, got boolean)")
+ expect.error(load, "", "", false):eq("bad argument #3 (expected string, got boolean)")
+ expect.error(load, "", "", "", false):eq("bad argument #4 (expected table, got boolean)")
+ end)
+
+ local function generator(parts)
+ return coroutine.wrap(function()
+ for i = 1, #parts do
+ coroutine.yield(parts[i])
+ end
+ end)
+ end
+
+ it("does not prefix the chunk name with '='", function()
+ local info = debug.getinfo(load("return 1", "name"), "S")
+ expect(info):matches { short_src = "[string \"name\"]", source = "name" }
+
+ info = debug.getinfo(load(generator { "return 1" }, "name"), "S")
+ expect(info):matches { short_src = "[string \"name\"]", source = "name" }
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/lua/README.md b/src/test/resources/test-rom/spec/lua/README.md
new file mode 100644
index 000000000..9bd6c61a0
--- /dev/null
+++ b/src/test/resources/test-rom/spec/lua/README.md
@@ -0,0 +1,19 @@
+# Lua VM tests
+
+Unlike the rest of the test suites, the code in this folder doesn't test any
+(well, much) CC code. Instead, it ensures that the Lua VM behaves in a way that
+we expect.
+
+The VM that CC uses (LuaJ and later Cobalt) does not really conform to any one
+version of Lua, instead supporting a myriad of features from Lua 5.1 to 5.3 (and
+not always accurately). These tests attempt to pin down what behaviour is
+required for a well behaved emulator.
+
+
+These tests are split into several folders:
+ - `/` Tests for CC specific functionality, based on Cobalt/Lua quirks or needs
+ of CC.
+ - `puc`: Tests derived from the [PUC Lua test suite][puc-tests].
+
+
+[puc-tests]: https://www.lua.org/tests/ "Lua: test suites"
diff --git a/src/test/resources/test-rom/spec/lua/coroutine_spec.lua b/src/test/resources/test-rom/spec/lua/coroutine_spec.lua
new file mode 100644
index 000000000..2bf29e260
--- /dev/null
+++ b/src/test/resources/test-rom/spec/lua/coroutine_spec.lua
@@ -0,0 +1,330 @@
+describe("Coroutines", function()
+ local function assert_resume(ok, ...)
+ if ok then return table.pack(...) end
+ error(..., 0)
+ end
+
+ --- Run a function in a coroutine, "echoing" the yielded value back as the resumption value.
+ local function coroutine_echo(f)
+ local co = coroutine.create(f)
+ local result = { n = 0 }
+ while coroutine.status(co) ~= "dead" do
+ result = assert_resume(coroutine.resume(co, table.unpack(result, 1, result.n)))
+ end
+
+ return table.unpack(result, 1, result.n)
+ end
+
+ describe("allow yielding", function()
+ --[[
+ Tests for some non-standard yield locations. I'm not saying that users
+ /should/ use this, but it's useful for us to allow it in order to suspend the
+ VM in arbitrary locations.
+
+ Cobalt does support this within load too, but that's unlikely to be supported
+ in the future.
+
+ These tests were split over about 7 files in Cobalt and are in one massive one
+ in this test suite. Sorry.
+ ]]
+
+ it("within debug hooks", function()
+ coroutine_echo(function()
+ local counts = { call = 0, ['return'] = 0, count = 0, line = 0 }
+
+ debug.sethook(function(kind)
+ counts[kind] = (counts[kind] or 0) + 1
+ expect(coroutine.yield(kind)):eq(kind)
+ end, "crl", 1)
+
+ expect(string.gsub("xyz", "x", "z")):eq("zyz")
+ expect(pcall(function()
+ local x = 0
+ for i = 1, 5 do x = x + i end
+ end)):eq(true)
+
+ debug.sethook(nil)
+
+ -- These numbers are going to vary beyond the different VMs a
+ -- little. As long as they're non-0, it's all fine.
+ expect(counts.call):ne(0)
+ expect(counts['return']):ne(0)
+ expect(counts.count):ne(0)
+ expect(counts.line):ne(0)
+ end)
+ end)
+
+ it("within string.gsub", function()
+ local result, count = coroutine_echo(function()
+ return ("hello world"):gsub("%w", function(entry)
+ local x = coroutine.yield(entry)
+ return x:upper()
+ end)
+ end)
+
+ expect(result):eq("HELLO WORLD")
+ expect(count):eq(10)
+ end)
+
+ describe("within pcall", function()
+ it("with no error", function()
+ local ok, a, b, c = coroutine_echo(function()
+ return pcall(function()
+ local a, b, c = coroutine.yield(1, 2, 3)
+ return a, b, c
+ end)
+ end)
+
+ expect(ok):eq(true)
+ expect({ a, b, c }):same { 1, 2, 3 }
+ end)
+
+ it("with an error", function()
+ local ok, msg = coroutine_echo(function()
+ return pcall(function()
+ local a, b, c = coroutine.yield(1, 2, 3)
+ expect({ a, b, c }):same { 1, 2, 3 }
+ error("Error message", 0)
+ end)
+ end)
+
+ expect(ok):eq(false)
+ expect(msg):eq("Error message")
+ end)
+ end)
+
+ it("within table.foreach", function()
+ coroutine_echo(function()
+ local x = { 3, "foo", 4, 1 }
+ local idx = 1
+ table.foreach(x, function(key, val)
+ expect(key):eq(idx)
+ expect(val):eq(x[idx])
+ expect(coroutine.yield(val)):eq(val)
+
+ idx = idx + 1
+ end)
+ end)
+ end)
+
+ it("within table.foreachi", function()
+ coroutine_echo(function()
+ local x = { 3, "foo", 4, 1 }
+ local idx = 1
+ table.foreachi(x, function(key, val)
+ expect(key):eq(idx)
+ expect(val):eq(x[idx])
+ expect(coroutine.yield(val)):eq(val)
+
+ idx = idx + 1
+ end)
+ end)
+ end)
+
+ describe("within table.sort", function()
+ it("with a yielding comparator", function()
+ coroutine_echo(function()
+ local x = { 32, 2, 4, 13 }
+ table.sort(x, function(a, b)
+ local x, y = coroutine.yield(a, b)
+ expect(x):eq(a)
+ expect(y):eq(b)
+
+ return a < b
+ end)
+
+ expect(x[1]):eq(2)
+ expect(x[2]):eq(4)
+ expect(x[3]):eq(13)
+ expect(x[4]):eq(32)
+ end)
+ end)
+
+ it("within a yielding metatable comparator", function()
+ local meta = {
+ __lt = function(a, b)
+ local x, y = coroutine.yield(a, b)
+ expect(x):eq(a)
+ expect(y):eq(b)
+
+ return a.x < b.x
+ end,
+ }
+
+ local function create(val) return setmetatable({ x = val }, meta) end
+
+ coroutine_echo(function()
+ local x = { create(32), create(2), create(4), create(13) }
+ table.sort(x)
+
+ expect(x[1].x):eq(2)
+ expect(x[2].x):eq(4)
+ expect(x[3].x):eq(13)
+ expect(x[4].x):eq(32)
+ end)
+ end)
+ end)
+
+ describe("within xpcall", function()
+ it("within the main function", function()
+ -- Ensure that yielding within a xpcall works as expected
+ coroutine_echo(function()
+ local ok, a, b, c = xpcall(function()
+ return coroutine.yield(1, 2, 3)
+ end, function(msg) return msg .. "!" end)
+
+ expect(true):eq(ok)
+ expect(1):eq(a)
+ expect(2):eq(b)
+ expect(3):eq(c)
+ end)
+ end)
+
+ it("within the main function (with an error)", function()
+ coroutine_echo(function()
+ local ok, msg = xpcall(function()
+ local a, b, c = coroutine.yield(1, 2, 3)
+ expect(1):eq(a)
+ expect(2):eq(b)
+ expect(3):eq(c)
+
+ error("Error message", 0)
+ end, function(msg) return msg .. "!" end)
+
+ expect(false):eq(ok)
+ expect("Error message!"):eq(msg)
+ end)
+ end)
+
+ it("with an error in the error handler", function()
+ coroutine_echo(function()
+ local ok, msg = xpcall(function()
+ local a, b, c = coroutine.yield(1, 2, 3)
+ expect(1):eq(a)
+ expect(2):eq(b)
+ expect(3):eq(c)
+
+ error("Error message")
+ end, function(msg) error(msg) end)
+
+ expect(false):eq(ok)
+ expect("error in error handling"):eq(msg)
+ end)
+ end)
+
+ it("within the error handler", function()
+ coroutine_echo(function()
+ local ok, msg = xpcall(function()
+ local a, b, c = coroutine.yield(1, 2, 3)
+ expect(1):eq(a)
+ expect(2):eq(b)
+ expect(3):eq(c)
+
+ error("Error message", 0)
+ end, function(msg)
+ return coroutine.yield(msg) .. "!"
+ end)
+
+ expect(false):eq(ok)
+ expect("Error message!"):eq(msg)
+ end)
+ end)
+
+ it("within the error handler with an error", function()
+ coroutine_echo(function()
+ local ok, msg = xpcall(function()
+ local a, b, c = coroutine.yield(1, 2, 3)
+ expect(1):eq(a)
+ expect(2):eq(b)
+ expect(3):eq(c)
+
+ error("Error message", 0)
+ end, function(msg)
+ coroutine.yield(msg)
+ error("nope")
+ end)
+
+ expect(false):eq(ok)
+ expect("error in error handling"):eq(msg)
+ end)
+ end)
+ end)
+
+ it("within metamethods", function()
+ local create, ops
+ create = function(val) return setmetatable({ x = val }, ops) end
+ ops = {
+ __add = function(x, y)
+ local a, b = coroutine.yield(x, y)
+ return create(a.x + b.x)
+ end,
+ __div = function(x, y)
+ local a, b = coroutine.yield(x, y)
+ return create(a.x / b.x)
+ end,
+ __concat = function(x, y)
+ local a, b = coroutine.yield(x, y)
+ return create(a.x .. b.x)
+ end,
+ __eq = function(x, y)
+ local a, b = coroutine.yield(x, y)
+ return a.x == b.x
+ end,
+ __lt = function(x, y)
+ local a, b = coroutine.yield(x, y)
+ return a.x < b.x
+ end,
+ __index = function(tbl, key)
+ local res = coroutine.yield(key)
+ return res:upper()
+ end,
+ __newindex = function(tbl, key, val)
+ local rKey, rVal = coroutine.yield(key, val)
+ rawset(tbl, rKey, rVal .. "!")
+ end,
+ }
+
+ local varA = create(2)
+ local varB = create(3)
+
+ coroutine_echo(function()
+ expect(5):eq((varA + varB).x)
+ expect(5):eq((varB + varA).x)
+ expect(4):eq((varA + varA).x)
+ expect(6):eq((varB + varB).x)
+
+ expect(2 / 3):eq((varA / varB).x)
+ expect(3 / 2):eq((varB / varA).x)
+ expect(1):eq((varA / varA).x)
+ expect(1):eq((varB / varB).x)
+
+ expect("23"):eq((varA .. varB).x)
+ expect("32"):eq((varB .. varA).x)
+ expect("22"):eq((varA .. varA).x)
+ expect("33"):eq((varB .. varB).x)
+ expect("33333"):eq((varB .. varB .. varB .. varB .. varB).x)
+
+ expect(false):eq(varA == varB)
+ expect(false):eq(varB == varA)
+ expect(true):eq(varA == varA)
+ expect(true):eq(varB == varB)
+
+ expect(true):eq(varA < varB)
+ expect(false):eq(varB < varA)
+ expect(false):eq(varA < varA)
+ expect(false):eq(varB < varB)
+
+ expect(true):eq(varA <= varB)
+ expect(false):eq(varB <= varA)
+ expect(true):eq(varA <= varA)
+ expect(true):eq(varB <= varB)
+
+ expect("HELLO"):eq(varA.hello)
+ varA.hello = "bar"
+ expect("bar!"):eq(varA.hello)
+ end)
+
+
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/lua/timeout_spec.lua b/src/test/resources/test-rom/spec/lua/timeout_spec.lua
new file mode 100644
index 000000000..159130d4c
--- /dev/null
+++ b/src/test/resources/test-rom/spec/lua/timeout_spec.lua
@@ -0,0 +1,17 @@
+describe("The VM terminates long running code :slow", function()
+ it("in loops", function()
+ expect.error(function() while true do end end)
+ :str_match("^.+:%d+: Too long without yielding$")
+ end)
+
+ describe("in string pattern matching", function()
+ local str, pat = ("a"):rep(1e4), ".-.-.-.-b$"
+
+ it("string.find", function()
+ expect.error(string.find, str, pat):eq("Too long without yielding")
+ end)
+ it("string.match", function()
+ expect.error(string.match, str, pat):eq("Too long without yielding")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/lua/vm_spec.lua b/src/test/resources/test-rom/spec/lua/vm_spec.lua
new file mode 100644
index 000000000..702306939
--- /dev/null
+++ b/src/test/resources/test-rom/spec/lua/vm_spec.lua
@@ -0,0 +1,9 @@
+describe("The VM", function()
+ it("allows unpacking a large number of values", function()
+ -- We don't allow arbitrarily many values, half a meg is probably fine.
+ -- I don't actually have any numbers on this - maybe we do need more?
+ local len = 2 ^ 19
+ local tbl = { (" "):rep(len):byte(1, -1) }
+ expect(#tbl):eq(len)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/audio/dfpwm_spec.lua b/src/test/resources/test-rom/spec/modules/cc/audio/dfpwm_spec.lua
new file mode 100644
index 000000000..dbc28ca01
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/audio/dfpwm_spec.lua
@@ -0,0 +1,26 @@
+describe("cc.audio.dfpwm", function()
+ local dfpwm = require "cc.audio.dfpwm"
+
+ describe("decode", function()
+ it("decodes some test data", function()
+ -- Look, I'm not proud of this.
+ local input = "\43\225\33\44\30\240\171\23\253\201\46\186\68\189\74\160\188\16\94\169\251\87\11\240\19\92\85\185\126\5\172\64\17\250\85\245\255\169\244\1\85\200\33\176\82\104\163\17\126\23\91\226\37\224\117\184\198\11\180\19\148\86\191\246\255\188\231\10\210\85\124\202\15\232\43\162\117\63\220\15\250\88\87\230\173\106\41\13\228\143\246\190\119\169\143\68\201\40\149\62\20\72\3\160\114\169\254\39\152\30\20\42\84\24\47\64\43\61\221\95\191\42\61\42\206\4\247\81"
+ local output = { 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, -1, -2, -2, -1, 0, 1, 0, -1, -3, -5, -5, -5, -7, -9, -11, -11, -9, -9, -9, -9, -10, -12, -12, -10, -8, -6, -6, -8, -10, -12, -14, -16, -18, -17, -15, -12, -9, -6, -3, -2, -2, -2, -2, -2, -2, 0, 3, 6, 7, 7, 7, 4, 1, 1, 1, 1, 3, 5, 7, 9, 12, 15, 15, 12, 12, 12, 9, 9, 11, 12, 12, 14, 16, 17, 17, 17, 14, 11, 11, 11, 10, 12, 14, 14, 13, 13, 10, 9, 9, 7, 5, 4, 4, 4, 4, 4, 6, 8, 10, 10, 10, 10, 10, 10, 10, 9, 8, 8, 8, 7, 6, 4, 2, 0, 0, 0, 0, 0, -1, -1, 0, 1, 3, 3, 3, 3, 2, 0, -2, -2, -2, -3, -5, -7, -7, -5, -3, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -1, -1, 0, 1, 1, 1, 2, 3, 4, 5, 6, 7, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 9, 8, 7, 6, 4, 2, 0, 0, 2, 4, 6, 8, 10, 10, 8, 7, 7, 5, 3, 1, -1, 0, 2, 4, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 5, 5, 5, 5, 5, 6, 7, 8, 9, 10, 9, 9, 9, 9, 9, 8, 7, 6, 5, 3, 1, 1, 3, 3, 3, 3, 3, 3, 2, 1, 0, -1, -3, -3, -3, -3, -2, -3, -4, -4, -3, -4, -5, -6, -6, -5, -5, -4, -3, -2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 20, 17, 16, 16, 15, 15, 15, 15, 13, 13, 13, 13, 14, 15, 16, 18, 18, 16, 14, 12, 10, 8, 5, 5, 5, 4, 4, 4, 4, 4, 4, 2, 0, -2, -2, -2, -4, -4, -2, 0, 0, -2, -4, -6, -6, -6, -8, -10, -12, -14, -16, -15, -13, -12, -11, -11, -11, -11, -13, -13, -13, -13, -13, -14, -16, -18, -18, -18, -18, -16, -16, -16, -14, -13, -14, -15, -15, -14, -14, -12, -11, -12, -13, -13, -12, -13, -14, -15, -15, -13, -11, -9, -7, -5, -5, -5, -3, -1, -1, -1, -1, -3, -5, -5, -3, -3, -3, -1, -1, -1, -1, -3, -3, -3, -4, -6, -6, -4, -2, 0, 0, 0, 0, -2, -2, -2, -3, -5, -7, -9, -11, -13, -13, -11, -9, -7, -6, -6, -6, -6, -4, -2, -2, -4, -6, -8, -7, -5, -3, -2, -2, -2, -2, 0, 0, -2, -4, -4, -2, 0, 2, 2, 1, 1, -1, -3, -5, -7, -10, -10, -10, -10, -8, -7, -7, -5, -3, -2, -4, -4, -4, -6, -8, -10, -12, -12, -12, -12, -12, -14, -13, -13, -13, -11, -11, -11, -11, -11, -11, -11, -9, -7, -5, -3, -1, -1, -1, -1, -1, 1, 1, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 22, 19, 18, 20, 22, 24, 23, 22, 24, 26, 28, 27, 24, 23, 25, 28, 28, 28, 27, 26, 26, 23, 20, 17, 14, 14, 14, 11, 11, 11, 11, 13, 15, 16, 16, 16, 15, 15, 14, 14, 12, 10, 9, 11, 13, 15, 17, 17, 14, 13, 13, 12, 12, 10, 9, 11, 13, 15, 17, 19, 19, 16, 13, 10, 7, 4, 1, 1, 2, 2, 4, 7, 10, 13, 13, 13, 12, 12, 12, 9, 6, 6, 6, 3, 0, 0, 0, 0, 2, 3, 3, 3, 3, 5, 7, 7, 7, 9, 11, 13, 15, 18, 18, 15, 12, 9, 8, 10, 13, 13, 13, 15, 18, 21, 24, 27, 27, 23, 19, 15, 11, 10, 9, 9, 12, 16, 19, 22, 23, 19, 14, 13, 16, 16, 15, 15, 14, 17, 20, 20, 19, 19, 18, 17, 14, 13, 15, 15, 12, 11, 13, 16, 19, 19, 18, 20, 20, 19, 18, 18, 17, 17, 16, 16, 16, 15, 17, 17, 16, 16, 13, 12, 12, 11, 11, 9, 9, 9, 9, 11, 11, 9, 7, 5, 3, 1, 1, 1, -1, -1, 1, 3, 5, 7, 9, 11, 12, 9, 6, 6, 6, 6, 8, 8, 7, 9, 11, 13, 13, 12, 14, 16, 18, 20, 20, 20, 22, 24, 26, 25, 25, 27, 29, 28, 27, 26, 23, 22, 22, 21, 21, 20, 22, 24, 26, 28, 27, 24, 21, 21, 21, 18, 17, 17, 14, 11, 11, 11, 10, 10, 7, 6, 6, 4, 3, 5, 5, 3, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 0, 0, 1, 2, 3, 4, 3, 1, -1, -3, -3, -3, -3, -2, -3, -4, -6, -8, -10, -10, -10, -12, -12, -12, -12, -10, -10, -11, -12, -14, -16, -18, -20, -22, -24, -26, -28, -27, -27, -26, -26, -25, -25, -27, -26, -24, -22, -22, -22, -22, -24, -24, -24, -24, -23, -23, -22, -22, -21, -20, -19, -17, -15, -13, -11, -9, -7, -7, -9, -9, -9, -11, -13, -15, -17, -16, -14, -13, -15, -14, -14, -14, -12, -10, -8, -7, -9, -11, -13, -15, -14, -14, -13, -13, -15, -17, -19, -18, -18, -17, -17, -16, -16, -18, -20, -22, -21, -21, -21, -21, -21, -20, -21, -22, -24, -24, -22, -22, -24, -26, -25, -23, -21, -19, -18, -17, -17, -19, -21, -23, -25, -27, -29, -31, -30, -29, -28, -26, -25, -24, -24, -23, -23, -25, -24, -24, -24, -22, -20, -18, -18, -20, -20, -20, -20, -18, -16, -16, -16, -14, -12, -10, -8, -6, -4, -4, -4, -4, -4, -2, 0, 2, 4, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 4, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, -1, -2, -3, -4, -4, -2, 0, 0, 0, 1, 3, 5, 7, 7, 5, 3, 3, 3, 3, 3 }
+
+ local decoded = dfpwm.decode(input)
+ expect(#decoded):describe("The lengths match"):eq(#output)
+ for i = 1, #decoded do expect(decoded[i]):describe("Item at #" .. i):eq(output[i]) end
+ end)
+ end)
+
+ describe("encode", function()
+ it("encodes some data", function()
+ local input = { 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3 }
+ local output = { 87, 74, 42, 165, 164, 148, 84, 169, 170, 86, 173, 90, 173, 213, 90, 171, 214, 106, 213, 170, 106, 149, 42, 149, 74, 169, 74, 165, 74, 165, 170, 170, 106, 85, 107, 173, 106, 173, 173, 86, 181, 170, 42, 85, 149, 82, 41, 165, 82, 74, 41, 149, 170, 212, 170, 86, 181, 106, 173, 181, 170, 181, 90, 173, 170, 170, 170, 82, 165, 74, 149, 170, 82, 169, 82, 85, 85, 85, 173, 86, 181, 170, 213, 90, 173, 90, 85, 85, 149, 42, 165, 82, 170, 82, 74, 41, 85, 169, 170, 170, 106, 181, 90, 173, 86, 171, 106, 213, 106, 85, 85, 85, 85, 149, 42, 85, 170, 42, 149, 170, 170, 170, 170, 106, 181, 170, 86, 171 }
+
+ local encoded = dfpwm.encode(input)
+ expect(#encoded):describe("The lengths match"):eq(#output)
+ for i = 1, #encoded do expect(encoded:byte(i)):describe("Item at #" .. i):eq(output[i] % 256) end
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/completion_spec.lua b/src/test/resources/test-rom/spec/modules/cc/completion_spec.lua
new file mode 100644
index 000000000..41e7ae5bc
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/completion_spec.lua
@@ -0,0 +1,57 @@
+describe("cc.completion", function()
+ local c = require("cc.completion")
+
+ describe("choice", function()
+ it("provides all choices", function()
+ expect(c.choice("", { "some text", "some other", "other" }))
+ :same { "some text", "some other", "other" }
+ end)
+
+ it("provides a filtered list of choices", function()
+ expect(c.choice("som", { "some text", "some other", "other" }))
+ :same { "e text", "e other" }
+
+ expect(c.choice("none", { "some text", "some other", "other" }))
+ :same { }
+ end)
+
+ it("adds text if needed", function()
+ expect(c.choice("som", { "some text", "some other", "other" }, true))
+ :same { "e text ", "e other " }
+ end)
+ end)
+
+ describe("peripheral", function()
+ it("provides a choice of peripherals", function()
+ stub(peripheral, "getNames", function() return { "drive_0", "left" } end)
+
+ expect(c.peripheral("dri")):same { "ve_0" }
+ expect(c.peripheral("dri", true)):same { "ve_0 " }
+ end)
+ end)
+
+ describe("side", function()
+ it("provides a choice of sides", function()
+ expect(c.side("le")):same { "ft" }
+ expect(c.side("le", true)):same { "ft " }
+ end)
+ end)
+
+ describe("setting", function()
+ it("provides a choice of setting names", function()
+ stub(settings, "getNames", function() return { "shell.allow_startup", "list.show_hidden" } end)
+
+ expect(c.setting("li")):same { "st.show_hidden" }
+ expect(c.setting("li", true)):same { "st.show_hidden " }
+ end)
+ end)
+
+ describe("command", function()
+ it("provides a choice of command names", function()
+ stub(_G, "commands", { list = function() return { "list", "say" } end })
+
+ expect(c.command("li")):same { "st" }
+ expect(c.command("li", true)):same { "st " }
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua b/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua
new file mode 100644
index 000000000..98c91e53e
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua
@@ -0,0 +1,74 @@
+describe("cc.expect", function()
+ local e = require("cc.expect")
+
+ describe("expect", function()
+ it("checks a single type", function()
+ expect(e.expect(1, "test", "string")):eq("test")
+ expect(e.expect(1, 2, "number")):eq(2)
+
+ expect.error(e.expect, 1, nil, "string"):eq("bad argument #1 (expected string, got nil)")
+ expect.error(e.expect, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)")
+ end)
+
+ it("checks multiple types", function()
+ expect(e.expect(1, "test", "string", "number")):eq("test")
+ expect(e.expect(1, 2, "string", "number")):eq(2)
+
+ expect.error(e.expect, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)")
+ expect.error(e.expect, 2, false, "string", "table", "number", "nil")
+ :eq("bad argument #2 (expected string, table or number, got boolean)")
+ end)
+
+ it("includes the function name", function()
+ local function worker()
+ expect(e.expect(1, nil, "string")):eq(true)
+ end
+ local function trampoline()
+ worker()
+ end
+
+ expect.error(trampoline):str_match("^[^:]*expect_spec.lua:27: bad argument #1 to 'worker' %(expected string, got nil%)$")
+ end)
+ end)
+
+ describe("field", function()
+ it("checks a single type", function()
+ expect(e.field({ k = "test" }, "k", "string")):eq("test")
+ expect(e.field({ k = 2 }, "k", "number")):eq(2)
+
+ expect.error(e.field, { k = nil }, "k", "string"):eq("field 'k' missing from table")
+ expect.error(e.field, { l = 1 }, "l", "nil"):eq("bad field 'l' (expected nil, got number)")
+ end)
+
+ it("checks multiple types", function()
+ expect(e.field({ k = "test" }, "k", "string", "number")):eq("test")
+ expect(e.field({ k = 2 }, "k", "string", "number")):eq(2)
+
+ expect.error(e.field, { k = nil }, "k", "string", "number")
+ :eq("field 'k' missing from table")
+ expect.error(e.field, { l = false }, "l", "string", "table", "number", "nil")
+ :eq("bad field 'l' (expected string, table or number, got boolean)")
+ end)
+ end)
+
+ describe("range", function()
+ it("works fith full args", function()
+ expect(e.range(1, 1, 1)):eq(1)
+ expect(e.range(2, 1, 3)):eq(2)
+
+ expect.error(e.range, 2, 0, 1):eq("number outside of range (expected 2 to be within 0 and 1)")
+ expect.error(e.range, 0, 1, 2):eq("number outside of range (expected 0 to be within 1 and 2)")
+ local NaN = 0 / 0
+ expect.error(e.range, NaN, 1, 2):eq(("number outside of range (expected %s to be within 1 and 2)"):format(tostring(NaN)))
+ end)
+
+ it("fills in min and max if they are nil", function()
+ expect(e.range(1, 1)):eq(1)
+ expect(e.range(2, nil, 3)):eq(2)
+ expect(e.range(2)):eq(2)
+
+ expect.error(e.range, 2, nil, 1):eq("number outside of range (expected 2 to be within -inf and 1)")
+ expect.error(e.range, 0, 1):eq("number outside of range (expected 0 to be within 1 and inf)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/image/nft_spec.lua b/src/test/resources/test-rom/spec/modules/cc/image/nft_spec.lua
new file mode 100644
index 000000000..f67c40898
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/image/nft_spec.lua
@@ -0,0 +1,91 @@
+local helpers = require "test_helpers"
+
+describe("cc.image.nft", function()
+ local nft = require("cc.image.nft")
+
+ describe("parse", function()
+ it("validates arguments", function()
+ nft.parse("")
+ expect.error(nft.parse, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("parses an empty string", function()
+ expect(nft.parse("")):same {}
+ end)
+
+ it("parses a string with no colours", function()
+ expect(nft.parse("Hello")):same { { text = "Hello", foreground = "00000", background = "fffff" } }
+ end)
+
+ it("handles background and foreground colours", function()
+ expect(nft.parse("\30a\31bHello"))
+ :same { { text = "Hello", foreground = "bbbbb", background = "aaaaa" } }
+ end)
+
+ it("parses multi-line files", function()
+ expect(nft.parse("Hello\nWorld")):same {
+ { text = "Hello", foreground = "00000", background = "fffff" },
+ { text = "World", foreground = "00000", background = "fffff" },
+ }
+ end)
+
+ it("handles empty lines", function()
+ expect(nft.parse("\n\n")):same {
+ { text = "", foreground = "", background = "" },
+ { text = "", foreground = "", background = "" },
+ }
+ end)
+ end)
+
+ describe("load", function()
+ it("validates arguments", function()
+ nft.load("")
+ expect.error(nft.load, nil):eq("bad argument #1 (expected string, got nil)")
+ end)
+
+ it("loads from a file", function()
+ local image = fs.open("/test-files/example.nft", "w")
+ image.write("\30aHello, world!")
+ image.close()
+
+ expect(nft.load("/test-files/example.nft")):same {
+ { background = "aaaaaaaaaaaaa", foreground = "0000000000000", text = "Hello, world!" },
+ }
+ end)
+
+ it("fails on missing files", function()
+ expect({ nft.load("/test-files/not_a_file.nft") })
+ :same { nil, "/test-files/not_a_file.nft: No such file" }
+ end)
+ end)
+
+ describe("draw", function()
+ it("validates arguments", function()
+ expect.error(nft.draw, nil):eq("bad argument #1 (expected table, got nil)")
+ expect.error(nft.draw, {}, nil):eq("bad argument #2 (expected number, got nil)")
+ expect.error(nft.draw, {}, 1, nil):eq("bad argument #3 (expected number, got nil)")
+ expect.error(nft.draw, {}, 1, 1, false):eq("bad argument #4 (expected table, got boolean)")
+ end)
+
+ it("draws an image", function()
+ local win = helpers.with_window(7, 3, function()
+ nft.draw({
+ { background = "aaaaa", foreground = "f000f", text = "Hello" },
+ }, 2, 2)
+ end)
+
+ expect(win.getLine(1)):eq(" ")
+ expect({ win.getLine(2) }):same { " Hello ", "0f000f0", "faaaaaf" }
+ expect(win.getLine(3)):eq(" ")
+ end)
+
+ it("draws an image to a custom redirect", function()
+ local win = window.create(term.current(), 1, 1, 5, 1, false)
+ nft.draw({
+ { background = "aaaaa", foreground = "f000f", text = "Hello" },
+ }, 1, 1, win)
+
+ expect({ win.getLine(1) }):same { "Hello", "f000f", "aaaaa" }
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua
new file mode 100644
index 000000000..0832a5715
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua
@@ -0,0 +1,228 @@
+local with_window = require "test_helpers".with_window
+
+describe("cc.pretty", function()
+ local pp = require("cc.pretty")
+
+ describe("text", function()
+ it("is constant for the empty string", function()
+ expect(pp.text("")):eq(pp.empty)
+ end)
+
+ it("is constant for a space", function()
+ expect(pp.text(" ")):eq(pp.space)
+ end)
+
+ it("is constant for a newline", function()
+ expect(pp.text("\n")):eq(pp.space_line)
+ end)
+
+ it("validates arguments", function()
+ expect.error(pp.text, 123):eq("bad argument #1 (expected string, got number)")
+ expect.error(pp.text, "", ""):eq("bad argument #2 (expected number, got string)")
+ end)
+
+ it("produces text documents", function()
+ expect(pp.text("a")):same({ tag = "text", text = "a" })
+ expect(pp.text("a", colours.grey)):same({ tag = "text", text = "a", colour = colours.grey })
+ end)
+
+ it("splits lines", function()
+ expect(pp.text("a\nb"))
+ :same(pp.concat(pp.text("a"), pp.space_line, pp.text("b")))
+ expect(pp.text("ab\ncd\nef"))
+ :same(pp.concat(pp.text("ab"), pp.space_line, pp.text("cd"), pp.space_line, pp.text("ef")))
+ end)
+
+ it("preserves empty lines", function()
+ expect(pp.text("a\n\nb"))
+ :same(pp.concat(pp.text("a"), pp.space_line, pp.space_line, pp.text("b")))
+ expect(pp.text("\n\nb"))
+ :same(pp.concat(pp.space_line, pp.space_line, pp.text("b")))
+ expect(pp.text("a\n\n"))
+ :same(pp.concat(pp.text("a"), pp.space_line, pp.space_line))
+ end)
+ end)
+
+ describe("concat", function()
+ it("returns empty with 0 arguments", function()
+ expect(pp.concat()):eq(pp.empty)
+ end)
+
+ it("acts as the identity with 1 argument", function()
+ local x = pp.text("test")
+ expect(pp.concat(x)):eq(x)
+ end)
+
+ it("coerces strings", function()
+ expect(pp.concat("a", "b")):same(pp.concat(pp.text("a"), pp.text("b")))
+ end)
+
+ it("validates arguments", function()
+ expect.error(pp.concat, 123):eq("bad argument #1 (expected document, got number)")
+ expect.error(pp.concat, "", {}):eq("bad argument #2 (expected document, got table)")
+ end)
+
+ it("can be used as an operator", function()
+ local a, b = pp.text("a"), pp.text("b")
+ expect(pp.concat(a, b)):same(a .. b)
+ end)
+ end)
+
+ describe("group", function()
+ it("is idempotent", function()
+ local x = pp.group(pp.text("a\nb"))
+ expect(pp.group(x)):eq(x)
+ end)
+
+ it("does nothing for flat strings", function()
+ local x = pp.text("a")
+ expect(pp.group(x)):eq(x)
+ end)
+ end)
+
+ -- Allows us to test
+ local function test_output(display)
+ it("displays the empty document", function()
+ expect(display(pp.empty)):same { "" }
+ end)
+
+ it("displays a multiline string", function()
+ expect(display(pp.text("hello\nworld"))):same {
+ "hello",
+ "world",
+ }
+ end)
+
+ it("displays a nested string", function()
+ expect(display(pp.nest(2, pp.concat("hello", pp.line, "world")))):same {
+ "hello",
+ " world",
+ }
+ end)
+
+ it("displays a flattened group", function()
+ expect(display(pp.group(pp.concat("hello", pp.space_line, "world")))):same {
+ "hello world",
+ }
+
+ expect(display(pp.group(pp.concat("hello", pp.line, "world")))):same {
+ "helloworld",
+ }
+ end)
+
+ it("displays an expanded group", function()
+ expect(display(pp.group(pp.concat("hello darkness", pp.space_line, "my old friend")))):same {
+ "hello darkness",
+ "my old friend",
+ }
+ end)
+
+ it("group removes nest", function()
+ expect(display(pp.group(pp.nest(2, pp.concat("hello", pp.space_line, "world"))))):same {
+ "hello world",
+ }
+ end)
+ end
+
+ describe("write", function()
+ local function display(doc)
+ local w = with_window(20, 10, function() pp.write(doc) end)
+ local _, y = w.getCursorPos()
+
+ local out = {}
+ for i = 1, y do out[i] = w.getLine(i):gsub("%s+$", "") end
+ return out
+ end
+
+ test_output(display)
+
+ it("wraps a long string", function()
+ expect(display(pp.text("hello world this is a long string which will wrap"))):same {
+ "hello world this is",
+ "a long string which",
+ "will wrap",
+ }
+ end)
+ end)
+
+ describe("render", function()
+ local function display(doc)
+ local rendered = pp.render(doc, 20)
+ local n, lines = 1, {}
+ for line in (rendered .. "\n"):gmatch("([^\n]*)\n") do lines[n], n = line, n + 1 end
+ return lines
+ end
+
+ test_output(display)
+
+ it("does not wrap a long string", function()
+ expect(display(pp.text("hello world this is a long string which will wrap"))):same {
+ "hello world this is a long string which will wrap",
+ }
+ end)
+ end)
+
+ describe("pretty", function()
+ -- We make use of "render" here, as it's considerably easier than checking against the actual structure.
+ -- However, it does also mean our tests are less unit-like.
+ local function pretty(x, width, options) return pp.render(pp.pretty(x, options), width) end
+
+ describe("tables", function()
+ it("displays empty tables", function()
+ expect(pp.pretty({})):same(pp.text("{}"))
+ end)
+
+ it("displays list-like tables", function()
+ expect(pretty({ 1, 2, 3 })):eq("{ 1, 2, 3 }")
+ end)
+
+ it("displays mixed tables", function()
+ expect(pretty({ n = 3, 1, 2, 3 })):eq("{ 1, 2, 3, n = 3 }")
+ end)
+
+ it("escapes keys", function()
+ expect(pretty({ ["and"] = 1, ["not that"] = 2 })):eq('{ ["and"] = 1, ["not that"] = 2 }')
+ end)
+
+ it("sorts keys", function()
+ expect(pretty({ c = 1, b = 2, a = 3 })):eq('{ a = 3, b = 2, c = 1 }')
+ end)
+
+ it("groups tables", function()
+ expect(pretty({ 1, 2, 3 }, 4)):eq("{\n 1,\n 2,\n 3\n}")
+ end)
+
+ it("handles sparse tables", function()
+ local tbl = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
+ tbl[4] = nil
+
+ expect(tostring(pp.pretty(tbl))):eq("{ 1, 2, 3, nil, 5, 6, 7, 8, 9, 10 }")
+ end)
+ end)
+
+ it("shows numbers", function()
+ expect(pretty(123)):eq("123")
+ end)
+
+ it("shows strings", function()
+ expect(pretty("hello\nworld")):eq('"hello\\nworld"')
+ end)
+
+ describe("functions", function()
+ it("shows functions", function()
+ expect(pretty(pretty)):eq(tostring(pretty))
+ end)
+
+ it("shows function arguments", function()
+ local f = function(a, ...) end
+ expect(pretty(f, nil, { function_args = true })):eq(tostring(f) .. "(a, ...)")
+ end)
+
+ it("shows the function source", function()
+ local f = function(a, ...) end
+ expect(pretty(f, nil, { function_source = true }))
+ :str_match("^function<.*pretty_spec%.lua:%d+>$")
+ end)
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua b/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua
new file mode 100644
index 000000000..4ecc6056f
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua
@@ -0,0 +1,65 @@
+describe("cc.shell.completion", function()
+ local c = require "cc.shell.completion"
+
+ describe("dirOrFile", function()
+ it("completes both", function()
+ expect(c.dirOrFile(shell, "rom/")):same {
+ "apis/", "apis", "autorun/", "autorun", "help/", "help",
+ "modules/", "modules", "motd.txt", "programs/", "programs", "startup.lua",
+ }
+ end)
+
+ it("adds a space", function()
+ expect(c.dirOrFile(shell, "rom/", nil, true)):same {
+ "apis/", "apis ", "autorun/", "autorun ", "help/", "help ",
+ "modules/", "modules ", "motd.txt ", "programs/", "programs ", "startup.lua ",
+ }
+ end)
+ end)
+
+ describe("program", function()
+ it("completes programs", function()
+ expect(c.program(shell, "rom/")):same {
+ "apis/", "autorun/", "help/", "modules/", "motd.txt", "programs/", "startup.lua",
+ }
+ end)
+ end)
+
+ describe("programWithArgs", function()
+ it("completes program name", function()
+ shell.setCompletionFunction("rom/motd.txt", function() end)
+ expect(c.programWithArgs(shell, "rom/", { "rom/programs/shell.lua" }, 2)):same {
+ "apis/", "autorun/", "help/", "modules/", "motd.txt ", "programs/", "startup.lua",
+ }
+ end)
+
+ it("completes program arguments", function()
+ expect(c.programWithArgs(shell, "", { "rom/programs/shell.lua", "pastebin" }, 2)):same {
+ "put ", "get ", "run ",
+ }
+ end)
+
+ end)
+
+ describe("build", function()
+ it("completes multiple arguments", function()
+ local spec = c.build(
+ function() return { "a", "b", "c" } end,
+ nil,
+ { c.choice, { "d", "e", "f" } }
+ )
+
+ expect(spec(shell, 1, "")):same { "a", "b", "c" }
+ expect(spec(shell, 2, "")):same(nil)
+ expect(spec(shell, 3, "")):same { "d", "e", "f" }
+ expect(spec(shell, 4, "")):same(nil)
+ end)
+
+ it("supports variadic completions", function()
+ local spec = c.build({ function() return { "a", "b", "c" } end, many = true })
+
+ expect(spec(shell, 1, "")):same({ "a", "b", "c" })
+ expect(spec(shell, 2, "")):same({ "a", "b", "c" })
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/shell/require_spec.lua b/src/test/resources/test-rom/spec/modules/cc/shell/require_spec.lua
new file mode 100644
index 000000000..721476f9d
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/shell/require_spec.lua
@@ -0,0 +1,79 @@
+describe("cc.require", function()
+ local r = require "cc.require"
+ local function mk()
+ local env = setmetatable({}, { __index = _ENV })
+ env.require, env.package = r.make({}, "/test-files/modules")
+ return env.require, env.package
+ end
+
+ local function setup(path, contents)
+ fs.delete("/test-files/modules")
+ io.open(path, "w"):write(contents):close()
+ end
+
+ describe("require", function()
+ it("errors on recursive modules", function()
+ local require, package = mk()
+ package.preload.pkg = function() require "pkg" end
+ expect.error(require, "pkg"):eq("loop or previous error loading module 'pkg'")
+ end)
+
+ it("supplies the current module name", function()
+ local require, package = mk()
+ package.preload.pkg = table.pack
+ expect(require("pkg")):same { n = 1, "pkg" }
+ end)
+
+ it("returns true instead of nil", function()
+ local require, package = mk()
+ package.preload.pkg = function() return nil end
+ expect(require("pkg")):eq(true)
+ end)
+
+ it("returns a constant value", function()
+ local require, package = mk()
+ package.preload.pkg = function() return {} end
+ expect(require("pkg")):eq(require("pkg"))
+ end)
+
+ it("returns an error on not-found modules", function()
+ local require, package = mk()
+ package.path = "/?;/?.lua"
+ expect.error(require, "pkg"):eq(
+ "module 'pkg' not found:\n" ..
+ " no field package.preload['pkg']\n" ..
+ " no file '/pkg'\n" ..
+ " no file '/pkg.lua'")
+ end)
+ end)
+
+ describe("the file loader", function()
+ local function get(path)
+ local require, package = mk()
+ if path then package.path = path end
+ return require
+ end
+
+ it("works on absolute paths", function()
+ local require = get("/test-files/?.lua")
+ setup("test-files/some_module.lua", "return 123")
+ expect(require("some_module")):eq(123)
+ end)
+
+ it("works on relative paths", function()
+ local require = get("?.lua")
+ setup("test-files/modules/some_module.lua", "return 123")
+ expect(require("some_module")):eq(123)
+ end)
+
+ it("fails on syntax errors", function()
+ local require = get("?.lua")
+ setup("test-files/modules/some_module.lua", "1")
+ expect.error(require, "some_module"):str_match(
+ "^module 'some_module' not found:\n" ..
+ " no field package.preload%['some_module'%]\n" ..
+ " [^:]*some_module.lua:1: unexpected symbol near '1'$"
+ )
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua b/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua
new file mode 100644
index 000000000..900c6fe6f
--- /dev/null
+++ b/src/test/resources/test-rom/spec/modules/cc/strings_spec.lua
@@ -0,0 +1,41 @@
+describe("cc.pretty", function()
+ local str = require("cc.strings")
+
+ describe("wrap", function()
+ it("validates arguments", function()
+ str.wrap("test string is long")
+ str.wrap("test string is long", 11)
+ expect.error(str.wrap, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(str.wrap, "", false):eq("bad argument #2 (expected number, got boolean)")
+ end)
+
+ it("wraps lines", function()
+ expect(str.wrap("test string is long")[1]):eq("test string is long")
+
+ expect(str.wrap("test string is long", 15)[1]):eq("test string is ")
+ expect(str.wrap("test string is long", 15)[2]):eq("long")
+
+ expect(str.wrap("test string is long", 12)[1]):eq("test string ")
+ expect(str.wrap("test string is long", 12)[2]):eq("is long")
+
+ expect(str.wrap("test string is long", 11)[1]):eq("test string")
+ expect(str.wrap("test string is long", 11)[2]):eq("is long")
+ end)
+ end)
+
+ describe("ensure_width", function()
+ it("validates arguments", function()
+ str.wrap("test string is long")
+ str.wrap("test string is long", 11)
+ expect.error(str.ensure_width, nil):eq("bad argument #1 (expected string, got nil)")
+ expect.error(str.ensure_width, "", false):eq("bad argument #2 (expected number, got boolean)")
+ end)
+
+ it("pads lines", function()
+ expect(str.ensure_width("test string is long", 25)):eq("test string is long ")
+ end)
+ it("truncates lines", function()
+ expect(str.ensure_width("test string is long", 15)):eq("test string is ")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/advanced/bg_spec.lua b/src/test/resources/test-rom/spec/programs/advanced/bg_spec.lua
new file mode 100644
index 000000000..a53dc2f2d
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/advanced/bg_spec.lua
@@ -0,0 +1,11 @@
+local capture = require "test_helpers".capture_program
+
+describe("The bg program", function()
+ it("opens a tab in the background", function()
+ local openTab = stub(shell, "openTab", function() return 12 end)
+ local switchTab = stub(shell, "switchTab")
+ capture(stub, "bg")
+ expect(openTab):called_with("shell")
+ expect(switchTab):called(0)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/advanced/fg_spec.lua b/src/test/resources/test-rom/spec/programs/advanced/fg_spec.lua
new file mode 100644
index 000000000..8e5cc4f26
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/advanced/fg_spec.lua
@@ -0,0 +1,11 @@
+local capture = require "test_helpers".capture_program
+
+describe("The fg program", function()
+ it("opens the shell in the foreground", function()
+ local openTab = stub(shell, "openTab", function() return 12 end)
+ local switchTab = stub(shell, "switchTab")
+ capture(stub, "fg")
+ expect(openTab):called_with("shell")
+ expect(switchTab):called_with(12)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/advanced/multishell_spec.lua b/src/test/resources/test-rom/spec/programs/advanced/multishell_spec.lua
new file mode 100644
index 000000000..d0917c296
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/advanced/multishell_spec.lua
@@ -0,0 +1,30 @@
+describe("The multishell program", function()
+ describe("multishell.setFocus", function()
+ it("validates arguments", function()
+ multishell.setFocus(multishell.getFocus())
+ expect.error(multishell.setFocus, nil):eq("bad argument #1 (expected number, got nil)")
+ end)
+ end)
+
+ describe("multishell.getTitle", function()
+ it("validates arguments", function()
+ multishell.getTitle(1)
+ expect.error(multishell.getTitle, nil):eq("bad argument #1 (expected number, got nil)")
+ end)
+ end)
+
+ describe("multishell.setTitle", function()
+ it("validates arguments", function()
+ multishell.setTitle(1, multishell.getTitle(1))
+ expect.error(multishell.setTitle, nil):eq("bad argument #1 (expected number, got nil)")
+ expect.error(multishell.setTitle, 1, nil):eq("bad argument #2 (expected string, got nil)")
+ end)
+ end)
+
+ describe("multishell.launch", function()
+ it("validates arguments", function()
+ expect.error(multishell.launch, nil):eq("bad argument #1 (expected table, got nil)")
+ expect.error(multishell.launch, _ENV, nil):eq("bad argument #2 (expected string, got nil)")
+ end)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/alias_spec.lua b/src/test/resources/test-rom/spec/programs/alias_spec.lua
new file mode 100644
index 000000000..95a2c2556
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/alias_spec.lua
@@ -0,0 +1,28 @@
+local capture = require "test_helpers".capture_program
+
+describe("The alias program", function()
+ it("displays its usage when given too many arguments", function()
+ expect(capture(stub, "alias a b c"))
+ :matches { ok = true, output = "Usage: alias \n", error = "" }
+ end)
+
+ it("lists aliases", function()
+ local pagedTabulate = stub(textutils, "pagedTabulate", function(x) print(table.unpack(x)) end)
+ stub(shell, "aliases", function() return { cp = "copy" } end)
+ expect(capture(stub, "alias"))
+ :matches { ok = true, output = "cp:copy\n", error = "" }
+ expect(pagedTabulate):called_with_matching({ "cp:copy" })
+ end)
+
+ it("sets an alias", function()
+ local setAlias = stub(shell, "setAlias")
+ capture(stub, "alias test Hello")
+ expect(setAlias):called_with("test", "Hello")
+ end)
+
+ it("clears an alias", function()
+ local clearAlias = stub(shell, "clearAlias")
+ capture(stub, "alias test")
+ expect(clearAlias):called_with("test")
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/cd_spec.lua b/src/test/resources/test-rom/spec/programs/cd_spec.lua
new file mode 100644
index 000000000..348d62944
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/cd_spec.lua
@@ -0,0 +1,19 @@
+local capture = require "test_helpers".capture_program
+
+describe("The cd program", function()
+ it("changes into a directory", function()
+ local setDir = stub(shell, "setDir")
+ capture(stub, "cd /rom/programs")
+ expect(setDir):called_with("rom/programs")
+ end)
+
+ it("does not move into a non-existent directory", function()
+ expect(capture(stub, "cd /rom/nothing"))
+ :matches { ok = true, output = "Not a directory\n", error = "" }
+ end)
+
+ it("displays the usage when given no arguments", function()
+ expect(capture(stub, "cd"))
+ :matches { ok = true, output = "Usage: cd \n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/clear_spec.lua b/src/test/resources/test-rom/spec/programs/clear_spec.lua
new file mode 100644
index 000000000..f339c83ce
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/clear_spec.lua
@@ -0,0 +1,13 @@
+local capture = require "test_helpers".capture_program
+
+describe("The clear program", function()
+ it("clears the screen", function()
+ local clear = stub(term, "clear")
+ local setCursorPos = stub(term, "setCursorPos")
+
+ capture(stub, "clear")
+
+ expect(clear):called(1)
+ expect(setCursorPos):called_with(1, 1)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/command/commands_spec.lua b/src/test/resources/test-rom/spec/programs/command/commands_spec.lua
new file mode 100644
index 000000000..6c9d87cdc
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/command/commands_spec.lua
@@ -0,0 +1,20 @@
+local capture = require "test_helpers".capture_program
+
+describe("The commands program", function()
+ it("displays an error without the commands api", function()
+ stub(_G, "commands", nil)
+ expect(capture(stub, "/rom/programs/command/commands.lua"))
+ :matches { ok = true, output = "", error = "Requires a Command Computer.\n" }
+ end)
+
+ it("lists commands", function()
+ local pagedTabulate = stub(textutils, "pagedTabulate", function(x) print(table.unpack(x)) end)
+ stub(_G, "commands", {
+ list = function() return { "computercraft" } end,
+ })
+
+ expect(capture(stub, "/rom/programs/command/commands.lua"))
+ :matches { ok = true, output = "Available commands:\ncomputercraft\n", error = "" }
+ expect(pagedTabulate):called_with_matching({ "computercraft" })
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/command/exec_spec.lua b/src/test/resources/test-rom/spec/programs/command/exec_spec.lua
new file mode 100644
index 000000000..f5eecd3d3
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/command/exec_spec.lua
@@ -0,0 +1,33 @@
+local capture = require "test_helpers".capture_program
+
+describe("The exec program", function()
+ it("displays an error without the commands api", function()
+ stub(_G, "commands", nil)
+ expect(capture(stub, "/rom/programs/command/exec.lua"))
+ :matches { ok = true, output = "", error = "Requires a Command Computer.\n" }
+ end)
+
+ it("displays its usage when given no argument", function()
+ stub(_G, "commands", {})
+ expect(capture(stub, "/rom/programs/command/exec.lua"))
+ :matches { ok = true, output = "", error = "Usage: /rom/programs/command/exec.lua \n" }
+ end)
+
+ it("runs a command", function()
+ stub(_G, "commands", {
+ exec = function() return true, { "Hello World!" } end,
+ })
+
+ expect(capture(stub, "/rom/programs/command/exec.lua computercraft"))
+ :matches { ok = true, output = "Success\nHello World!\n", error = "" }
+ end)
+
+ it("reports command failures", function()
+ stub(_G, "commands", {
+ exec = function() return false, { "Hello World!" } end,
+ })
+
+ expect(capture(stub, "/rom/programs/command/exec.lua computercraft"))
+ :matches { ok = true, output = "Hello World!\n", error = "Failed\n" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/copy_spec.lua b/src/test/resources/test-rom/spec/programs/copy_spec.lua
new file mode 100644
index 000000000..5bcdda8b0
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/copy_spec.lua
@@ -0,0 +1,40 @@
+local capture = require "test_helpers".capture_program
+
+describe("The copy program", function()
+ local function touch(file)
+ io.open(file, "w"):close()
+ end
+
+ it("copies a file", function()
+ touch("/test-files/copy/a.txt")
+
+ shell.run("copy /test-files/copy/a.txt /test-files/copy/b.txt")
+
+ expect(fs.exists("/test-files/copy/a.txt")):eq(true)
+ expect(fs.exists("/test-files/copy/b.txt")):eq(true)
+ end)
+
+ it("fails when copying a non-existent file", function()
+ expect(capture(stub, "copy nothing destination"))
+ :matches { ok = true, output = "", error = "No matching files\n" }
+ end)
+
+ it("fails when overwriting an existing file", function()
+ touch("/test-files/copy/c.txt")
+
+ expect(capture(stub, "copy /test-files/copy/c.txt /test-files/copy/c.txt"))
+ :matches { ok = true, output = "", error = "Destination exists\n" }
+ end)
+
+ it("fails when copying into read-only locations", function()
+ touch("/test-files/copy/d.txt")
+
+ expect(capture(stub, "copy /test-files/copy/d.txt /rom/test.txt"))
+ :matches { ok = true, output = "", error = "Destination is read-only\n" }
+ end)
+
+ it("displays the usage when given no arguments", function()
+ expect(capture(stub, "copy"))
+ :matches { ok = true, output = "Usage: copy \n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/delete_spec.lua b/src/test/resources/test-rom/spec/programs/delete_spec.lua
new file mode 100644
index 000000000..18a01c5c0
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/delete_spec.lua
@@ -0,0 +1,60 @@
+local capture = require "test_helpers".capture_program
+
+describe("The rm program", function()
+ local function touch(file)
+ io.open(file, "w"):close()
+ end
+
+ it("deletes one file", function()
+ touch("/test-files/a.txt")
+
+ shell.run("rm /test-files/a.txt")
+
+ expect(fs.exists("/test-files/a.txt")):eq(false)
+ end)
+
+ it("deletes many files", function()
+ touch("/test-files/a.txt")
+ touch("/test-files/b.txt")
+ touch("/test-files/c.txt")
+
+ shell.run("rm /test-files/a.txt /test-files/b.txt")
+
+ expect(fs.exists("/test-files/a.txt")):eq(false)
+ expect(fs.exists("/test-files/b.txt")):eq(false)
+ expect(fs.exists("/test-files/c.txt")):eq(true)
+ end)
+
+ it("deletes a glob", function()
+ touch("/test-files/a.txt")
+ touch("/test-files/b.txt")
+
+ shell.run("rm /test-files/*.txt")
+
+ expect(fs.exists("/test-files/a.txt")):eq(false)
+ expect(fs.exists("/test-files/b.txt")):eq(false)
+ end)
+
+ it("displays the usage with no arguments", function()
+ expect(capture(stub, "rm"))
+ :matches { ok = true, output = "Usage: rm \n", error = "" }
+ end)
+
+ it("errors when trying to delete a read-only file", function()
+ expect(capture(stub, "rm /rom/startup.lua"))
+ :matches { ok = true, output = "", error = "Cannot delete read-only file /rom/startup.lua\n" }
+ end)
+
+ it("errors when trying to delete the root mount", function()
+ expect(capture(stub, "rm /")):matches {
+ ok = true,
+ output = "To delete its contents run rm /*\n",
+ error = "Cannot delete mount /\n",
+ }
+ end)
+
+ it("errors when a glob fails to match", function()
+ expect(capture(stub, "rm", "never-existed"))
+ :matches { ok = true, output = "", error = "never-existed: No matching files\n" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/drive_spec.lua b/src/test/resources/test-rom/spec/programs/drive_spec.lua
new file mode 100644
index 000000000..d175a60f7
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/drive_spec.lua
@@ -0,0 +1,16 @@
+local capture = require "test_helpers".capture_program
+
+describe("The drive program", function()
+ it("run the program", function()
+ local getFreeSpace = stub(fs, "getFreeSpace", function() return 1234e4 end)
+
+ expect(capture(stub, "drive"))
+ :matches { ok = true, output = "hdd (12.3MB remaining)\n", error = "" }
+ expect(getFreeSpace):called(1):called_with("")
+ end)
+
+ it("fails on a non-existent path", function()
+ expect(capture(stub, "drive /rom/nothing"))
+ :matches { ok = true, output = "No such path\n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/edit_spec.lua b/src/test/resources/test-rom/spec/programs/edit_spec.lua
new file mode 100644
index 000000000..e58938e23
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/edit_spec.lua
@@ -0,0 +1,9 @@
+local capture = require "test_helpers".capture_program
+
+describe("The edit program", function()
+
+ it("displays its usage when given no argument", function()
+ expect(capture(stub, "edit"))
+ :matches { ok = true, output = "Usage: edit \n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/eject_spec.lua b/src/test/resources/test-rom/spec/programs/eject_spec.lua
new file mode 100644
index 000000000..e524a8625
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/eject_spec.lua
@@ -0,0 +1,13 @@
+local capture = require "test_helpers".capture_program
+
+describe("The eject program", function()
+ it("displays its usage when given no argument", function()
+ expect(capture(stub, "eject"))
+ :matches { ok = true, output = "Usage: eject \n", error = "" }
+ end)
+
+ it("fails when trying to eject a non-drive", function()
+ expect(capture(stub, "eject /rom"))
+ :matches { ok = true, output = "Nothing in /rom drive\n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/exit_spec.lua b/src/test/resources/test-rom/spec/programs/exit_spec.lua
new file mode 100644
index 000000000..301461087
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/exit_spec.lua
@@ -0,0 +1,9 @@
+local capture = require "test_helpers".capture_program
+
+describe("The exit program", function()
+ it("exits the shell", function()
+ local exit = stub(shell, "exit")
+ expect(capture(stub, "exit")):matches { ok = true, combined = "" }
+ expect(exit):called(1)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/fun/advanced/paint_spec.lua b/src/test/resources/test-rom/spec/programs/fun/advanced/paint_spec.lua
new file mode 100644
index 000000000..086404add
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/fun/advanced/paint_spec.lua
@@ -0,0 +1,8 @@
+local capture = require "test_helpers".capture_program
+
+describe("The paint program", function()
+ it("displays its usage when given no arguments", function()
+ expect(capture(stub, "paint"))
+ :matches { ok = true, output = "Usage: paint \n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/fun/dj_spec.lua b/src/test/resources/test-rom/spec/programs/fun/dj_spec.lua
new file mode 100644
index 000000000..4464d4e8c
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/fun/dj_spec.lua
@@ -0,0 +1,13 @@
+local capture = require "test_helpers".capture_program
+
+describe("The dj program", function()
+ it("displays its usage when given too many arguments", function()
+ expect(capture(stub, "dj a b c"))
+ :matches { ok = true, output = "Usages:\ndj play\ndj play \ndj stop\n", error = "" }
+ end)
+
+ it("fails when no disks are present", function()
+ expect(capture(stub, "dj"))
+ :matches { ok = true, output = "No Music Discs in attached disk drives\n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/fun/hello_spec.lua b/src/test/resources/test-rom/spec/programs/fun/hello_spec.lua
new file mode 100644
index 000000000..25db085bc
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/fun/hello_spec.lua
@@ -0,0 +1,10 @@
+local capture = require "test_helpers".capture_program
+
+describe("The hello program", function()
+ it("says hello", function()
+ local slowPrint = stub(textutils, "slowPrint", function(...) return print(...) end)
+ expect(capture(stub, "hello"))
+ :matches { ok = true, output = "Hello World!\n", error = "" }
+ expect(slowPrint):called(1)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/gps_spec.lua b/src/test/resources/test-rom/spec/programs/gps_spec.lua
new file mode 100644
index 000000000..245f259c9
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/gps_spec.lua
@@ -0,0 +1,23 @@
+local capture = require "test_helpers".capture_program
+
+describe("The gps program", function()
+ it("displays its usage when given no arguments", function()
+ expect(capture(stub, "gps"))
+ :matches { ok = true, output = "Usages:\ngps host\ngps host \ngps locate\n", error = "" }
+ end)
+
+ it("fails on a pocket computer", function()
+ stub(_G, "pocket", {})
+
+ expect(capture(stub, "gps host"))
+ :matches { ok = true, output = "GPS Hosts must be stationary\n", error = "" }
+ end)
+
+ it("can locate the computer", function()
+ local locate = stub(gps, "locate", function() print("Some debugging information.") end)
+
+ expect(capture(stub, "gps locate"))
+ :matches { ok = true, output = "Some debugging information.\n", error = "" }
+ expect(locate):called_with(2, true)
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/help_spec.lua b/src/test/resources/test-rom/spec/programs/help_spec.lua
new file mode 100644
index 000000000..35ce021b4
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/help_spec.lua
@@ -0,0 +1,8 @@
+local capture = require "test_helpers".capture_program
+
+describe("The help program", function()
+ it("errors when there is no such help file", function()
+ expect(capture(stub, "help nothing"))
+ :matches { ok = true, error = "No help available\n", output = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua
new file mode 100644
index 000000000..192ba7007
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua
@@ -0,0 +1,77 @@
+local capture = require "test_helpers".capture_program
+
+describe("The pastebin program", function()
+ local function setup_request()
+ stub(_G, "http", {
+ checkURL = function()
+ return true
+ end,
+ get = function()
+ return {
+ readAll = function()
+ return [[print("Hello", ...)]]
+ end,
+ close = function()
+ end,
+ getResponseHeaders = function()
+ local tHeader = {}
+ tHeader["Content-Type"] = "text/plain; charset=utf-8"
+ return tHeader
+ end,
+ }
+ end,
+ post = function()
+ return {
+ readAll = function()
+ return "https://pastebin.com/abcde"
+ end,
+ close = function()
+ end,
+ }
+ end,
+ })
+ end
+
+ it("downloads one file", function()
+ setup_request()
+ capture(stub, "pastebin", "get", "abcde", "testdown")
+
+ expect(fs.exists("/testdown")):eq(true)
+ end)
+
+ it("runs a program from the internet", function()
+ setup_request()
+
+ expect(capture(stub, "pastebin", "run", "abcde", "a", "b", "c"))
+ :matches { ok = true, output = "Connecting to pastebin.com... Success.\nHello a b c\n", error = "" }
+ end)
+
+ it("upload a program to pastebin", function()
+ setup_request()
+
+ local file = fs.open("testup", "w")
+ file.close()
+
+ expect(capture(stub, "pastebin", "put", "testup"))
+ :matches { ok = true, output = "Connecting to pastebin.com... Success.\nUploaded as https://pastebin.com/abcde\nRun \"pastebin get abcde\" to download anywhere\n", error = "" }
+ end)
+
+ it("upload a not existing program to pastebin", function()
+ setup_request()
+
+ expect(capture(stub, "pastebin", "put", "nothing"))
+ :matches { ok = true, output = "No such file\n", error = "" }
+ end)
+
+ it("displays its usage when given no arguments", function()
+ setup_request()
+
+ expect(capture(stub, "pastebin"))
+ :matches { ok = true, output = "Usages:\npastebin put \npastebin get \npastebin run \n", error = "" }
+ end)
+
+ it("can be completed", function()
+ local complete = shell.getCompletionInfo()["rom/programs/http/pastebin.lua"].fnComplete
+ expect(complete(shell, 1, "", {})):same { "put ", "get ", "run " }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/http/wget_spec.lua b/src/test/resources/test-rom/spec/programs/http/wget_spec.lua
new file mode 100644
index 000000000..c4b28dc53
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/http/wget_spec.lua
@@ -0,0 +1,78 @@
+local capture = require "test_helpers".capture_program
+
+describe("The wget program", function()
+ local default_contents = [[print("Hello", ...)]]
+ local function setup_request(contents)
+ stub(_G, "http", {
+ checkURL = function()
+ return true
+ end,
+ get = function()
+ return {
+ readAll = function()
+ return contents
+ end,
+ close = function()
+ end,
+ }
+ end,
+ })
+ end
+
+ it("downloads one file", function()
+ fs.delete("/example.com")
+ setup_request(default_contents)
+
+ capture(stub, "wget", "https://example.com")
+
+ expect(fs.exists("/example.com")):eq(true)
+ end)
+
+ it("downloads one file with given filename", function()
+ fs.delete("/test-files/download")
+ setup_request(default_contents)
+
+ capture(stub, "wget", "https://example.com /test-files/download")
+
+ expect(fs.exists("/test-files/download")):eq(true)
+ end)
+
+ it("downloads empty files", function()
+ fs.delete("/test-files/download")
+ setup_request(nil)
+
+ capture(stub, "wget", "https://example.com", "/test-files/download")
+
+ expect(fs.exists("/test-files/download")):eq(true)
+ expect(fs.getSize("/test-files/download")):eq(0)
+ end)
+
+ it("cannot save to rom", function()
+ setup_request(default_contents)
+
+ expect(capture(stub, "wget", "https://example.com", "/rom/a-file.txt")):matches {
+ ok = true,
+ output = "Connecting to https://example.com... Success.\n",
+ error = "Cannot save file: /rom/a-file.txt: Access denied\n",
+ }
+ end)
+
+ it("runs a program from the internet", function()
+ setup_request(default_contents)
+
+ expect(capture(stub, "wget", "run", "http://test.com", "a", "b", "c"))
+ :matches { ok = true, output = "Connecting to http://test.com... Success.\nHello a b c\n", error = "" }
+ end)
+
+ it("displays its usage when given no arguments", function()
+ setup_request(default_contents)
+
+ expect(capture(stub, "wget"))
+ :matches { ok = true, output = "Usage:\nwget [filename]\nwget run \n", error = "" }
+ end)
+
+ it("can be completed", function()
+ local complete = shell.getCompletionInfo()["rom/programs/http/wget.lua"].fnComplete
+ expect(complete(shell, 1, "", {})):same { "run " }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/id_spec.lua b/src/test/resources/test-rom/spec/programs/id_spec.lua
new file mode 100644
index 000000000..7c5d47da7
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/id_spec.lua
@@ -0,0 +1,11 @@
+local capture = require "test_helpers".capture_program
+
+describe("The id program", function()
+
+ it("displays computer id", function()
+ local id = os.getComputerID()
+
+ expect(capture(stub, "id"))
+ :matches { ok = true, output = "This is computer #" .. id .. "\n", error = "" }
+ end)
+end)
diff --git a/src/test/resources/test-rom/spec/programs/label_spec.lua b/src/test/resources/test-rom/spec/programs/label_spec.lua
new file mode 100644
index 000000000..5762f8fbe
--- /dev/null
+++ b/src/test/resources/test-rom/spec/programs/label_spec.lua
@@ -0,0 +1,34 @@
+local capture = require "test_helpers".capture_program
+
+describe("The label program", function()
+ it("displays its usage when given no arguments", function()
+ expect(capture(stub, "label"))
+ :matches { ok = true, output = "Usages:\nlabel get\nlabel get \nlabel set \nlabel set