Merge remote-tracking branch 'origin/upstream' am: da962d0686 am: 41b390c0c7
Original change: undetermined
Change-Id: I83e08dc961b636da2dd956b9da4683f8d60b95b2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7f1fdb7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+edid-decode
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..44b4a24
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,57 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["external_edid_decode_license"],
+}
+
+license {
+ name: "external_edid_decode_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-MIT",
+ ],
+ license_text: ["LICENSE"],
+}
+
+cc_binary {
+ name: "edid-decode",
+
+ srcs: [
+ "edid-decode.cpp",
+ "edid-decode.cpp",
+ "parse-base-block.cpp",
+ "parse-cta-block.cpp",
+ "parse-displayid-block.cpp",
+ "parse-ls-ext-block.cpp",
+ "parse-di-ext-block.cpp",
+ "parse-vtb-ext-block.cpp",
+ "calc-gtf-cvt.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-parameter",
+ "-Wno-implicit-fallthrough",
+ // edid-decode version = SHA + DATE
+ // Leave version blank by making SHA and DATE blank
+ "-DSHA=",
+ "-DDATE=",
+ ],
+
+ host_supported: true,
+
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6146137
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright 2006-2012 Red Hat, Inc.
+Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+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
+on the rights to use, copy, modify, merge, publish, distribute, sub
+license, 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 (including the next
+paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS 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/METADATA b/METADATA
new file mode 100644
index 0000000..6e50af2
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "edid-decode"
+description:
+ "Decodes the Extended Display Identification Data (EDID) binary structure "
+ "into a human readable format."
+
+third_party {
+homepage: "https://git.linuxtv.org/edid-decode.git/"
+ identifier {
+ type: "Git"
+ value: "https://git.linuxtv.org/edid-decode.git/"
+ primary_source: true
+ version: "e908154d87394de9e84e900162a3035e6c398fb2"
+ }
+ version: "e908154d87394de9e84e900162a3035e6c398fb2"
+ license_type: NOTICE
+ last_upgrade_date { year: 2024 month: 8 day: 21 }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..287b72d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+bindir ?= /usr/bin
+mandir ?= /usr/share/man
+
+EMXX ?= em++
+
+SOURCES = edid-decode.cpp parse-base-block.cpp parse-cta-block.cpp \
+ parse-displayid-block.cpp parse-ls-ext-block.cpp \
+ parse-di-ext-block.cpp parse-vtb-ext-block.cpp calc-gtf-cvt.cpp
+WARN_FLAGS = -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter
+
+all: edid-decode
+
+sha = -DSHA=$(shell if test -d .git ; then git rev-parse --short=12 HEAD ; fi)
+date = -DDATE=$(shell if test -d .git ; then printf '"'; TZ=UTC git show --quiet --date='format-local:%F %T"' --format="%cd"; fi)
+
+edid-decode: $(SOURCES) edid-decode.h Makefile
+ $(CXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) -g $(sha) $(date) -o $@ $(SOURCES) -lm
+
+edid-decode.js: $(SOURCES) edid-decode.h Makefile
+ $(EMXX) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(WARN_FLAGS) $(sha) $(date) -s EXPORTED_FUNCTIONS='["_parse_edid"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -o $@ $(SOURCES) -lm
+
+clean:
+ rm -f edid-decode
+
+install:
+ mkdir -p $(DESTDIR)$(bindir)
+ install -m 0755 edid-decode $(DESTDIR)$(bindir)
+ mkdir -p $(DESTDIR)$(mandir)/man1
+ install -m 0644 edid-decode.1 $(DESTDIR)$(mandir)/man1
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..3cabcaa
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,5 @@
+include platform/system/core:main:/janitors/OWNERS
+
+ddavenport@google.com
+sukoo@google.com
+seanpaul@google.com
diff --git a/README b/README
new file mode 100644
index 0000000..251587a
--- /dev/null
+++ b/README
@@ -0,0 +1,53 @@
+This utility is maintained here:
+
+https://git.linuxtv.org/edid-decode.git/
+
+To build this do:
+
+git clone git://linuxtv.org/edid-decode.git
+cd edid-decode
+make
+make install
+
+Patches and bug reports can be sent to the linux-media@vger.kernel.org
+mailinglist (see https://www.linuxtv.org/lists.php). Please make sure
+that 'edid-decode' appears in the subject line and don't forget to
+add a 'Signed-off-by' line when submitting patches!
+
+If you don't know what that is, then see:
+https://elinux.org/Developer_Certificate_Of_Origin
+
+New sample EDIDs are welcome. Please note that these must be real
+EDIDs read from real hardware, so no artificially constructed EDIDs.
+
+The naming of EDIDs in the data directory is:
+
+<vendor>-<model>-<port>[-tileX]
+
+<port> is one of dp, hdmi, vga, dvi
+When a tile is also specified, then dp should include the DP connector
+number, e.g. dp1-tile0, dp2-tile1.
+
+All lower case.
+
+If the EDID was modified by other devices in the chain (e.g. AV Receivers),
+then name the EDID as:
+
+<TV vendor>-<TV model>-<TV port>-<AV vendor>-<AV model>-<AV port>
+
+The test directory contains some special input files to verify the
+edid-decode parser and hand-crafted EDIDs to test rarely seen
+EDID features.
+
+You can find a very large collection of EDIDs here:
+https://github.com/linuxhw/EDID
+
+This collection has been used to test edid-decode.
+
+I can also be contacted directly:
+
+Hans Verkuil <hverkuil-cisco@xs4all.nl>
+
+Regards,
+
+ Hans
diff --git a/calc-gtf-cvt.cpp b/calc-gtf-cvt.cpp
new file mode 100644
index 0000000..bc32ea0
--- /dev/null
+++ b/calc-gtf-cvt.cpp
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "edid-decode.h"
+
+#define CELL_GRAN 8.0
+#define MARGIN_PERC 1.8
+#define GTF_MIN_PORCH 1.0
+#define GTF_V_SYNC_RQD 3.0
+#define GTF_H_SYNC_PERC 8.0
+#define GTF_MIN_VSYNC_BP 550.0
+
+timings edid_state::calc_gtf_mode(unsigned h_pixels, unsigned v_lines,
+ double ip_freq_rqd, bool int_rqd,
+ enum gtf_ip_parm ip_parm, bool margins_rqd,
+ bool secondary, double C, double M, double K, double J)
+{
+ timings t = {};
+ /* C' and M' are part of the Blanking Duty Cycle computation */
+ double C_PRIME = ((C - J) * K / 256.0) + J;
+ double M_PRIME = K / 256.0 * M;
+
+ double h_pixels_rnd = round(h_pixels / CELL_GRAN) * CELL_GRAN;
+ double v_lines_rnd = int_rqd ? round(v_lines / 2.0) : v_lines;
+ unsigned hor_margin = margins_rqd ?
+ round(h_pixels_rnd * MARGIN_PERC / 100.0 / CELL_GRAN) * CELL_GRAN : 0;
+ unsigned vert_margin = margins_rqd ? round(MARGIN_PERC / 100.0 * v_lines_rnd) : 0;
+ double interlace = int_rqd ? 0.5 : 0;
+ double total_active_pixels = h_pixels_rnd + hor_margin * 2;
+
+ t.hact = h_pixels_rnd;
+ t.vact = v_lines;
+ t.interlaced = int_rqd;
+
+ double pixel_freq;
+ double h_blank_pixels;
+ double total_pixels;
+ double v_sync_bp;
+
+ if (ip_parm == gtf_ip_vert_freq) {
+ // vertical frame frequency (Hz)
+ double v_field_rate_rqd = int_rqd ? ip_freq_rqd * 2 : ip_freq_rqd;
+ double h_period_est = ((1.0 / v_field_rate_rqd) - GTF_MIN_VSYNC_BP / 1000000.0) /
+ (v_lines_rnd + vert_margin * 2 + GTF_MIN_PORCH + interlace) * 1000000.0;
+ v_sync_bp = round(GTF_MIN_VSYNC_BP / h_period_est);
+ double total_v_lines = v_lines_rnd + vert_margin * 2 +
+ v_sync_bp + interlace + GTF_MIN_PORCH;
+ double v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
+ double h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
+ double ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
+ h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ total_pixels = total_active_pixels + h_blank_pixels;
+ pixel_freq = total_pixels / h_period;
+ } else if (ip_parm == gtf_ip_hor_freq) {
+ // horizontal frequency (kHz)
+ double h_freq = ip_freq_rqd;
+ v_sync_bp = round(GTF_MIN_VSYNC_BP * h_freq / 1000.0);
+ double ideal_duty_cycle = C_PRIME - (M_PRIME / h_freq);
+ h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ total_pixels = total_active_pixels + h_blank_pixels;
+ pixel_freq = total_pixels * h_freq / 1000.0;
+ } else {
+ // pixel clock rate (MHz)
+ pixel_freq = ip_freq_rqd;
+ double ideal_h_period =
+ ((C_PRIME - 100.0) +
+ sqrt(((100.0 - C_PRIME) * (100.0 - C_PRIME) +
+ (0.4 * M_PRIME * (total_active_pixels + hor_margin * 2) /
+ pixel_freq)))) / 2.0 / M_PRIME * 1000.0;
+ double ideal_duty_cycle = C_PRIME - (M_PRIME * ideal_h_period) / 1000.0;
+ h_blank_pixels = round(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ total_pixels = total_active_pixels + h_blank_pixels;
+ double h_freq = pixel_freq / total_pixels * 1000.0;
+ v_sync_bp = round(GTF_MIN_VSYNC_BP * h_freq / 1000.0);
+ }
+
+ double v_back_porch = v_sync_bp - GTF_V_SYNC_RQD;
+
+ t.vbp = v_back_porch;
+ t.vsync = GTF_V_SYNC_RQD;
+ t.vfp = GTF_MIN_PORCH;
+ t.pixclk_khz = round(1000.0 * pixel_freq);
+ t.hsync = round(GTF_H_SYNC_PERC / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
+ t.hfp = (h_blank_pixels / 2.0) - t.hsync;
+ t.hbp = t.hfp + t.hsync;
+ t.hborder = hor_margin;
+ t.vborder = vert_margin;
+ t.pos_pol_hsync = secondary;
+ t.pos_pol_vsync = !secondary;
+ t.rb = secondary ? RB_GTF : RB_NONE;
+ return t;
+}
+
+void edid_state::edid_gtf_mode(unsigned refresh, struct timings &t)
+{
+ unsigned hratio = t.hratio;
+ unsigned vratio = t.vratio;
+ t = calc_gtf_mode(t.hact, t.vact, refresh, t.interlaced);
+ t.hratio = hratio;
+ t.vratio = vratio;
+}
+
+#define CVT_MIN_VSYNC_BP 550.0
+#define CVT_MIN_V_PORCH 3
+#define CVT_MIN_V_BPORCH 6
+#define CVT_C_PRIME 30.0
+#define CVT_M_PRIME 300.0
+#define CVT_RB_MIN_VBLANK 460.0
+
+// If rb == RB_CVT_V2, then alt means video-optimized (i.e. 59.94 instead of 60 Hz, etc.).
+// If rb == RB_CVT_V3, then alt means that rb_h_blank is 160 instead of 80.
+timings edid_state::calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
+ double ip_freq_rqd, unsigned rb, bool int_rqd,
+ bool margins_rqd, bool alt, unsigned rb_h_blank)
+{
+ timings t = {};
+
+ t.hact = h_pixels;
+ t.vact = v_lines;
+ t.interlaced = int_rqd;
+
+ double cell_gran = rb == RB_CVT_V2 ? 1 : CELL_GRAN;
+ double h_pixels_rnd = floor(h_pixels / cell_gran) * cell_gran;
+ double v_lines_rnd = int_rqd ? floor(v_lines / 2.0) : v_lines;
+ unsigned hor_margin = margins_rqd ?
+ floor((h_pixels_rnd * MARGIN_PERC / 100.0) / cell_gran) * cell_gran : 0;
+ unsigned vert_margin = margins_rqd ? floor(MARGIN_PERC / 100.0 * v_lines_rnd) : 0;
+ double interlace = int_rqd ? 0.5 : 0;
+ double total_active_pixels = h_pixels_rnd + hor_margin * 2;
+ double v_field_rate_rqd = int_rqd ? ip_freq_rqd * 2 : ip_freq_rqd;
+ double clock_step = rb == RB_CVT_V2 ? 0.001 : 0.25;
+ double h_blank = (rb == RB_CVT_V1 || (rb == RB_CVT_V3 && alt)) ? 160 : 80;
+ double rb_v_fporch = rb == RB_CVT_V1 ? 3 : 1;
+ double refresh_multiplier = (rb == RB_CVT_V2 && alt) ? 1000.0 / 1001.0 : 1;
+ double h_sync = 32;
+
+ double v_sync;
+ double pixel_freq;
+ double v_blank;
+ double v_sync_bp;
+
+ if (rb == RB_CVT_V3 && rb_h_blank) {
+ h_blank = rb_h_blank & ~7;
+ if (h_blank < 80)
+ h_blank = 80;
+ else if (h_blank > 200)
+ h_blank = 200;
+ }
+
+ /* Determine VSync Width from aspect ratio */
+ if ((t.vact * 4 / 3) == t.hact)
+ v_sync = 4;
+ else if ((t.vact * 16 / 9) == t.hact)
+ v_sync = 5;
+ else if ((t.vact * 16 / 10) == t.hact)
+ v_sync = 6;
+ else if (!(t.vact % 4) && ((t.vact * 5 / 4) == t.hact))
+ v_sync = 7;
+ else if ((t.vact * 15 / 9) == t.hact)
+ v_sync = 7;
+ else /* Custom */
+ v_sync = 10;
+
+ if (rb >= RB_CVT_V2)
+ v_sync = 8;
+
+ if (rb == RB_NONE) {
+ double h_period_est = ((1.0 / v_field_rate_rqd) - CVT_MIN_VSYNC_BP / 1000000.0) /
+ (v_lines_rnd + vert_margin * 2 + CVT_MIN_V_PORCH + interlace) * 1000000.0;
+ v_sync_bp = floor(CVT_MIN_VSYNC_BP / h_period_est) + 1;
+ if (v_sync_bp < v_sync + CVT_MIN_V_BPORCH)
+ v_sync_bp = v_sync + CVT_MIN_V_BPORCH;
+ v_blank = v_sync_bp + CVT_MIN_V_PORCH;
+ double ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * h_period_est / 1000.0);
+ if (ideal_duty_cycle < 20)
+ ideal_duty_cycle = 20;
+ h_blank = floor(total_active_pixels * ideal_duty_cycle /
+ (100.0 - ideal_duty_cycle) /
+ (2 * CELL_GRAN)) * 2 * CELL_GRAN;
+ double total_pixels = total_active_pixels + h_blank;
+ h_sync = floor(total_pixels * 0.08 / CELL_GRAN) * CELL_GRAN;
+ pixel_freq = floor((total_pixels / h_period_est) / clock_step) * clock_step;
+ } else {
+ double h_period_est = ((1000000.0 / v_field_rate_rqd) - CVT_RB_MIN_VBLANK) /
+ (v_lines_rnd + vert_margin * 2);
+ double vbi_lines = floor(CVT_RB_MIN_VBLANK / h_period_est) + 1;
+ double rb_min_vbi = rb_v_fporch + v_sync + CVT_MIN_V_BPORCH;
+ v_blank = vbi_lines < rb_min_vbi ? rb_min_vbi : vbi_lines;
+ double total_v_lines = v_blank + v_lines_rnd + vert_margin * 2 + interlace;
+ if (rb == RB_CVT_V1)
+ v_sync_bp = v_blank - rb_v_fporch;
+ else
+ v_sync_bp = v_sync + CVT_MIN_V_BPORCH;
+ double total_pixels = h_blank + total_active_pixels;
+ double freq = v_field_rate_rqd * total_v_lines * total_pixels * refresh_multiplier;
+ if (rb == RB_CVT_V3)
+ pixel_freq = ceil((freq / 1000000.0) / clock_step) * clock_step;
+ else
+ pixel_freq = floor((freq / 1000000.0) / clock_step) * clock_step;
+ }
+
+ t.vbp = v_sync_bp - v_sync;
+ t.vsync = v_sync;
+ t.vfp = v_blank - t.vbp - t.vsync;
+ t.pixclk_khz = round(1000.0 * pixel_freq);
+ t.hsync = h_sync;
+ if (rb == RB_CVT_V3)
+ t.hfp = 8;
+ else
+ t.hfp = (h_blank / 2.0) - t.hsync;
+ t.hbp = h_blank - t.hfp - t.hsync;
+ t.hborder = hor_margin;
+ t.vborder = vert_margin;
+ t.rb = rb;
+ if (alt && (rb == RB_CVT_V2 || rb == RB_CVT_V3))
+ t.rb |= RB_ALT;
+ t.pos_pol_hsync = t.rb;
+ t.pos_pol_vsync = !t.rb;
+ calc_ratio(&t);
+ return t;
+}
+
+void edid_state::edid_cvt_mode(unsigned refresh, struct timings &t)
+{
+ unsigned hratio = t.hratio;
+ unsigned vratio = t.vratio;
+
+ t = calc_cvt_mode(t.hact, t.vact, refresh, t.rb & ~RB_ALT, t.interlaced,
+ false, t.rb & RB_ALT);
+ t.hratio = hratio;
+ t.vratio = vratio;
+}
diff --git a/data/acer-xv273k-dp b/data/acer-xv273k-dp
new file mode 100644
index 0000000..47c1528
--- /dev/null
+++ b/data/acer-xv273k-dp
Binary files differ
diff --git a/data/acer-xv273k-dp1-tile0 b/data/acer-xv273k-dp1-tile0
new file mode 100644
index 0000000..d93c5d2
--- /dev/null
+++ b/data/acer-xv273k-dp1-tile0
Binary files differ
diff --git a/data/acer-xv273k-dp2-tile1 b/data/acer-xv273k-dp2-tile1
new file mode 100644
index 0000000..8e41faf
--- /dev/null
+++ b/data/acer-xv273k-dp2-tile1
Binary files differ
diff --git a/data/apple-cinemahd-30-dvi b/data/apple-cinemahd-30-dvi
new file mode 100644
index 0000000..be6ae1c
--- /dev/null
+++ b/data/apple-cinemahd-30-dvi
Binary files differ
diff --git a/data/asus-vw246-hdmi b/data/asus-vw246-hdmi
new file mode 100644
index 0000000..e17f33c
--- /dev/null
+++ b/data/asus-vw246-hdmi
Binary files differ
diff --git a/data/dell-2408wfp-dp b/data/dell-2408wfp-dp
new file mode 100644
index 0000000..32ec2f6
--- /dev/null
+++ b/data/dell-2408wfp-dp
Binary files differ
diff --git a/data/dell-3007wfp-dvi b/data/dell-3007wfp-dvi
new file mode 100644
index 0000000..922bad5
--- /dev/null
+++ b/data/dell-3007wfp-dvi
Binary files differ
diff --git a/data/dell-e178wfp-vga b/data/dell-e178wfp-vga
new file mode 100644
index 0000000..7915446
--- /dev/null
+++ b/data/dell-e178wfp-vga
Binary files differ
diff --git a/data/dell-p2415q-dp b/data/dell-p2415q-dp
new file mode 100644
index 0000000..ffc77f0
--- /dev/null
+++ b/data/dell-p2415q-dp
Binary files differ
diff --git a/data/dell-p2415q-hdmi1.4 b/data/dell-p2415q-hdmi1.4
new file mode 100644
index 0000000..ada8d54
--- /dev/null
+++ b/data/dell-p2415q-hdmi1.4
Binary files differ
diff --git a/data/dell-p2415q-hdmi2.0 b/data/dell-p2415q-hdmi2.0
new file mode 100644
index 0000000..d9f3eca
--- /dev/null
+++ b/data/dell-p2415q-hdmi2.0
Binary files differ
diff --git a/data/dell-up2715k-dp b/data/dell-up2715k-dp
new file mode 100644
index 0000000..835aaa5
--- /dev/null
+++ b/data/dell-up2715k-dp
Binary files differ
diff --git a/data/dell-up2715k-dp-switchresx b/data/dell-up2715k-dp-switchresx
new file mode 100644
index 0000000..d0d425a
--- /dev/null
+++ b/data/dell-up2715k-dp-switchresx
Binary files differ
diff --git a/data/dell-up2715k-dp1-tile1 b/data/dell-up2715k-dp1-tile1
new file mode 100644
index 0000000..424394d
--- /dev/null
+++ b/data/dell-up2715k-dp1-tile1
Binary files differ
diff --git a/data/dell-up2715k-dp2-tile0 b/data/dell-up2715k-dp2-tile0
new file mode 100644
index 0000000..c95095f
--- /dev/null
+++ b/data/dell-up2715k-dp2-tile0
Binary files differ
diff --git a/data/dell-up2718q-dp b/data/dell-up2718q-dp
new file mode 100644
index 0000000..9c6667f
--- /dev/null
+++ b/data/dell-up2718q-dp
Binary files differ
diff --git a/data/dell-up3218k-dp1-tile0 b/data/dell-up3218k-dp1-tile0
new file mode 100644
index 0000000..fc8d3d0
--- /dev/null
+++ b/data/dell-up3218k-dp1-tile0
Binary files differ
diff --git a/data/dell-up3218k-dp2-tile1 b/data/dell-up3218k-dp2-tile1
new file mode 100644
index 0000000..35bf693
--- /dev/null
+++ b/data/dell-up3218k-dp2-tile1
Binary files differ
diff --git a/data/elo-4600l-hdmi b/data/elo-4600l-hdmi
new file mode 100644
index 0000000..2a2fa4c
--- /dev/null
+++ b/data/elo-4600l-hdmi
Binary files differ
diff --git a/data/hp-lp2275w-dp b/data/hp-lp2275w-dp
new file mode 100644
index 0000000..6bc442f
--- /dev/null
+++ b/data/hp-lp2275w-dp
Binary files differ
diff --git a/data/hp-lp2480zx-dp b/data/hp-lp2480zx-dp
new file mode 100644
index 0000000..15a8355
--- /dev/null
+++ b/data/hp-lp2480zx-dp
Binary files differ
diff --git a/data/hp-lp2480zx-hdmi b/data/hp-lp2480zx-hdmi
new file mode 100644
index 0000000..92185a3
--- /dev/null
+++ b/data/hp-lp2480zx-hdmi
Binary files differ
diff --git a/data/kogan-kaled24144f-dp b/data/kogan-kaled24144f-dp
new file mode 100644
index 0000000..ce340a6
--- /dev/null
+++ b/data/kogan-kaled24144f-dp
Binary files differ
diff --git a/data/kogan-kaled24144f-hdmi b/data/kogan-kaled24144f-hdmi
new file mode 100644
index 0000000..f3d4a0f
--- /dev/null
+++ b/data/kogan-kaled24144f-hdmi
Binary files differ
diff --git a/data/lg-32ud99-w-dp b/data/lg-32ud99-w-dp
new file mode 100644
index 0000000..b62a6b0
--- /dev/null
+++ b/data/lg-32ud99-w-dp
Binary files differ
diff --git a/data/lg-32ud99-w-hdmi b/data/lg-32ud99-w-hdmi
new file mode 100644
index 0000000..a19ccda
--- /dev/null
+++ b/data/lg-32ud99-w-hdmi
Binary files differ
diff --git a/data/lg-55uh5c-hdmi b/data/lg-55uh5c-hdmi
new file mode 100644
index 0000000..61c70d9
--- /dev/null
+++ b/data/lg-55uh5c-hdmi
Binary files differ
diff --git a/data/lg-75uh5c-hdmi b/data/lg-75uh5c-hdmi
new file mode 100644
index 0000000..ec96ddd
--- /dev/null
+++ b/data/lg-75uh5c-hdmi
Binary files differ
diff --git a/data/lg-88bh7d-hdmi b/data/lg-88bh7d-hdmi
new file mode 100644
index 0000000..0d411ae
--- /dev/null
+++ b/data/lg-88bh7d-hdmi
Binary files differ
diff --git a/data/lg-l206wu-usb-dvi b/data/lg-l206wu-usb-dvi
new file mode 100644
index 0000000..8a2681f
--- /dev/null
+++ b/data/lg-l206wu-usb-dvi
Binary files differ
diff --git a/data/lg-oled-c9-hdmi b/data/lg-oled-c9-hdmi
new file mode 100644
index 0000000..e46aa67
--- /dev/null
+++ b/data/lg-oled-c9-hdmi
Binary files differ
diff --git a/data/lg-oled-cx-hdmi b/data/lg-oled-cx-hdmi
new file mode 100644
index 0000000..a539057
--- /dev/null
+++ b/data/lg-oled-cx-hdmi
Binary files differ
diff --git a/data/lg-oled55b6v-hdmi b/data/lg-oled55b6v-hdmi
new file mode 100644
index 0000000..baebd80
--- /dev/null
+++ b/data/lg-oled55b6v-hdmi
Binary files differ
diff --git a/data/lg-oled55b6v-hdmi-denon-avr-x2300w-hdmi b/data/lg-oled55b6v-hdmi-denon-avr-x2300w-hdmi
new file mode 100644
index 0000000..9300a98
--- /dev/null
+++ b/data/lg-oled55b6v-hdmi-denon-avr-x2300w-hdmi
Binary files differ
diff --git a/data/lg-oled55e6v-hdmi b/data/lg-oled55e6v-hdmi
new file mode 100644
index 0000000..7524bef
--- /dev/null
+++ b/data/lg-oled55e6v-hdmi
Binary files differ
diff --git a/data/lg-ultrafine-5k-v2-thunderbolt-dp1-tile0 b/data/lg-ultrafine-5k-v2-thunderbolt-dp1-tile0
new file mode 100644
index 0000000..13ab607
--- /dev/null
+++ b/data/lg-ultrafine-5k-v2-thunderbolt-dp1-tile0
Binary files differ
diff --git a/data/lg-ultrafine-5k-v2-thunderbolt-dp2-tile1 b/data/lg-ultrafine-5k-v2-thunderbolt-dp2-tile1
new file mode 100644
index 0000000..abc93a4
--- /dev/null
+++ b/data/lg-ultrafine-5k-v2-thunderbolt-dp2-tile1
Binary files differ
diff --git a/data/matrox-triplehead2go-dvi b/data/matrox-triplehead2go-dvi
new file mode 100644
index 0000000..891471c
--- /dev/null
+++ b/data/matrox-triplehead2go-dvi
Binary files differ
diff --git a/data/oculus-rift-dk1-hdmi b/data/oculus-rift-dk1-hdmi
new file mode 100644
index 0000000..a677d6a
--- /dev/null
+++ b/data/oculus-rift-dk1-hdmi
Binary files differ
diff --git a/data/samsung-qm65h-hdmi b/data/samsung-qm65h-hdmi
new file mode 100644
index 0000000..a3c450a
--- /dev/null
+++ b/data/samsung-qm65h-hdmi
Binary files differ
diff --git a/data/samsung-qp82r-8k-hdmi4 b/data/samsung-qp82r-8k-hdmi4
new file mode 100644
index 0000000..6487a9f
--- /dev/null
+++ b/data/samsung-qp82r-8k-hdmi4
Binary files differ
diff --git a/data/samsung-ue40d8000yu-hmdi b/data/samsung-ue40d8000yu-hmdi
new file mode 100644
index 0000000..ba87cb0
--- /dev/null
+++ b/data/samsung-ue40d8000yu-hmdi
Binary files differ
diff --git a/data/samsung-ue40ku6070-hdmi b/data/samsung-ue40ku6070-hdmi
new file mode 100644
index 0000000..92dfe51
--- /dev/null
+++ b/data/samsung-ue40ku6070-hdmi
Binary files differ
diff --git a/data/samsung-ue48ju7090-hdmi b/data/samsung-ue48ju7090-hdmi
new file mode 100644
index 0000000..521ab8f
--- /dev/null
+++ b/data/samsung-ue48ju7090-hdmi
Binary files differ
diff --git a/data/samsung-ue49ks8005-hdmi b/data/samsung-ue49ks8005-hdmi
new file mode 100644
index 0000000..56084ee
--- /dev/null
+++ b/data/samsung-ue49ks8005-hdmi
Binary files differ
diff --git a/data/samsung-ue55ks-hdmi b/data/samsung-ue55ks-hdmi
new file mode 100644
index 0000000..e8247b6
--- /dev/null
+++ b/data/samsung-ue55ks-hdmi
Binary files differ
diff --git a/data/sharp-aquos-hdmi b/data/sharp-aquos-hdmi
new file mode 100644
index 0000000..e2a37bb
--- /dev/null
+++ b/data/sharp-aquos-hdmi
Binary files differ
diff --git a/data/sharp-pn465-dvi b/data/sharp-pn465-dvi
new file mode 100644
index 0000000..bd6c7f8
--- /dev/null
+++ b/data/sharp-pn465-dvi
Binary files differ
diff --git a/data/skyworth-50e780u-hdmi b/data/skyworth-50e780u-hdmi
new file mode 100644
index 0000000..b618a6f
--- /dev/null
+++ b/data/skyworth-50e780u-hdmi
Binary files differ
diff --git a/data/sony-kd55xd-hdmi b/data/sony-kd55xd-hdmi
new file mode 100644
index 0000000..3f203ae
--- /dev/null
+++ b/data/sony-kd55xd-hdmi
Binary files differ
diff --git a/edid-decode.1 b/edid-decode.1
new file mode 100644
index 0000000..ecd7fe3
--- /dev/null
+++ b/edid-decode.1
@@ -0,0 +1,336 @@
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH edid-decode 1
+.SH NAME
+edid-decode - Decode EDID data in human-readable format
+.SH SYNOPSIS
+.B edid-decode <options> [in [out]]
+.SH DESCRIPTION
+.B edid-decode
+decodes EDID monitor description data in human-readable format.
+If [in] is not given, or [in] is '-', then the EDID will be read from
+standard input. If [out] is given then the EDID that was read from [in]
+is written to [out] or to standard output if [out] is '-'. By default
+the output is written as a hex dump when writing to standard output or
+a raw EDID if written to a file.
+
+If [out] is given then edid-decode only does the conversion, it will
+skip the decoding step.
+.PP
+Input files may be raw binaries or ASCII text. ASCII input is scanned for
+hex dumps; heuristics are included to search for hexdumps in
+.B edid-decode(1)
+output (as long as the initial hex dump was included),
+.B xrandr(1)
+property output and
+.B Xorg(1)
+log file formats, otherwise the data is treated as a raw hexdump. EDID blocks
+for connected monitors can be found in
+.B /sys/class/drm/*/edid
+on modern Linux systems with kernel modesetting support.
+
+All timings are shown in a short format, for example:
+
+ VIC 16: 1920x1080 60.000 Hz 16:9 67.500 kHz 148.500 MHz (native)
+ VIC 5: 1920x1080i 60.000 Hz 16:9 33.750 kHz 74.250 MHz
+ VIC 39: 1920x1080i 50.000 Hz 16:9 31.250 kHz 72.000 MHz
+
+Each format starts with a timings type prefix, the resolution, an optional
+interlaced indicator ('i'), the frame rate (field rate for interlaced formats),
+the picture aspect ratio, the horizontal frequency, the pixelclock
+frequency and optionally additional flags between parenthesis.
+
+Note that for interlaced formats the frame height is given, not the field
+height. So each field in a 1920x1080i format has 540 lines.
+
+Detailed timings have another 2-3 lines of data:
+
+ VIC 16: 1920x1080 60.000 Hz 16:9 67.500 kHz 148.500 MHz (native)
+ Hfront 88 Hsync 44 Hback 148 Hpol P
+ Vfront 4 Vsync 5 Vback 36 Vpol P
+ VIC 5: 1920x1080i 60.000 Hz 16:9 33.750 kHz 74.250 MHz
+ Hfront 88 Hsync 44 Hback 148 Hpol P
+ Vfront 2 Vsync 5 Vback 15 Vpol P Vfront +0.5 Odd Field
+ Vfront 2 Vsync 5 Vback 15 Vpol P Vback +0.5 Even Field
+ VIC 39: 1920x1080i 50.000 Hz 16:9 31.250 kHz 72.000 MHz
+ Hfront 32 Hsync 168 Hback 184 Hpol P
+ Vfront 23 Vsync 5 Vback 57 Vpol N Both Fields
+
+These describe the horizontal and vertical front porch, sync, backporch
+and sync polarity values. For interlaced formats there are two lines
+for the vertical information: one for the Odd Field (aka Field 1) and
+one for the Even Field (aka Field 2). The vertical front porch of the
+Odd Field is actually 2.5 (hence the 'Vfront +0.5' at the end of the
+line), and the back porch of the Even Field is actually 15.5 (hence
+the 'Vback +0.5' at the end of the line).
+
+There is a special 'VIC 39' interlaced format where both fields have
+the same vertical timings, in that case this is marked with 'Both Fields'.
+
+The following timing types can be shown:
+
+.RS
+.TP
+DMT #: Discrete Monitor Timing (see DMT 1.3 standard). The number is the DMT ID in hexadecimal.
+.TP
+CVT: Coordinated Video Timings (formula-based, see CVT 1.2 standard)
+.TP
+GTF: Generalized Timing Formula (formula-based, see GTF 1.1 standard)
+.TP
+IBM: Old IBM Timings
+.TP
+Apple: Old Apple Timings
+.TP
+VIC #: Video Identification Code (see CTA-861 standard). The number is the actual
+VIC code.
+.TP
+HDMI VIC #: HDMI-specific Video Identification Code (see HDMI 2.1 standard). The number
+is the actual HDMI VIC code.
+.TP
+DTD #: Detailed Timings Descriptor (see EDID standard). Also used for
+DisplayID Video Timing Modes Types I, II, VI and VII. The number denotes that
+this is the Nth DTD in the EDID.
+.RE
+
+By default DTDs are shown in the long format while others are just shown in
+the short format. With the option \fB\-\-short\-timings\fR all timings are
+shown in short format only. With the option \fB\-\-long\-timings\fR all timings
+are shown in long format.
+
+Alternate formats for long timings can be chosen via the \fB\-\-xmodeline\fR or
+\fB\-\-fbmode\fR options.
+
+.SH STANDARDS
+.TP
+The following EDID standards are supported by edid-decode:
+.RS
+.TP
+EDID 1.3: VESA Enhanced Extended Display Identication Data Standard, Release A, Revision 1
+.TP
+EDID 1.4: VESA Enhanced Extended Display Identication Data Standard, Release A, Revision 2
+.TP
+DisplayID 1.3: VESA Display Identification Data (DisplayID) Standard, Version 1.3
+.TP
+DisplayID 2.0: VESA DisplayID Standard, Version 2.0
+.TP
+DisplayID 2.0: VESA DisplayID v2.0 Errata E7
+.TP
+DI-EXT: VESA Display Information Extension Block Standard, Release A
+.TP
+LS-EXT: VESA Enhanced EDID Localized String Extension Standard, Release A
+.TP
+VTB-EXT: VESA Video Timing Block Extension Data Standard, Release A
+.TP
+DTCDB: VESA Display Transfer Characteristics Data Block Standard, Version 1.0
+.TP
+DDDB: VESA Display Device Data Block (DDDB) Standard, Version 1
+.TP
+HDMI 1.4b: High-Definition Multimedia Interface, Version 1.4b
+.TP
+HDMI 2.1: High-Definition Multimedia Interface, Version 2.1
+.TP
+HDMI 2.1: Amendment A1 to HDMI Specification Version 2.1
+.TP
+CTA-861-H: A DTV Profile for Uncompressed High Speed Digital Interfaces
+.TP
+SPWG Notebook Panel Specification, Version 3.5
+.TP
+EPI Embedded Panel Interface, Revision 1.0
+.TP
+Microsoft EDID extension for head-mounted and specialized monitors, Version 3
+.RE
+
+.TP
+The following related standards are also used by edid-decode:
+.RS
+.TP
+DMT 1.3: VESA and Industry Standards and Guidelines for Computer Display Monitor Timing (DMT), Version 1.0, Rev. 13
+.TP
+CVT 1.2: VESA Coordinated Video Timings (CVT) Standard, Version 1.2
+.TP
+CVT 1.2: VESA CVT v1.2 Errata E1
+.TP
+GTF 1.1: VESA Generalized Timing Formula Standard, Version: 1.1
+.RE
+
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Prints the help message.
+.TP
+\fB\-o\fR, \fB\-\-output\-format\fR \fI<fmt>\fR
+If [out] is specified, then write the EDID in format \fI<fmt>\fR.
+
+The output format can be one of:
+.br
+hex: hex numbers in ascii text (default for stdout)
+.br
+raw: binary data (default unless writing to stdout)
+.br
+carray: c-program struct
+.br
+xml: XML data
+.TP
+\fB\-c\fR, \fB\-\-check\fR
+Check if the EDID conforms to the standards. Warnings and failures are
+reported at the end.
+.TP
+\fB\-C\fR, \fB\-\-check\-inline\fR
+Check if the EDID conforms to the standards. Warnings and failures are
+reported as they happen.
+.TP
+\fB\-n\fR, \fB\-\-native\-timings\fR
+Report the native timings at the end. There may be multiple native timing reports
+depending on whether the Source only parses Block 0 (e.g. DVI outputs) or Block 0
+and the CTA-861 Extension Blocks (HDMI).
+.TP
+\fB\-p\fR, \fB\-\-preferred\-timings\fR
+Report the preferred timings at the end. There may be multiple native timing reports
+depending on whether the Source only parses Block 0 (e.g. DVI outputs), or Block 0
+and the CTA-861 Extension Blocks (HDMI), or Block 0 and the DisplayID Extension Blocks
+(typical for DisplayPort).
+.TP
+\fB\-P\fR, \fB\-\-physical\-address\fR
+Just report the HDMI Source Physical Address and nothing else. Reports f.f.f.f
+if the EDID could not be parsed, or if there was no CTA-861 Vendor-Specific Data Block
+with OUI 00-0C-03. Otherwise it reports the Source Physical Address as provided
+in that Data Block. This can be used as input to HDMI CEC utilities such as the
+linux cec-ctl(1) utility.
+.TP
+\fB\-S\fR, \fB\-\-short\-timings\fR
+Report all video timings in a short format.
+.TP
+\fB\-L\fR, \fB\-\-long\-timings\fR
+Report all video timings in a long format.
+.TP
+\fB\-X\fR, \fB\-\-xmodeline\fR
+Report all long video timings in the ModeLine format as defined in xorg.conf(5).
+This ModeLine can be used in the xorg.conf file or passed to xrandr(1) with the
+xrandr \fB\-\-newmode\fR option.
+.TP
+\fB\-F\fR, \fB\-\-fbmode\fR
+Report all long video timings in the video mode format as defined in fb.modes(5).
+.TP
+\fB\-V\fR, \fB\-\-v4l2\-timings\fR
+Report all long video timings in the video mode format as defined in the linux header v4l2-dv-timings.h
+for use with the V4L2 VIDIOC_S_DV_TIMINGS ioctl.
+.TP
+\fB\-s\fR, \fB\-\-skip\-hex\-dump\fR
+Skip the initial hex dump of the EDID.
+.TP
+\fB\-H\fR, \fB\-\-only\-hex\-dump\fR
+Only show the hex dump of the EDID, then exit.
+.TP
+\fB\-\-skip\-sha\fR
+Don't show the SHA hash. Normally edid-decode will show the SHA, i.e. the
+hash of the git commit used to compile edid-decode. This uniquely identifies
+the version of edid-decode that is used to generate the warnings and
+failures. But it will also change the output of edid-decode for every new commit
+in the git repository, even if nothing else changed in the edid-decode output.
+Use this option to avoid including the SHA in the edid-decode output.
+.TP
+\fB\-\-hide\-serial\-numbers\fR
+Replace any serial numbers in the human readable output by '...'.
+Note that they are still easily extracted from the EDID hex dump at
+the start.
+.TP
+\fB\-\-version\fR
+Show the SHA hash and the last commit date.
+
+.SH TIMING OPTIONS
+The following options report the timings for DMT, VIC and HDMI VIC codes and
+calculate the timings for CVT or GTF timings, based on the given parameters.
+The EDID will not be shown, although it can be used with the \fB\-\-gtf\fR
+option in order to read the secondary curve parameters.
+.TP
+\fB\-\-std\fR \fI<byte1>\fR,\fI<byte2>\fR
+Show the standard timing represented by these two bytes.
+.TP
+\fB\-\-dmt\fR \fI<dmt>\fR
+Show the timings for the DMT with the given DMT ID.
+.TP
+\fB\-\-vic\fR \fI<vic>\fR
+Show the timings for this VIC.
+.TP
+\fB\-\-hdmi\-vic\fR \fI<hdmivic>\fR
+Show the timings for this HDMI VIC.
+.TP
+\fB\-\-cvt\fR \fBw\fR=\fI<width>\fR,\fBh\fR=\fI<height>\fR,\fBfps\fR=\fI<fps>\fR[,\fBrb\fR=\fI<rb>\fR][,\fBinterlaced\fR][,\fBoverscan\fR][,\fBalt\fR][,\fBhblank\fR=\fI<hblank>\fR]
+.br
+Calculate the CVT timings for the given format.
+
+\fI<width>\fR is the width in pixels, \fI<height>\fR is the frame (not field!) height in lines.
+.br
+\fI<fps>\fR is frames per second for progressive timings and fields per second for interlaced timings.
+.br
+\fI<rb>\fR can be 0 (no reduced blanking, default), or 1-3 for the reduced blanking version.
+.br
+If \fBinterlaced\fR is given, then this is an interlaced format.
+.br
+If \fBoverscan\fR is given, then this is an overscanned format. I.e., margins are required.
+.br
+If \fBalt\fR is given and \fI<rb>\fR=2, then report the timings
+optimized for video: 1000 / 1001 * \fI<fps>\fR.
+.br
+If \fBalt\fR is given and \fI<rb>\fR=3, then the horizontal blanking
+is 160 instead of 80 pixels.
+.br
+If \fBhblank\fR is given and \fI<rb>\fR=3, then the horizontal blanking
+is \fI<hblank>\fR pixels (range of 80-200 and divisible by 8), overriding \fBalt\fR.
+.TP
+\fB\-\-gtf\fR \fBw\fR=\fI<width>\fR,\fBh\fR=\fI<height>\fR[,\fBfps\fR=\fI<fps>\fR][,\fBhorfreq\fR=\fI<horfreq>\fR][,\fBpixclk\fR=\fI<pixclk>\fR]
+[,\fBinterlaced\fR][,\fBoverscan\fR][,\fBsecondary\fR][,\fBC\fR=\fI<c>\fR][,\fBM\fR=\fI<m>\fR][,\fBK\fR=\fI<k>\fR][,\fBJ\fR=\fI<j>\fR]
+.br
+Calculate the GTF timings for the given format.
+
+\fI<width>\fR is the width in pixels, \fI<height>\fR is the frame (not field!) height in lines.
+.br
+\fI<fps>\fR is frames per second for progressive timings and fields per second for interlaced timings.
+.br
+\fI<horfreq>\fR is the horizontal frequency in kHz.
+.br
+\fI<pixclk>\fR is the pixel clock frequency in MHz.
+Only one of \fBfps\fR, \fBhorfreq\fR or \fBpixclk\fR must be given.
+.br
+If \fBinterlaced\fR is given, then this is an interlaced format.
+.br
+If \fBoverscan\fR is given, then this is an overscanned format. I.e., margins are required.
+.br
+If \fBsecondary\fR is given, then the secondary GTF is used for
+reduced blanking, where \fI<c>\fR, \fI<m>\fR, \fI<k>\fR and \fI<j>\fR are parameters
+for the secondary curve. If none of the secondary curve parameters
+were set, and an EDID file is passed as command line option, then the
+secondary curve parameters are read from that EDID.
+.br
+The default secondary curve parameters are 40 for \fI<c>\fR, 600 for \fI<m>\fR,
+128 for \fI<k>\fR and 20 for \fI<j>\fR.
+These values correspond to the normal curve that GTF uses.
+.TP
+\fB\-\-list\-established\-timings\fR
+List all known Established Timings.
+.TP
+\fB\-\-list\-dmts\fR
+List all known DMTs.
+.TP
+\fB\-\-list\-vics\fR
+List all known VICs.
+.TP
+\fB\-\-list\-hdmi\-vics\fR
+List all known HDMI VICs.
+
+.PP
+.SH NOTES
+Not all fields are decoded, or decoded completely.
+.B edid-decode
+does attempt to validate its input against the relevant standards, but its
+opinions have not been double-checked with the relevant standards bodies,
+so they may be wrong. Do not rely on the output format, as it will likely
+change in future versions of the tool as additional fields and extensions are
+added.
+.SH "SEE ALSO"
+Xorg(1), xrandr(1), cec-ctl(1), xorg.conf(5), fb.modes(5)
+.SH AUTHORS
+edid-decode was written by Adam Jackson, with contributions from Eric
+Anholt, Damien Lespiau, Hans Verkuil and others. For complete history and the
+latest version, see
+.B http://git.linuxtv.org/cgit.cgi/edid-decode.git
diff --git a/edid-decode.cpp b/edid-decode.cpp
new file mode 100644
index 0000000..de70242
--- /dev/null
+++ b/edid-decode.cpp
@@ -0,0 +1,1794 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "edid-decode.h"
+
+#define STR(x) #x
+#define STRING(x) STR(x)
+
+static edid_state state;
+
+static unsigned char edid[EDID_PAGE_SIZE * EDID_MAX_BLOCKS];
+static bool odd_hex_digits;
+
+enum output_format {
+ OUT_FMT_DEFAULT,
+ OUT_FMT_HEX,
+ OUT_FMT_RAW,
+ OUT_FMT_CARRAY,
+ OUT_FMT_XML,
+};
+
+/*
+ * Options
+ * Please keep in alphabetical order of the short option.
+ * That makes it easier to see which options are still free.
+ */
+enum Option {
+ OptCheck = 'c',
+ OptCheckInline = 'C',
+ OptFBModeTimings = 'F',
+ OptHelp = 'h',
+ OptOnlyHexDump = 'H',
+ OptLongTimings = 'L',
+ OptNativeTimings = 'n',
+ OptOutputFormat = 'o',
+ OptPreferredTimings = 'p',
+ OptPhysicalAddress = 'P',
+ OptSkipHexDump = 's',
+ OptShortTimings = 'S',
+ OptV4L2Timings = 'V',
+ OptXModeLineTimings = 'X',
+ OptSkipSHA = 128,
+ OptHideSerialNumbers,
+ OptVersion,
+ OptSTD,
+ OptDMT,
+ OptVIC,
+ OptHDMIVIC,
+ OptCVT,
+ OptGTF,
+ OptListEstTimings,
+ OptListDMTs,
+ OptListVICs,
+ OptListHDMIVICs,
+ OptLast = 256
+};
+
+static char options[OptLast];
+
+static struct option long_options[] = {
+ { "help", no_argument, 0, OptHelp },
+ { "output-format", required_argument, 0, OptOutputFormat },
+ { "native-timings", no_argument, 0, OptNativeTimings },
+ { "preferred-timings", no_argument, 0, OptPreferredTimings },
+ { "physical-address", no_argument, 0, OptPhysicalAddress },
+ { "skip-hex-dump", no_argument, 0, OptSkipHexDump },
+ { "only-hex-dump", no_argument, 0, OptOnlyHexDump },
+ { "skip-sha", no_argument, 0, OptSkipSHA },
+ { "hide-serial-numbers", no_argument, 0, OptHideSerialNumbers },
+ { "version", no_argument, 0, OptVersion },
+ { "check-inline", no_argument, 0, OptCheckInline },
+ { "check", no_argument, 0, OptCheck },
+ { "short-timings", no_argument, 0, OptShortTimings },
+ { "long-timings", no_argument, 0, OptLongTimings },
+ { "xmodeline", no_argument, 0, OptXModeLineTimings },
+ { "fbmode", no_argument, 0, OptFBModeTimings },
+ { "v4l2-timings", no_argument, 0, OptV4L2Timings },
+ { "std", required_argument, 0, OptSTD },
+ { "dmt", required_argument, 0, OptDMT },
+ { "vic", required_argument, 0, OptVIC },
+ { "hdmi-vic", required_argument, 0, OptHDMIVIC },
+ { "cvt", required_argument, 0, OptCVT },
+ { "gtf", required_argument, 0, OptGTF },
+ { "list-established-timings", no_argument, 0, OptListEstTimings },
+ { "list-dmts", no_argument, 0, OptListDMTs },
+ { "list-vics", no_argument, 0, OptListVICs },
+ { "list-hdmi-vics", no_argument, 0, OptListHDMIVICs },
+ { 0, 0, 0, 0 }
+};
+
+static void usage(void)
+{
+ printf("Usage: edid-decode <options> [in [out]]\n"
+ " [in] EDID file to parse. Read from standard input if none given\n"
+ " or if the input filename is '-'.\n"
+ " [out] Output the read EDID to this file. Write to standard output\n"
+ " if the output filename is '-'.\n"
+ "\nOptions:\n"
+ " -o, --output-format <fmt>\n"
+ " If [out] is specified, then write the EDID in this format\n"
+ " <fmt> is one of:\n"
+ " hex: hex numbers in ascii text (default for stdout)\n"
+ " raw: binary data (default unless writing to stdout)\n"
+ " carray: c-program struct\n"
+ " xml: XML data\n"
+ " -c, --check Check if the EDID conforms to the standards, failures and\n"
+ " warnings are reported at the end.\n"
+ " -C, --check-inline Check if the EDID conforms to the standards, failures and\n"
+ " warnings are reported inline.\n"
+ " -n, --native-timings Report the native timings.\n"
+ " -p, --preferred-timings Report the preferred timings.\n"
+ " -P, --physical-address Only report the CEC physical address.\n"
+ " -S, --short-timings Report all video timings in a short format.\n"
+ " -L, --long-timings Report all video timings in a long format.\n"
+ " -X, --xmodeline Report all long video timings in Xorg.conf format.\n"
+ " -F, --fbmode Report all long video timings in fb.modes format.\n"
+ " -V, --v4l2-timings Report all long video timings in v4l2-dv-timings.h format.\n"
+ " -s, --skip-hex-dump Skip the initial hex dump of the EDID.\n"
+ " -H, --only-hex-dump Only output the hex dump of the EDID.\n"
+ " --skip-sha Skip the SHA report.\n"
+ " --hide-serial-numbers Replace serial numbers with '...'\n"
+ " --version show the edid-decode version (SHA)\n"
+ " --std <byte1>,<byte2> Show the standard timing represented by these two bytes.\n"
+ " --dmt <dmt> Show the timings for the DMT with the given DMT ID.\n"
+ " --vic <vic> Show the timings for this VIC.\n"
+ " --hdmi-vic <hdmivic> Show the timings for this HDMI VIC.\n"
+ " --cvt w=<width>,h=<height>,fps=<fps>[,rb=<rb>][,interlaced][,overscan][,alt][,hblank=<hblank]\n"
+ " Calculate the CVT timings for the given format.\n"
+ " <fps> is frames per second for progressive timings,\n"
+ " or fields per second for interlaced timings.\n"
+ " <rb> can be 0 (no reduced blanking, default), or\n"
+ " 1-3 for the reduced blanking version.\n"
+ " If 'interlaced' is given, then this is an interlaced format.\n"
+ " If 'overscan' is given, then this is an overscanned format.\n"
+ " If 'alt' is given and <rb>=2, then report the timings\n"
+ " optimized for video: 1000 / 1001 * <fps>.\n"
+ " If 'alt' is given and <rb>=3, then the horizontal blanking\n"
+ " is 160 instead of 80 pixels.\n"
+ " If 'hblank' is given and <rb>=3, then the horizontal blanking\n"
+ " is <hblank> pixels (range of 80-200), overriding 'alt'.\n"
+ " --gtf w=<width>,h=<height>[,fps=<fps>][,horfreq=<horfreq>][,pixclk=<pixclk>][,interlaced]\n"
+ " [,overscan][,secondary][,C=<c>][,M=<m>][,K=<k>][,J=<j>]\n"
+ " Calculate the GTF timings for the given format.\n"
+ " <fps> is frames per second for progressive timings,\n"
+ " or fields per second for interlaced timings.\n"
+ " <horfreq> is the horizontal frequency in kHz.\n"
+ " <pixclk> is the pixel clock frequency in MHz.\n"
+ " Only one of fps, horfreq or pixclk must be given.\n"
+ " If 'interlaced' is given, then this is an interlaced format.\n"
+ " If 'overscan' is given, then this is an overscanned format.\n"
+ " If 'secondary' is given, then the secondary GTF is used for\n"
+ " reduced blanking, where <c>, <m>, <k> and <j> are parameters\n"
+ " for the secondary curve.\n"
+ " --list-established-timings List all known Established Timings.\n"
+ " --list-dmts List all known DMTs.\n"
+ " --list-vics List all known VICs.\n"
+ " --list-hdmi-vics List all known HDMI VICs.\n"
+ " -h, --help Display this help message.\n");
+}
+
+static std::string s_msgs[EDID_MAX_BLOCKS + 1][2];
+
+void msg(bool is_warn, const char *fmt, ...)
+{
+ char buf[1024] = "";
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ if (is_warn)
+ state.warnings++;
+ else
+ state.failures++;
+ if (state.data_block.empty())
+ s_msgs[state.block_nr][is_warn] += std::string(" ") + buf;
+ else
+ s_msgs[state.block_nr][is_warn] += " " + state.data_block + ": " + buf;
+
+ if (options[OptCheckInline])
+ printf("%s: %s", is_warn ? "WARN" : "FAIL", buf);
+}
+
+static void show_msgs(bool is_warn)
+{
+ printf("\n%s:\n\n", is_warn ? "Warnings" : "Failures");
+ for (unsigned i = 0; i < state.num_blocks; i++) {
+ if (s_msgs[i][is_warn].empty())
+ continue;
+ printf("Block %u, %s:\n%s",
+ i, block_name(edid[i * EDID_PAGE_SIZE]).c_str(),
+ s_msgs[i][is_warn].c_str());
+ }
+ if (s_msgs[EDID_MAX_BLOCKS][is_warn].empty())
+ return;
+ printf("EDID:\n%s",
+ s_msgs[EDID_MAX_BLOCKS][is_warn].c_str());
+}
+
+
+void do_checksum(const char *prefix, const unsigned char *x, size_t len)
+{
+ unsigned char check = x[len - 1];
+ unsigned char sum = 0;
+ unsigned i;
+
+ printf("%sChecksum: 0x%02hhx", prefix, check);
+
+ for (i = 0; i < len-1; i++)
+ sum += x[i];
+
+ if ((unsigned char)(check + sum) != 0) {
+ printf(" (should be 0x%02x)\n", -sum & 0xff);
+ fail("Invalid checksum 0x%02x (should be 0x%02x).\n",
+ check, -sum & 0xff);
+ return;
+ }
+ printf("\n");
+}
+
+static unsigned gcd(unsigned a, unsigned b)
+{
+ while (b) {
+ unsigned t = b;
+
+ b = a % b;
+ a = t;
+ }
+ return a;
+}
+
+void calc_ratio(struct timings *t)
+{
+ unsigned d = gcd(t->hact, t->vact);
+
+ if (d == 0) {
+ t->hratio = t->vratio = 0;
+ return;
+ }
+ t->hratio = t->hact / d;
+ t->vratio = t->vact / d;
+}
+
+std::string edid_state::dtd_type(unsigned cnt)
+{
+ unsigned len = std::to_string(cta.preparsed_total_dtds).length();
+ char buf[16];
+ sprintf(buf, "DTD %*u", len, cnt);
+ return buf;
+}
+
+bool edid_state::match_timings(const timings &t1, const timings &t2)
+{
+ if (t1.hact != t2.hact ||
+ t1.vact != t2.vact ||
+ t1.rb != t2.rb ||
+ t1.interlaced != t2.interlaced ||
+ t1.hfp != t2.hfp ||
+ t1.hbp != t2.hbp ||
+ t1.hsync != t2.hsync ||
+ t1.pos_pol_hsync != t2.pos_pol_hsync ||
+ t1.hratio != t2.hratio ||
+ t1.vfp != t2.vfp ||
+ t1.vbp != t2.vbp ||
+ t1.vsync != t2.vsync ||
+ t1.pos_pol_vsync != t2.pos_pol_vsync ||
+ t1.vratio != t2.vratio ||
+ t1.pixclk_khz != t2.pixclk_khz)
+ return false;
+ return true;
+}
+
+static void or_str(std::string &s, const std::string &flag, unsigned &num_flags)
+{
+ if (!num_flags)
+ s = flag;
+ else if (num_flags % 2 == 0)
+ s = s + " | \\\n\t\t" + flag;
+ else
+ s = s + " | " + flag;
+ num_flags++;
+}
+
+/*
+ * Return true if the timings are a close, but not identical,
+ * match. The only differences allowed are polarities and
+ * porches and syncs, provided the total blanking remains the
+ * same.
+ */
+bool timings_close_match(const timings &t1, const timings &t2)
+{
+ // We don't want to deal with borders, you're on your own
+ // if you are using those.
+ if (t1.hborder || t1.vborder ||
+ t2.hborder || t2.vborder)
+ return false;
+ if (t1.hact != t2.hact || t1.vact != t2.vact ||
+ t1.interlaced != t2.interlaced ||
+ t1.pixclk_khz != t2.pixclk_khz ||
+ t1.hfp + t1.hsync + t1.hbp != t2.hfp + t2.hsync + t2.hbp ||
+ t1.vfp + t1.vsync + t1.vbp != t2.vfp + t2.vsync + t2.vbp)
+ return false;
+ if (t1.hfp == t2.hfp &&
+ t1.hsync == t2.hsync &&
+ t1.hbp == t2.hbp &&
+ t1.pos_pol_hsync == t2.pos_pol_hsync &&
+ t1.vfp == t2.vfp &&
+ t1.vsync == t2.vsync &&
+ t1.vbp == t2.vbp &&
+ t1.pos_pol_vsync == t2.pos_pol_vsync)
+ return false;
+ return true;
+}
+
+static void print_modeline(unsigned indent, const struct timings *t, double refresh)
+{
+ unsigned offset = (!t->even_vtotal && t->interlaced) ? 1 : 0;
+ unsigned hfp = t->hborder + t->hfp;
+ unsigned hbp = t->hborder + t->hbp;
+ unsigned vfp = t->vborder + t->vfp;
+ unsigned vbp = t->vborder + t->vbp;
+
+ printf("%*sModeline \"%ux%u_%.2f%s\" %.3f %u %u %u %u %u %u %u %u %cHSync",
+ indent, "",
+ t->hact, t->vact, refresh,
+ t->interlaced ? "i" : "", t->pixclk_khz / 1000.0,
+ t->hact, t->hact + hfp, t->hact + hfp + t->hsync,
+ t->hact + hfp + t->hsync + hbp,
+ t->vact, t->vact + vfp, t->vact + vfp + t->vsync,
+ t->vact + vfp + t->vsync + vbp + offset,
+ t->pos_pol_hsync ? '+' : '-');
+ if (!t->no_pol_vsync)
+ printf(" %cVSync", t->pos_pol_vsync ? '+' : '-');
+ if (t->interlaced)
+ printf(" Interlace");
+ printf("\n");
+}
+
+static void print_fbmode(unsigned indent, const struct timings *t,
+ double refresh, double hor_freq_khz)
+{
+ printf("%*smode \"%ux%u-%u%s\"\n",
+ indent, "",
+ t->hact, t->vact,
+ (unsigned)(0.5 + (t->interlaced ? refresh / 2.0 : refresh)),
+ t->interlaced ? "-lace" : "");
+ printf("%*s# D: %.2f MHz, H: %.3f kHz, V: %.2f Hz\n",
+ indent + 8, "",
+ t->pixclk_khz / 1000.0, hor_freq_khz, refresh);
+ printf("%*sgeometry %u %u %u %u 32\n",
+ indent + 8, "",
+ t->hact, t->vact, t->hact, t->vact);
+ unsigned mult = t->interlaced ? 2 : 1;
+ unsigned offset = !t->even_vtotal && t->interlaced;
+ unsigned hfp = t->hborder + t->hfp;
+ unsigned hbp = t->hborder + t->hbp;
+ unsigned vfp = t->vborder + t->vfp;
+ unsigned vbp = t->vborder + t->vbp;
+ printf("%*stimings %llu %d %d %d %u %u %u\n",
+ indent + 8, "",
+ (unsigned long long)(1000000000.0 / (double)(t->pixclk_khz) + 0.5),
+ hbp, hfp, mult * vbp, mult * vfp + offset, t->hsync, mult * t->vsync);
+ if (t->interlaced)
+ printf("%*slaced true\n", indent + 8, "");
+ if (t->pos_pol_hsync)
+ printf("%*shsync high\n", indent + 8, "");
+ if (t->pos_pol_vsync)
+ printf("%*svsync high\n", indent + 8, "");
+ printf("%*sendmode\n", indent, "");
+}
+
+static void print_v4l2_timing(const struct timings *t,
+ double refresh, const char *type)
+{
+ printf("\t#define V4L2_DV_BT_%uX%u%c%u_%02u { \\\n",
+ t->hact, t->vact, t->interlaced ? 'I' : 'P',
+ (unsigned)refresh, (unsigned)(0.5 + 100.0 * (refresh - (unsigned)refresh)));
+ printf("\t\t.type = V4L2_DV_BT_656_1120, \\\n");
+ printf("\t\tV4L2_INIT_BT_TIMINGS(%u, %u, %u, ",
+ t->hact, t->vact, t->interlaced);
+ if (!t->pos_pol_hsync && !t->pos_pol_vsync)
+ printf("0, \\\n");
+ else if (t->pos_pol_hsync && t->pos_pol_vsync)
+ printf("\\\n\t\t\tV4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \\\n");
+ else if (t->pos_pol_hsync)
+ printf("V4L2_DV_HSYNC_POS_POL, \\\n");
+ else
+ printf("V4L2_DV_VSYNC_POS_POL, \\\n");
+ unsigned hfp = t->hborder + t->hfp;
+ unsigned hbp = t->hborder + t->hbp;
+ unsigned vfp = t->vborder + t->vfp;
+ unsigned vbp = t->vborder + t->vbp;
+ printf("\t\t\t%lluULL, %d, %u, %d, %u, %u, %d, %u, %u, %d, \\\n",
+ t->pixclk_khz * 1000ULL, hfp, t->hsync, hbp,
+ vfp, t->vsync, vbp,
+ t->interlaced ? vfp : 0,
+ t->interlaced ? t->vsync : 0,
+ t->interlaced ? vbp + !t->even_vtotal : 0);
+
+ std::string flags;
+ unsigned num_flags = 0;
+ unsigned vic = 0;
+ unsigned hdmi_vic = 0;
+ const char *std = "0";
+
+ if (t->interlaced && !t->even_vtotal)
+ or_str(flags, "V4L2_DV_FL_HALF_LINE", num_flags);
+ if (!memcmp(type, "VIC", 3)) {
+ or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
+ or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
+ vic = strtoul(type + 4, 0, 0);
+ }
+ if (!memcmp(type, "HDMI VIC", 8)) {
+ or_str(flags, "V4L2_DV_FL_HAS_HDMI_VIC", num_flags);
+ or_str(flags, "V4L2_DV_FL_IS_CE_VIDEO", num_flags);
+ hdmi_vic = strtoul(type + 9, 0, 0);
+ vic = hdmi_vic_to_vic(hdmi_vic);
+ if (vic)
+ or_str(flags, "V4L2_DV_FL_HAS_CEA861_VIC", num_flags);
+ }
+ if (vic && (fmod(refresh, 6)) == 0.0)
+ or_str(flags, "V4L2_DV_FL_CAN_REDUCE_FPS", num_flags);
+ if (t->rb)
+ or_str(flags, "V4L2_DV_FL_REDUCED_BLANKING", num_flags);
+ if (t->hratio && t->vratio)
+ or_str(flags, "V4L2_DV_FL_HAS_PICTURE_ASPECT", num_flags);
+
+ if (!memcmp(type, "VIC", 3) || !memcmp(type, "HDMI VIC", 8))
+ std = "V4L2_DV_BT_STD_CEA861";
+ else if (!memcmp(type, "DMT", 3))
+ std = "V4L2_DV_BT_STD_DMT";
+ else if (!memcmp(type, "CVT", 3))
+ std = "V4L2_DV_BT_STD_CVT";
+ else if (!memcmp(type, "GTF", 3))
+ std = "V4L2_DV_BT_STD_GTF";
+ printf("\t\t\t%s, \\\n", std);
+ printf("\t\t\t%s, \\\n", flags.empty() ? "0" : flags.c_str());
+ printf("\t\t\t{ %u, %u }, %u, %u) \\\n",
+ t->hratio, t->vratio, vic, hdmi_vic);
+ printf("\t}\n");
+}
+
+static void print_detailed_timing(unsigned indent, const struct timings *t)
+{
+ printf("%*sHfront %4d Hsync %3u Hback %3d Hpol %s",
+ indent, "",
+ t->hfp, t->hsync, t->hbp, t->pos_pol_hsync ? "P" : "N");
+ if (t->hborder)
+ printf(" Hborder %u", t->hborder);
+ printf("\n");
+
+ printf("%*sVfront %4u Vsync %3u Vback %3d",
+ indent, "", t->vfp, t->vsync, t->vbp);
+ if (!t->no_pol_vsync)
+ printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
+ if (t->vborder)
+ printf(" Vborder %u", t->vborder);
+ if (t->even_vtotal) {
+ printf(" Both Fields");
+ } else if (t->interlaced) {
+ printf(" Vfront +0.5 Odd Field\n");
+ printf("%*sVfront %4d Vsync %3u Vback %3d",
+ indent, "", t->vfp, t->vsync, t->vbp);
+ if (!t->no_pol_vsync)
+ printf(" Vpol %s", t->pos_pol_vsync ? "P" : "N");
+ if (t->vborder)
+ printf(" Vborder %u", t->vborder);
+ printf(" Vback +0.5 Even Field");
+ }
+ printf("\n");
+}
+
+bool edid_state::print_timings(const char *prefix, const struct timings *t,
+ const char *type, const char *flags,
+ bool detailed, bool do_checks)
+{
+ if (!t) {
+ // Should not happen
+ if (do_checks)
+ fail("Unknown video timings.\n");
+ return false;
+ }
+
+ if (detailed && options[OptShortTimings])
+ detailed = false;
+ if (options[OptLongTimings])
+ detailed = true;
+
+ unsigned vact = t->vact;
+ unsigned hbl = t->hfp + t->hsync + t->hbp + 2 * t->hborder;
+ unsigned vbl = t->vfp + t->vsync + t->vbp + 2 * t->vborder;
+ unsigned htotal = t->hact + hbl;
+ double hor_freq_khz = htotal ? (double)t->pixclk_khz / htotal : 0;
+
+ if (t->interlaced)
+ vact /= 2;
+
+ if (t->ycbcr420)
+ hor_freq_khz /= 2;
+
+ double vtotal = vact + vbl;
+
+ bool ok = true;
+
+ if (!t->hact || !hbl || !t->hfp || !t->hsync ||
+ !vact || !vbl || (!t->vfp && !t->interlaced && !t->even_vtotal) || !t->vsync) {
+ if (do_checks)
+ fail("0 values in the video timing:\n"
+ " Horizontal Active/Blanking %u/%u\n"
+ " Horizontal Frontporch/Sync Width %u/%u\n"
+ " Vertical Active/Blanking %u/%u\n"
+ " Vertical Frontporch/Sync Width %u/%u\n",
+ t->hact, hbl, t->hfp, t->hsync, vact, vbl, t->vfp, t->vsync);
+ ok = false;
+ }
+
+ if (t->even_vtotal)
+ vtotal = vact + t->vfp + t->vsync + t->vbp;
+ else if (t->interlaced)
+ vtotal = vact + t->vfp + t->vsync + t->vbp + 0.5;
+
+ double refresh = (double)t->pixclk_khz * 1000.0 / (htotal * vtotal);
+
+ std::string s;
+ unsigned rb = t->rb & ~RB_ALT;
+ if (rb) {
+ bool alt = t->rb & RB_ALT;
+ s = "RB";
+ if (rb == RB_CVT_V2)
+ s += std::string("v2") + (alt ? ",video-optimized" : "");
+ else if (rb == RB_CVT_V3)
+ s += std::string("v3") + (alt ? ",h-blank-160" : "");
+ }
+ add_str(s, flags);
+ if (t->hsize_mm || t->vsize_mm)
+ add_str(s, std::to_string(t->hsize_mm) + " mm x " + std::to_string(t->vsize_mm) + " mm");
+ if (t->hsize_mm > dtd_max_hsize_mm)
+ dtd_max_hsize_mm = t->hsize_mm;
+ if (t->vsize_mm > dtd_max_vsize_mm)
+ dtd_max_vsize_mm = t->vsize_mm;
+ if (!s.empty())
+ s = " (" + s + ")";
+ unsigned pixclk_khz = t->pixclk_khz / (t->ycbcr420 ? 2 : 1);
+
+ char buf[10];
+
+ sprintf(buf, "%u%s", t->vact, t->interlaced ? "i" : "");
+ printf("%s%s: %5ux%-5s %7.3f Hz %3u:%-3u %7.3f kHz %8.3f MHz%s\n",
+ prefix, type,
+ t->hact, buf,
+ refresh,
+ t->hratio, t->vratio,
+ hor_freq_khz,
+ pixclk_khz / 1000.0,
+ s.c_str());
+
+ unsigned len = strlen(prefix) + 2;
+
+ if (!t->ycbcr420 && detailed && options[OptXModeLineTimings])
+ print_modeline(len, t, refresh);
+ else if (!t->ycbcr420 && detailed && options[OptFBModeTimings])
+ print_fbmode(len, t, refresh, hor_freq_khz);
+ else if (!t->ycbcr420 && detailed && options[OptV4L2Timings])
+ print_v4l2_timing(t, refresh, type);
+ else if (detailed)
+ print_detailed_timing(len + strlen(type) + 6, t);
+
+ if (!do_checks)
+ return ok;
+
+ if (!memcmp(type, "DTD", 3)) {
+ unsigned vic, dmt;
+ const timings *vic_t = cta_close_match_to_vic(*t, vic);
+
+ if (vic_t)
+ warn("DTD is similar but not identical to VIC %u.\n", vic);
+
+ const timings *dmt_t = close_match_to_dmt(*t, dmt);
+ if (!vic_t && dmt_t)
+ warn("DTD is similar but not identical to DMT 0x%02x.\n", dmt);
+ }
+
+ if (refresh) {
+ min_vert_freq_hz = min(min_vert_freq_hz, refresh);
+ max_vert_freq_hz = max(max_vert_freq_hz, refresh);
+ }
+ if (hor_freq_khz) {
+ min_hor_freq_hz = min(min_hor_freq_hz, hor_freq_khz * 1000.0);
+ max_hor_freq_hz = max(max_hor_freq_hz, hor_freq_khz * 1000.0);
+ max_pixclk_khz = max(max_pixclk_khz, pixclk_khz);
+ if (t->pos_pol_hsync && !t->pos_pol_vsync && t->vsync == 3)
+ base.max_pos_neg_hor_freq_khz = hor_freq_khz;
+ }
+
+ if (t->ycbcr420 && t->pixclk_khz < 590000)
+ warn_once("Some YCbCr 4:2:0 timings are invalid for HDMI (which requires an RGB timings pixel rate >= 590 MHz).\n");
+ if (t->hfp <= 0)
+ fail("0 or negative horizontal front porch.\n");
+ if (t->hbp <= 0)
+ fail("0 or negative horizontal back porch.\n");
+ if (t->vbp <= 0)
+ fail("0 or negative vertical back porch.\n");
+ if (!base.max_display_width_mm && !base.max_display_height_mm) {
+ /* this is valid */
+ } else if (!t->hsize_mm && !t->vsize_mm) {
+ /* this is valid */
+ } else if (t->hsize_mm > base.max_display_width_mm + 9 ||
+ t->vsize_mm > base.max_display_height_mm + 9) {
+ fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
+ t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
+ } else if (t->hsize_mm < base.max_display_width_mm - 9 &&
+ t->vsize_mm < base.max_display_height_mm - 9) {
+ fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
+ t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
+ }
+ return ok;
+}
+
+std::string containerid2s(const unsigned char *x)
+{
+ char buf[40];
+
+ sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ x[0], x[1], x[2], x[3],
+ x[4], x[5],
+ x[6], x[7],
+ x[8], x[9],
+ x[10], x[11], x[12], x[13], x[14], x[15]);
+ return buf;
+}
+
+std::string utohex(unsigned char x)
+{
+ char buf[10];
+
+ sprintf(buf, "0x%02hhx", x);
+ return buf;
+}
+
+const char *oui_name(unsigned oui, bool reverse)
+{
+ if (reverse)
+ oui = (oui >> 16) | (oui & 0xff00) | ((oui & 0xff) << 16);
+
+ switch (oui) {
+ case 0x00001a: return "AMD";
+ case 0x000c03: return "HDMI";
+ case 0x00044b: return "NVIDIA";
+ case 0x000c6e: return "ASUS";
+ case 0x0010fa: return "Apple";
+ case 0x0014b9: return "MSTAR";
+ case 0x00d046: return "Dolby";
+ case 0x00e047: return "InFocus";
+ case 0x3a0292: return "VESA";
+ case 0x90848b: return "HDR10+";
+ case 0xc45dd8: return "HDMI Forum";
+ case 0xca125c: return "Microsoft";
+ default: return NULL;
+ }
+}
+
+std::string ouitohex(unsigned oui)
+{
+ char buf[32];
+
+ sprintf(buf, "%02X-%02X-%02X", (oui >> 16) & 0xff, (oui >> 8) & 0xff, oui & 0xff);
+ return buf;
+}
+
+bool memchk(const unsigned char *x, unsigned len, unsigned char v)
+{
+ for (unsigned i = 0; i < len; i++)
+ if (x[i] != v)
+ return false;
+ return true;
+}
+
+void hex_block(const char *prefix, const unsigned char *x,
+ unsigned length, bool show_ascii, unsigned step)
+{
+ unsigned i, j;
+
+ if (!length)
+ return;
+
+ for (i = 0; i < length; i += step) {
+ unsigned len = min(step, length - i);
+
+ printf("%s", prefix);
+ for (j = 0; j < len; j++)
+ printf("%s%02x", j ? " " : "", x[i + j]);
+
+ if (show_ascii) {
+ for (j = len; j < step; j++)
+ printf(" ");
+ printf(" '");
+ for (j = 0; j < len; j++)
+ printf("%c", x[i + j] >= ' ' && x[i + j] <= '~' ? x[i + j] : '.');
+ printf("'");
+ }
+ printf("\n");
+ }
+}
+
+static bool edid_add_byte(const char *s, bool two_digits = true)
+{
+ char buf[3];
+
+ if (state.edid_size == sizeof(edid))
+ return false;
+ buf[0] = s[0];
+ buf[1] = two_digits ? s[1] : 0;
+ buf[2] = 0;
+ edid[state.edid_size++] = strtoul(buf, NULL, 16);
+ return true;
+}
+
+static bool extract_edid_quantumdata(const char *start)
+{
+ /* Parse QuantumData 980 EDID files */
+ do {
+ start = strstr(start, ">");
+ if (!start)
+ return false;
+ start++;
+ for (unsigned i = 0; start[i] && start[i + 1] && i < 256; i += 2)
+ if (!edid_add_byte(start + i))
+ return false;
+ start = strstr(start, "<BLOCK");
+ } while (start);
+ return state.edid_size;
+}
+
+static const char *ignore_chars = ",:;";
+
+static bool extract_edid_hex(const char *s, bool require_two_digits = true)
+{
+ for (; *s; s++) {
+ if (isspace(*s) || strchr(ignore_chars, *s))
+ continue;
+
+ if (*s == '0' && tolower(s[1]) == 'x') {
+ s++;
+ continue;
+ }
+
+ /* Read one or two hex digits from the log */
+ if (!isxdigit(s[0])) {
+ if (state.edid_size && state.edid_size % 128 == 0)
+ break;
+ return false;
+ }
+ if (require_two_digits && !isxdigit(s[1])) {
+ odd_hex_digits = true;
+ return false;
+ }
+ if (!edid_add_byte(s, isxdigit(s[1])))
+ return false;
+ if (isxdigit(s[1]))
+ s++;
+ }
+ return state.edid_size;
+}
+
+static bool extract_edid_xrandr(const char *start)
+{
+ static const char indentation1[] = " ";
+ static const char indentation2[] = "\t\t";
+ /* Used to detect that we've gone past the EDID property */
+ static const char half_indentation1[] = " ";
+ static const char half_indentation2[] = "\t";
+ const char *indentation;
+ const char *s;
+
+ for (;;) {
+ unsigned j;
+
+ /* Get the next start of the line of EDID hex, assuming spaces for indentation */
+ s = strstr(start, indentation = indentation1);
+ /* Did we skip the start of another property? */
+ if (s && s > strstr(start, half_indentation1))
+ break;
+
+ /* If we failed, retry assuming tabs for indentation */
+ if (!s) {
+ s = strstr(start, indentation = indentation2);
+ /* Did we skip the start of another property? */
+ if (s && s > strstr(start, half_indentation2))
+ break;
+ }
+
+ if (!s)
+ break;
+
+ start = s + strlen(indentation);
+
+ for (j = 0; j < 16; j++, start += 2) {
+ /* Read a %02x from the log */
+ if (!isxdigit(start[0]) || !isxdigit(start[1])) {
+ if (j)
+ break;
+ return false;
+ }
+ if (!edid_add_byte(start))
+ return false;
+ }
+ }
+ return state.edid_size;
+}
+
+static bool extract_edid_xorg(const char *start)
+{
+ bool find_first_num = true;
+
+ for (; *start; start++) {
+ if (find_first_num) {
+ const char *s;
+
+ /* skip ahead to the : */
+ s = strstr(start, ": \t");
+ if (!s)
+ s = strstr(start, ": ");
+ if (!s)
+ break;
+ start = s;
+ /* and find the first number */
+ while (!isxdigit(start[1]))
+ start++;
+ find_first_num = false;
+ continue;
+ } else {
+ /* Read a %02x from the log */
+ if (!isxdigit(*start)) {
+ find_first_num = true;
+ continue;
+ }
+ if (!edid_add_byte(start))
+ return false;
+ start++;
+ }
+ }
+ return state.edid_size;
+}
+
+static bool extract_edid(int fd, FILE *error)
+{
+ std::vector<char> edid_data;
+ char buf[EDID_PAGE_SIZE];
+
+ for (;;) {
+ ssize_t i = read(fd, buf, sizeof(buf));
+
+ if (i < 0)
+ return false;
+ if (i == 0)
+ break;
+ edid_data.insert(edid_data.end(), buf, buf + i);
+ }
+
+ if (edid_data.empty()) {
+ state.edid_size = 0;
+ return false;
+ }
+
+ const char *data = &edid_data[0];
+ const char *start;
+
+ /* Look for edid-decode output */
+ start = strstr(data, "EDID (hex):");
+ if (!start)
+ start = strstr(data, "edid-decode (hex):");
+ if (start)
+ return extract_edid_hex(strchr(start, ':'));
+
+ /* Look for C-array */
+ start = strstr(data, "unsigned char edid[] = {");
+ if (start)
+ return extract_edid_hex(strchr(start, '{') + 1, false);
+
+ /* Look for QuantumData EDID output */
+ start = strstr(data, "<BLOCK");
+ if (start)
+ return extract_edid_quantumdata(start);
+
+ /* Look for xrandr --verbose output (lines of 16 hex bytes) */
+ start = strstr(data, "EDID_DATA:");
+ if (!start)
+ start = strstr(data, "EDID:");
+ if (start)
+ return extract_edid_xrandr(start);
+
+ /* Look for an EDID in an Xorg.0.log file */
+ start = strstr(data, "EDID (in hex):");
+ if (start)
+ start = strstr(start, "(II)");
+ if (start)
+ return extract_edid_xorg(start);
+
+ unsigned i;
+
+ /* Is the EDID provided in hex? */
+ for (i = 0; i < 32 && (isspace(data[i]) || strchr(ignore_chars, data[i]) ||
+ tolower(data[i]) == 'x' || isxdigit(data[i])); i++);
+
+ if (i == 32)
+ return extract_edid_hex(data);
+
+ /* Assume binary */
+ if (edid_data.size() > sizeof(edid)) {
+ fprintf(error, "Binary EDID length %zu is greater than %zu.\n",
+ edid_data.size(), sizeof(edid));
+ return false;
+ }
+ memcpy(edid, data, edid_data.size());
+ state.edid_size = edid_data.size();
+ return true;
+}
+
+static unsigned char crc_calc(const unsigned char *b)
+{
+ unsigned char sum = 0;
+ unsigned i;
+
+ for (i = 0; i < 127; i++)
+ sum += b[i];
+ return 256 - sum;
+}
+
+static int crc_ok(const unsigned char *b)
+{
+ return crc_calc(b) == b[127];
+}
+
+static void hexdumpedid(FILE *f, const unsigned char *edid, unsigned size)
+{
+ unsigned b, i, j;
+
+ for (b = 0; b < size / 128; b++) {
+ const unsigned char *buf = edid + 128 * b;
+
+ if (b)
+ fprintf(f, "\n");
+ for (i = 0; i < 128; i += 0x10) {
+ fprintf(f, "%02x", buf[i]);
+ for (j = 1; j < 0x10; j++) {
+ fprintf(f, " %02x", buf[i + j]);
+ }
+ fprintf(f, "\n");
+ }
+ if (!crc_ok(buf))
+ fprintf(f, "Block %u has a checksum error (should be 0x%02x).\n",
+ b, crc_calc(buf));
+ }
+}
+
+static void carraydumpedid(FILE *f, const unsigned char *edid, unsigned size)
+{
+ unsigned b, i, j;
+
+ fprintf(f, "const unsigned char edid[] = {\n");
+ for (b = 0; b < size / 128; b++) {
+ const unsigned char *buf = edid + 128 * b;
+
+ if (b)
+ fprintf(f, "\n");
+ for (i = 0; i < 128; i += 8) {
+ fprintf(f, "\t0x%02x,", buf[i]);
+ for (j = 1; j < 8; j++) {
+ fprintf(f, " 0x%02x,", buf[i + j]);
+ }
+ fprintf(f, "\n");
+ }
+ if (!crc_ok(buf))
+ fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x). */\n",
+ b, crc_calc(buf));
+ }
+ fprintf(f, "};\n");
+}
+
+// This format can be read by the QuantumData EDID editor
+static void xmldumpedid(FILE *f, const unsigned char *edid, unsigned size)
+{
+ fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
+ fprintf(f, "<DATAOBJ>\n");
+ fprintf(f, " <HEADER TYPE=\"DID\" VERSION=\"1.0\"/>\n");
+ fprintf(f, " <DATA>\n");
+ for (unsigned b = 0; b < size / 128; b++) {
+ const unsigned char *buf = edid + 128 * b;
+
+ fprintf(f, " <BLOCK%u>", b);
+ for (unsigned i = 0; i < 128; i++)
+ fprintf(f, "%02X", buf[i]);
+ fprintf(f, "</BLOCK%u>\n", b);
+ }
+ fprintf(f, " </DATA>\n");
+ fprintf(f, "</DATAOBJ>\n");
+}
+
+
+static int edid_to_file(const char *to_file, enum output_format out_fmt)
+{
+ FILE *out;
+
+ if (!strcmp(to_file, "-")) {
+ to_file = "stdout";
+ out = stdout;
+ } else if ((out = fopen(to_file, "w")) == NULL) {
+ perror(to_file);
+ return -1;
+ }
+ if (out_fmt == OUT_FMT_DEFAULT)
+ out_fmt = out == stdout ? OUT_FMT_HEX : OUT_FMT_RAW;
+
+ switch (out_fmt) {
+ default:
+ case OUT_FMT_HEX:
+ hexdumpedid(out, edid, state.edid_size);
+ break;
+ case OUT_FMT_RAW:
+ fwrite(edid, state.edid_size, 1, out);
+ break;
+ case OUT_FMT_CARRAY:
+ carraydumpedid(out, edid, state.edid_size);
+ break;
+ case OUT_FMT_XML:
+ xmldumpedid(out, edid, state.edid_size);
+ break;
+ }
+
+ if (out != stdout)
+ fclose(out);
+ return 0;
+}
+
+static int edid_from_file(const char *from_file, FILE *error)
+{
+#ifdef O_BINARY
+ // Windows compatibility
+ int flags = O_RDONLY | O_BINARY;
+#else
+ int flags = O_RDONLY;
+#endif
+ int fd;
+
+ if (!strcmp(from_file, "-")) {
+ from_file = "stdin";
+ fd = 0;
+ } else if ((fd = open(from_file, flags)) == -1) {
+ perror(from_file);
+ return -1;
+ }
+
+ odd_hex_digits = false;
+ if (!extract_edid(fd, error)) {
+ if (!state.edid_size) {
+ fprintf(error, "EDID of '%s' was empty.\n", from_file);
+ return -1;
+ }
+ fprintf(error, "EDID extract of '%s' failed: ", from_file);
+ if (odd_hex_digits)
+ fprintf(error, "odd number of hexadecimal digits.\n");
+ else
+ fprintf(error, "unknown format.\n");
+ return -1;
+ }
+ if (state.edid_size % EDID_PAGE_SIZE) {
+ fprintf(error, "EDID length %u is not a multiple of %u.\n",
+ state.edid_size, EDID_PAGE_SIZE);
+ return -1;
+ }
+ state.num_blocks = state.edid_size / EDID_PAGE_SIZE;
+ if (fd != 0)
+ close(fd);
+
+ if (memcmp(edid, "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", 8)) {
+ fprintf(error, "No EDID header found in '%s'.\n", from_file);
+ return -1;
+ }
+ return 0;
+}
+
+/* generic extension code */
+
+std::string block_name(unsigned char block)
+{
+ char buf[10];
+
+ switch (block) {
+ case 0x00: return "Base EDID";
+ case 0x02: return "CTA-861 Extension Block";
+ case 0x10: return "Video Timing Extension Block";
+ case 0x20: return "EDID 2.0 Extension Block";
+ case 0x40: return "Display Information Extension Block";
+ case 0x50: return "Localized String Extension Block";
+ case 0x60: return "Microdisplay Interface Extension Block";
+ case 0x70: return "DisplayID Extension Block";
+ case 0xf0: return "Block Map Extension Block";
+ case 0xff: return "Manufacturer-Specific Extension Block";
+ default:
+ sprintf(buf, " 0x%02x", block);
+ return std::string("Unknown EDID Extension Block") + buf;
+ }
+}
+
+void edid_state::parse_block_map(const unsigned char *x)
+{
+ unsigned last_valid_block_tag = 0;
+ bool fail_once = false;
+ unsigned offset = 1;
+ unsigned i;
+
+ if (block_nr == 1)
+ block_map.saw_block_1 = true;
+ else if (!block_map.saw_block_1)
+ fail("No EDID Block Map Extension found in block 1.\n");
+ else if (block_nr == 128)
+ block_map.saw_block_128 = true;
+
+ if (block_nr > 1)
+ offset = 128;
+
+ for (i = 1; i < 127; i++) {
+ unsigned block = offset + i;
+
+ if (x[i]) {
+ last_valid_block_tag++;
+ if (i != last_valid_block_tag && !fail_once) {
+ fail("Valid block tags are not consecutive.\n");
+ fail_once = true;
+ }
+ printf(" Block %3u: %s\n", block, block_name(x[i]).c_str());
+ if (block >= num_blocks) {
+ if (!fail_once)
+ fail("Invalid block number %u.\n", block);
+ fail_once = true;
+ } else if (x[i] != edid[block * EDID_PAGE_SIZE]) {
+ fail("Block %u tag mismatch: expected 0x%02x, but got 0x%02x.\n",
+ block, edid[block * EDID_PAGE_SIZE], x[i]);
+ }
+ } else if (block < num_blocks) {
+ fail("Block %u tag mismatch: expected 0x%02x, but got 0x00.\n",
+ block, edid[block * EDID_PAGE_SIZE]);
+ }
+ }
+}
+
+void edid_state::preparse_extension(const unsigned char *x)
+{
+ switch (x[0]) {
+ case 0x02:
+ has_cta = true;
+ preparse_cta_block(x);
+ break;
+ case 0x70:
+ has_dispid = true;
+ preparse_displayid_block(x);
+ break;
+ }
+}
+
+void edid_state::parse_extension(const unsigned char *x)
+{
+ block = block_name(x[0]);
+ data_block.clear();
+
+ printf("\n");
+ if (block_nr && x[0] == 0)
+ block = "Unknown EDID Extension Block 0x00";
+ printf("Block %u, %s:\n", block_nr, block.c_str());
+
+ switch (x[0]) {
+ case 0x02:
+ parse_cta_block(x);
+ break;
+ case 0x10:
+ parse_vtb_ext_block(x);
+ break;
+ case 0x20:
+ fail("Deprecated extension block for EDID 2.0, do not use.\n");
+ break;
+ case 0x40:
+ parse_di_ext_block(x);
+ break;
+ case 0x50:
+ parse_ls_ext_block(x);
+ break;
+ case 0x70:
+ parse_displayid_block(x);
+ break;
+ case 0xf0:
+ parse_block_map(x);
+ if (block_nr != 1 && block_nr != 128)
+ fail("Must be used in block 1 and 128.\n");
+ break;
+ default:
+ hex_block(" ", x, EDID_PAGE_SIZE);
+ fail("Unknown Extension Block.\n");
+ break;
+ }
+
+ data_block.clear();
+ do_checksum("", x, EDID_PAGE_SIZE);
+}
+
+int edid_state::parse_edid()
+{
+ hide_serial_numbers = options[OptHideSerialNumbers];
+
+ for (unsigned i = 1; i < num_blocks; i++)
+ preparse_extension(edid + i * EDID_PAGE_SIZE);
+
+ if (options[OptPhysicalAddress]) {
+ printf("%x.%x.%x.%x\n",
+ (cta.preparsed_phys_addr >> 12) & 0xf,
+ (cta.preparsed_phys_addr >> 8) & 0xf,
+ (cta.preparsed_phys_addr >> 4) & 0xf,
+ cta.preparsed_phys_addr & 0xf);
+ return 0;
+ }
+
+ if (!options[OptSkipHexDump]) {
+ printf("edid-decode (hex):\n\n");
+ for (unsigned i = 0; i < num_blocks; i++) {
+ hex_block("", edid + i * EDID_PAGE_SIZE, EDID_PAGE_SIZE, false);
+ if (i == num_blocks - 1 && options[OptOnlyHexDump])
+ return 0;
+ printf("\n");
+ }
+ printf("----------------\n\n");
+ }
+
+ block = block_name(0x00);
+ printf("Block %u, %s:\n", block_nr, block.c_str());
+ parse_base_block(edid);
+
+ for (unsigned i = 1; i < num_blocks; i++) {
+ block_nr++;
+ printf("\n----------------\n");
+ parse_extension(edid + i * EDID_PAGE_SIZE);
+ }
+
+ block = "";
+ block_nr = EDID_MAX_BLOCKS;
+
+ if (has_cta)
+ cta_resolve_svrs();
+
+ if (options[OptPreferredTimings] && base.preferred_timing.is_valid()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing if only Block 0 is parsed:\n");
+ print_timings(" ", base.preferred_timing, true, false);
+ }
+
+ if (options[OptNativeTimings] &&
+ base.preferred_timing.is_valid() && base.preferred_is_also_native) {
+ printf("\n----------------\n");
+ printf("\nNative Video Timing if only Block 0 is parsed:\n");
+ print_timings(" ", base.preferred_timing, true, false);
+ }
+
+ if (options[OptPreferredTimings] && !cta.preferred_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
+ cta.preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true, false);
+ }
+
+ if (options[OptNativeTimings] && !cta.native_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nNative Video Timing%s if Block 0 and CTA-861 Blocks are parsed:\n",
+ cta.native_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter)
+ print_timings(" ", *iter, true, false);
+ }
+
+ if (options[OptPreferredTimings] && !dispid.preferred_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing%s if Block 0 and DisplayID Blocks are parsed:\n",
+ dispid.preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
+ iter != dispid.preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true, false);
+ }
+
+ if (!options[OptCheck] && !options[OptCheckInline])
+ return 0;
+
+ check_base_block();
+ if (has_cta)
+ check_cta_blocks();
+ if (has_dispid)
+ check_displayid_blocks();
+
+ printf("\n----------------\n");
+
+ if (!options[OptSkipSHA] && strlen(STRING(SHA))) {
+ printf("\nedid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
+ }
+
+ if (options[OptCheck]) {
+ if (warnings)
+ show_msgs(true);
+ if (failures)
+ show_msgs(false);
+ }
+ printf("\nEDID conformity: %s\n", failures ? "FAIL" : "PASS");
+ return failures ? -2 : 0;
+}
+
+enum cvt_opts {
+ CVT_WIDTH = 0,
+ CVT_HEIGHT,
+ CVT_FPS,
+ CVT_INTERLACED,
+ CVT_OVERSCAN,
+ CVT_RB,
+ CVT_ALT,
+ CVT_RB_H_BLANK,
+};
+
+static int parse_cvt_subopt(char **subopt_str, double *value)
+{
+ int opt;
+ char *opt_str;
+
+ static const char * const subopt_list[] = {
+ "w",
+ "h",
+ "fps",
+ "interlaced",
+ "overscan",
+ "rb",
+ "alt",
+ "hblank",
+ nullptr
+ };
+
+ opt = getsubopt(subopt_str, (char* const*) subopt_list, &opt_str);
+
+ if (opt == -1) {
+ fprintf(stderr, "Invalid suboptions specified.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (opt_str == nullptr && opt != CVT_INTERLACED && opt != CVT_ALT &&
+ opt != CVT_OVERSCAN) {
+ fprintf(stderr, "No value given to suboption <%s>.\n",
+ subopt_list[opt]);
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+
+ if (opt_str)
+ *value = strtod(opt_str, nullptr);
+ return opt;
+}
+
+static void parse_cvt(char *optarg)
+{
+ unsigned w = 0, h = 0;
+ double fps = 0;
+ unsigned rb = RB_NONE;
+ unsigned rb_h_blank = 0;
+ bool interlaced = false;
+ bool alt = false;
+ bool overscan = false;
+
+ while (*optarg != '\0') {
+ int opt;
+ double opt_val;
+
+ opt = parse_cvt_subopt(&optarg, &opt_val);
+
+ switch (opt) {
+ case CVT_WIDTH:
+ w = round(opt_val);
+ break;
+ case CVT_HEIGHT:
+ h = round(opt_val);
+ break;
+ case CVT_FPS:
+ fps = opt_val;
+ break;
+ case CVT_RB:
+ rb = opt_val;
+ break;
+ case CVT_OVERSCAN:
+ overscan = true;
+ break;
+ case CVT_INTERLACED:
+ interlaced = opt_val;
+ break;
+ case CVT_ALT:
+ alt = opt_val;
+ break;
+ case CVT_RB_H_BLANK:
+ rb_h_blank = opt_val;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!w || !h || !fps) {
+ fprintf(stderr, "Missing width, height and/or fps.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (interlaced)
+ fps /= 2;
+ timings t = state.calc_cvt_mode(w, h, fps, rb, interlaced, overscan, alt, rb_h_blank);
+ state.print_timings("", &t, "CVT", "", true, false);
+}
+
+struct gtf_parsed_data {
+ unsigned w, h;
+ double freq;
+ double C, M, K, J;
+ bool overscan;
+ bool interlaced;
+ bool secondary;
+ bool params_from_edid;
+ enum gtf_ip_parm ip_parm;
+};
+
+enum gtf_opts {
+ GTF_WIDTH = 0,
+ GTF_HEIGHT,
+ GTF_FPS,
+ GTF_HORFREQ,
+ GTF_PIXCLK,
+ GTF_INTERLACED,
+ GTF_OVERSCAN,
+ GTF_SECONDARY,
+ GTF_C2,
+ GTF_M,
+ GTF_K,
+ GTF_J2,
+};
+
+static int parse_gtf_subopt(char **subopt_str, double *value)
+{
+ int opt;
+ char *opt_str;
+
+ static const char * const subopt_list[] = {
+ "w",
+ "h",
+ "fps",
+ "horfreq",
+ "pixclk",
+ "interlaced",
+ "overscan",
+ "secondary",
+ "C",
+ "M",
+ "K",
+ "J",
+ nullptr
+ };
+
+ opt = getsubopt(subopt_str, (char * const *)subopt_list, &opt_str);
+
+ if (opt == -1) {
+ fprintf(stderr, "Invalid suboptions specified.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (opt_str == nullptr && opt != GTF_INTERLACED && opt != GTF_OVERSCAN &&
+ opt != GTF_SECONDARY) {
+ fprintf(stderr, "No value given to suboption <%s>.\n",
+ subopt_list[opt]);
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+
+ if (opt == GTF_C2 || opt == GTF_J2)
+ *value = round(2.0 * strtod(opt_str, nullptr));
+ else if (opt_str)
+ *value = strtod(opt_str, nullptr);
+ return opt;
+}
+
+static void parse_gtf(char *optarg, gtf_parsed_data &data)
+{
+ memset(&data, 0, sizeof(data));
+ data.params_from_edid = true;
+ data.C = 40;
+ data.M = 600;
+ data.K = 128;
+ data.J = 20;
+
+ while (*optarg != '\0') {
+ int opt;
+ double opt_val;
+
+ opt = parse_gtf_subopt(&optarg, &opt_val);
+
+ switch (opt) {
+ case GTF_WIDTH:
+ data.w = round(opt_val);
+ break;
+ case GTF_HEIGHT:
+ data.h = round(opt_val);
+ break;
+ case GTF_FPS:
+ data.freq = opt_val;
+ data.ip_parm = gtf_ip_vert_freq;
+ break;
+ case GTF_HORFREQ:
+ data.freq = opt_val;
+ data.ip_parm = gtf_ip_hor_freq;
+ break;
+ case GTF_PIXCLK:
+ data.freq = opt_val;
+ data.ip_parm = gtf_ip_clk_freq;
+ break;
+ case GTF_INTERLACED:
+ data.interlaced = true;
+ break;
+ case GTF_OVERSCAN:
+ data.overscan = true;
+ break;
+ case GTF_SECONDARY:
+ data.secondary = true;
+ break;
+ case GTF_C2:
+ data.C = opt_val / 2.0;
+ data.params_from_edid = false;
+ break;
+ case GTF_M:
+ data.M = round(opt_val);
+ data.params_from_edid = false;
+ break;
+ case GTF_K:
+ data.K = round(opt_val);
+ data.params_from_edid = false;
+ break;
+ case GTF_J2:
+ data.J = opt_val / 2.0;
+ data.params_from_edid = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!data.w || !data.h) {
+ fprintf(stderr, "Missing width and/or height.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (!data.freq) {
+ fprintf(stderr, "One of fps, horfreq or pixclk must be given.\n");
+ usage();
+ std::exit(EXIT_FAILURE);
+ }
+ if (!data.secondary)
+ data.params_from_edid = false;
+ if (data.interlaced && data.ip_parm == gtf_ip_vert_freq)
+ data.freq /= 2;
+}
+
+static void show_gtf(gtf_parsed_data &data)
+{
+ timings t;
+
+ t = state.calc_gtf_mode(data.w, data.h, data.freq, data.interlaced,
+ data.ip_parm, data.overscan, data.secondary,
+ data.C, data.M, data.K, data.J);
+ calc_ratio(&t);
+ state.print_timings("", &t, "GTF", "", true, false);
+}
+
+int main(int argc, char **argv)
+{
+ char short_options[26 * 2 * 2 + 1];
+ enum output_format out_fmt = OUT_FMT_DEFAULT;
+ gtf_parsed_data gtf_data;
+ int ret;
+
+ while (1) {
+ int option_index = 0;
+ unsigned idx = 0;
+ unsigned i, val;
+ const timings *t;
+ char buf[16];
+
+ for (i = 0; long_options[i].name; i++) {
+ if (!isalpha(long_options[i].val))
+ continue;
+ short_options[idx++] = long_options[i].val;
+ if (long_options[i].has_arg == required_argument)
+ short_options[idx++] = ':';
+ }
+ short_options[idx] = 0;
+ int ch = getopt_long(argc, argv, short_options,
+ long_options, &option_index);
+ if (ch == -1)
+ break;
+
+ options[ch] = 1;
+ switch (ch) {
+ case OptHelp:
+ usage();
+ return -1;
+ case OptOutputFormat:
+ if (!strcmp(optarg, "hex")) {
+ out_fmt = OUT_FMT_HEX;
+ } else if (!strcmp(optarg, "raw")) {
+ out_fmt = OUT_FMT_RAW;
+ } else if (!strcmp(optarg, "carray")) {
+ out_fmt = OUT_FMT_CARRAY;
+ } else if (!strcmp(optarg, "xml")) {
+ out_fmt = OUT_FMT_XML;
+ } else {
+ usage();
+ exit(1);
+ }
+ break;
+ case OptSTD: {
+ unsigned char byte1, byte2 = 0;
+ char *endptr;
+
+ byte1 = strtoul(optarg, &endptr, 0);
+ if (*endptr == ',')
+ byte2 = strtoul(endptr + 1, NULL, 0);
+ state.print_standard_timing("", byte1, byte2, false, true);
+ break;
+ }
+ case OptDMT:
+ val = strtoul(optarg, NULL, 0);
+ t = find_dmt_id(val);
+ if (t) {
+ sprintf(buf, "DMT 0x%02x", val);
+ state.print_timings("", t, buf, "", true, false);
+ } else {
+ fprintf(stderr, "Unknown DMT code 0x%02x.\n", val);
+ }
+ break;
+ case OptVIC:
+ val = strtoul(optarg, NULL, 0);
+ t = find_vic_id(val);
+ if (t) {
+ sprintf(buf, "VIC %3u", val);
+ state.print_timings("", t, buf, "", true, false);
+ } else {
+ fprintf(stderr, "Unknown VIC code %u.\n", val);
+ }
+ break;
+ case OptHDMIVIC:
+ val = strtoul(optarg, NULL, 0);
+ t = find_hdmi_vic_id(val);
+ if (t) {
+ sprintf(buf, "HDMI VIC %u", val);
+ state.print_timings("", t, buf, "", true, false);
+ } else {
+ fprintf(stderr, "Unknown HDMI VIC code %u.\n", val);
+ }
+ break;
+ case OptCVT:
+ parse_cvt(optarg);
+ break;
+ case OptGTF:
+ parse_gtf(optarg, gtf_data);
+ break;
+ case ':':
+ fprintf(stderr, "Option '%s' requires a value.\n",
+ argv[optind]);
+ usage();
+ return -1;
+ case '?':
+ fprintf(stderr, "Unknown argument '%s'.\n",
+ argv[optind]);
+ usage();
+ return -1;
+ }
+ }
+ if (optind == argc && options[OptVersion]) {
+ if (strlen(STRING(SHA)))
+ printf("edid-decode SHA: %s %s\n", STRING(SHA), STRING(DATE));
+ else
+ printf("edid-decode SHA: not available\n");
+ return 0;
+ }
+
+ if (options[OptListEstTimings])
+ state.list_established_timings();
+ if (options[OptListDMTs])
+ state.list_dmts();
+ if (options[OptListVICs])
+ state.cta_list_vics();
+ if (options[OptListHDMIVICs])
+ state.cta_list_hdmi_vics();
+
+ if (options[OptListEstTimings] || options[OptListDMTs] ||
+ options[OptListVICs] || options[OptListHDMIVICs])
+ return 0;
+
+ if (options[OptCVT] || options[OptDMT] || options[OptVIC] ||
+ options[OptHDMIVIC] || options[OptSTD])
+ return 0;
+
+ if (options[OptGTF] && (!gtf_data.params_from_edid || optind == argc)) {
+ show_gtf(gtf_data);
+ return 0;
+ }
+
+ if (optind == argc)
+ ret = edid_from_file("-", stdout);
+ else
+ ret = edid_from_file(argv[optind], argv[optind + 1] ? stderr : stdout);
+
+ if (ret && options[OptPhysicalAddress]) {
+ printf("f.f.f.f\n");
+ return 0;
+ }
+ if (optind < argc - 1)
+ return ret ? ret : edid_to_file(argv[optind + 1], out_fmt);
+
+ if (options[OptGTF]) {
+ timings t;
+
+ // Find the Secondary Curve
+ state.preparse_detailed_block(edid + 0x36);
+ state.preparse_detailed_block(edid + 0x48);
+ state.preparse_detailed_block(edid + 0x5a);
+ state.preparse_detailed_block(edid + 0x6c);
+
+ t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
+ gtf_data.interlaced, gtf_data.ip_parm,
+ gtf_data.overscan);
+ unsigned hbl = t.hfp + t.hsync + t.hbp;
+ unsigned htotal = t.hact + hbl;
+ double hor_freq_khz = htotal ? (double)t.pixclk_khz / htotal : 0;
+
+ if (state.base.supports_sec_gtf &&
+ hor_freq_khz >= state.base.sec_gtf_start_freq) {
+ t = state.calc_gtf_mode(gtf_data.w, gtf_data.h, gtf_data.freq,
+ gtf_data.interlaced, gtf_data.ip_parm,
+ gtf_data.overscan, true,
+ state.base.C, state.base.M,
+ state.base.K, state.base.J);
+ }
+ calc_ratio(&t);
+ if (t.hfp <= 0)
+ state.print_timings("", &t, "GTF", "INVALID: Hfront <= 0", true, false);
+ else
+ state.print_timings("", &t, "GTF", "", true, false);
+ return 0;
+ }
+
+ return ret ? ret : state.parse_edid();
+}
+
+#ifdef __EMSCRIPTEN__
+/*
+ * The surrounding JavaScript implementation will call this function
+ * each time it wants to decode an EDID. So this should reset all the
+ * state and start over.
+ */
+extern "C" int parse_edid(const char *input)
+{
+ for (unsigned i = 0; i < EDID_MAX_BLOCKS + 1; i++) {
+ s_msgs[i][0].clear();
+ s_msgs[i][1].clear();
+ }
+ options[OptCheck] = 1;
+ options[OptPreferredTimings] = 1;
+ options[OptNativeTimings] = 1;
+ state = edid_state();
+ int ret = edid_from_file(input, stderr);
+ return ret ? ret : state.parse_edid();
+}
+#endif
diff --git a/edid-decode.h b/edid-decode.h
new file mode 100644
index 0000000..f3fdfd0
--- /dev/null
+++ b/edid-decode.h
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#ifndef __EDID_DECODE_H_
+#define __EDID_DECODE_H_
+
+#include <string>
+#include <vector>
+#include <set>
+#include <string.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#define EDID_PAGE_SIZE 128U
+#define EDID_MAX_BLOCKS 256U
+
+#define RB_ALT (1U << 7)
+
+#define RB_NONE (0U)
+#define RB_CVT_V1 (1U)
+#define RB_CVT_V2 (2U)
+#define RB_CVT_V3 (3U)
+#define RB_GTF (4U)
+
+// Video Timings
+// If interlaced is true, then the vertical blanking
+// for each field is (vfp + vsync + vbp + 0.5), except for
+// the VIC 39 timings that doesn't have the 0.5 constant.
+//
+// The sequence of the various video parameters is as follows:
+//
+// border - front porch - sync - back porch - border - active video
+//
+// Note: this is slightly different from EDID 1.4 which calls
+// 'active video' as 'addressable video' and the EDID 1.4 term
+// 'active video' includes the borders.
+//
+// But since borders are rarely used, the term 'active video' will
+// typically be the same as 'addressable video', and that's how I
+// use it.
+struct timings {
+ // Active horizontal and vertical frame height, excluding any
+ // borders, if present.
+ // Note: for interlaced formats the active field height is vact / 2
+ unsigned hact, vact;
+ unsigned hratio, vratio;
+ unsigned pixclk_khz;
+ // 0: no reduced blanking
+ // 1: CVT reduced blanking version 1
+ // 2: CVT reduced blanking version 2
+ // 2 | RB_ALT: CVT reduced blanking version 2 video-optimized (1000/1001 fps)
+ // 3: CVT reduced blanking version 3
+ // 3 | RB_ALT: v3 with a horizontal blank of 160
+ // 4: GTF Secondary Curve
+ unsigned rb;
+ bool interlaced;
+ // The horizontal frontporch may be negative in GTF calculations,
+ // so use int instead of unsigned for hfp. Example: 292x176@76.
+ int hfp;
+ unsigned hsync;
+ // The backporch may be negative in buggy detailed timings.
+ // So use int instead of unsigned for hbp and vbp.
+ int hbp;
+ bool pos_pol_hsync;
+ // For interlaced formats the vertical front porch of the Even Field
+ // is actually a half-line longer.
+ unsigned vfp, vsync;
+ // For interlaced formats the vertical back porch of the Odd Field
+ // is actually a half-line longer.
+ int vbp;
+ bool pos_pol_vsync;
+ unsigned hborder, vborder;
+ bool even_vtotal; // special for VIC 39
+ bool no_pol_vsync; // digital composite signals have no vsync polarity
+ unsigned hsize_mm, vsize_mm;
+ bool ycbcr420; // YCbCr 4:2:0 encoding
+};
+
+struct timings_ext {
+ timings_ext()
+ {
+ memset(&t, 0, sizeof(t));
+ }
+ timings_ext(unsigned svr, const std::string &_type)
+ {
+ memset(&t, 0, sizeof(t));
+ t.hact = svr;
+ type = _type;
+ }
+ timings_ext(const timings &_t, const std::string &_type, const std::string &_flags)
+ {
+ t = _t;
+ type = _type;
+ flags = _flags;
+ }
+
+ bool is_valid() const { return t.hact; }
+ bool has_svr() const { return t.hact && !t.vact; }
+ unsigned svr() const { return t.hact; }
+ timings t;
+ std::string type;
+ std::string flags;
+};
+
+enum gtf_ip_parm {
+ gtf_ip_vert_freq = 1,
+ gtf_ip_hor_freq,
+ gtf_ip_clk_freq,
+};
+
+typedef std::vector<timings_ext> vec_timings_ext;
+
+struct edid_state {
+ edid_state()
+ {
+ // Global state
+ edid_size = num_blocks = block_nr = 0;
+ max_hor_freq_hz = max_vert_freq_hz = max_pixclk_khz = 0;
+ min_hor_freq_hz = 0xffffff;
+ min_vert_freq_hz = 0xffffffff;
+ dtd_max_vsize_mm = dtd_max_hsize_mm = 0;
+ warnings = failures = 0;
+ has_cta = has_dispid = false;
+ hide_serial_numbers = false;
+
+ // Base block state
+ base.edid_minor = 0;
+ base.has_name_descriptor = base.has_display_range_descriptor =
+ base.has_serial_number = base.has_serial_string =
+ base.supports_continuous_freq = base.supports_gtf =
+ base.supports_cvt = base.seen_non_detailed_descriptor =
+ base.has_640x480p60_est_timing = base.has_spwg =
+ base.preferred_is_also_native = false;
+ base.supports_sec_gtf = false;
+ base.sec_gtf_start_freq = 0;
+ base.C = base.M = base.K = base.J = 0;
+ base.max_pos_neg_hor_freq_khz = 0;
+ base.detailed_block_cnt = base.dtd_cnt = 0;
+
+ base.min_display_hor_freq_hz = base.max_display_hor_freq_hz =
+ base.min_display_vert_freq_hz = base.max_display_vert_freq_hz =
+ base.max_display_pixclk_khz = base.max_display_width_mm =
+ base.max_display_height_mm = 0;
+
+ // CTA-861 block state
+ cta.has_vic_1 = cta.first_svd_might_be_preferred = cta.has_sldb =
+ cta.has_hdmi = cta.has_vcdb = cta.has_vfpdb = false;
+ cta.last_block_was_hdmi_vsdb = cta.have_hf_vsdb = cta.have_hf_scdb = false;
+ cta.first_block = cta.first_svd = true;
+ cta.supported_hdmi_vic_codes = cta.supported_hdmi_vic_vsb_codes = 0;
+ memset(cta.vics, 0, sizeof(cta.vics));
+ memset(cta.preparsed_has_vic, 0, sizeof(cta.preparsed_has_vic));
+ cta.preparsed_phys_addr = 0xffff;
+ cta.preparsed_speaker_count = 0;
+ cta.preparsed_sld = false;
+ cta.preparsed_sld_has_coord = false;
+ cta.preparsed_total_dtds = 0;
+ cta.preparsed_total_vtdbs = 0;
+ cta.preparsed_has_t8vtdb = false;
+
+ // DisplayID block state
+ dispid.version = 0;
+ dispid.native_width = dispid.native_height = 0;
+ dispid.preparsed_color_ids = dispid.preparsed_xfer_ids = 0;
+ dispid.preparsed_displayid_blocks = 0;
+ dispid.is_base_block = true;
+ dispid.is_display = dispid.has_product_identification =
+ dispid.has_display_parameters = dispid.has_type_1_7 =
+ dispid.has_display_interface_features = false;
+
+ // Block Map block state
+ block_map.saw_block_1 = false;
+ block_map.saw_block_128 = false;
+ }
+
+ // Global state
+ unsigned edid_size;
+ unsigned num_blocks;
+ unsigned block_nr;
+ std::string block;
+ std::string data_block;
+ bool has_cta;
+ bool has_dispid;
+ bool hide_serial_numbers;
+
+ unsigned min_hor_freq_hz;
+ unsigned max_hor_freq_hz;
+ double min_vert_freq_hz;
+ double max_vert_freq_hz;
+ unsigned max_pixclk_khz;
+ unsigned dtd_max_hsize_mm;
+ unsigned dtd_max_vsize_mm;
+
+ unsigned warnings;
+ unsigned failures;
+
+ // Base block state
+ struct {
+ unsigned edid_minor;
+ bool has_name_descriptor;
+ bool has_display_range_descriptor;
+ bool has_serial_number;
+ bool has_serial_string;
+ bool supports_continuous_freq;
+ bool supports_gtf;
+ bool supports_sec_gtf;
+ unsigned sec_gtf_start_freq;
+ double C, M, K, J;
+ bool supports_cvt;
+ bool has_spwg;
+ unsigned detailed_block_cnt;
+ unsigned dtd_cnt;
+ bool seen_non_detailed_descriptor;
+ bool has_640x480p60_est_timing;
+ bool preferred_is_also_native;
+ timings_ext preferred_timing;
+
+ unsigned min_display_hor_freq_hz;
+ unsigned max_display_hor_freq_hz;
+ unsigned min_display_vert_freq_hz;
+ unsigned max_display_vert_freq_hz;
+ unsigned max_display_pixclk_khz;
+ unsigned max_display_width_mm;
+ unsigned max_display_height_mm;
+ unsigned max_pos_neg_hor_freq_khz;
+ } base;
+
+ // CTA-861 block state
+ struct {
+ unsigned preparsed_total_dtds;
+ vec_timings_ext vec_dtds;
+ unsigned preparsed_total_vtdbs;
+ vec_timings_ext vec_vtdbs;
+ vec_timings_ext preferred_timings;
+ bool preparsed_has_t8vtdb;
+ // Keep track of the found Tag/Extended Tag pairs.
+ // The unsigned value is equal to: (tag << 8) | ext_tag
+ std::set<unsigned> found_tags;
+ timings_ext t8vtdb;
+ vec_timings_ext native_timings;
+ bool has_vic_1;
+ bool first_svd_might_be_preferred;
+ unsigned char byte3;
+ bool has_hdmi;
+ bool has_vcdb;
+ bool has_vfpdb;
+ unsigned preparsed_speaker_count;
+ bool preparsed_sld_has_coord;
+ bool preparsed_sld;
+ bool has_sldb;
+ unsigned short preparsed_phys_addr;
+ bool last_block_was_hdmi_vsdb;
+ bool have_hf_vsdb, have_hf_scdb;
+ bool first_block;
+ bool first_svd;
+ unsigned supported_hdmi_vic_codes;
+ unsigned supported_hdmi_vic_vsb_codes;
+ unsigned short vics[256][2];
+ bool preparsed_has_vic[2][256];
+ std::vector<unsigned char> preparsed_svds[2];
+ } cta;
+
+ // DisplayID block state
+ struct {
+ unsigned char version;
+ unsigned short preparsed_color_ids;
+ unsigned short preparsed_xfer_ids;
+ unsigned preparsed_displayid_blocks;
+ bool is_base_block;
+ bool is_display;
+ bool has_product_identification;
+ bool has_display_parameters;
+ bool has_type_1_7;
+ bool has_display_interface_features;
+ vec_timings_ext preferred_timings;
+ unsigned native_width, native_height;
+ // Keep track of the found CTA-861 Tag/Extended Tag pairs.
+ // The unsigned value is equal to: (tag << 8) | ext_tag
+ std::set<unsigned> found_tags;
+ } dispid;
+
+ // Block Map block state
+ struct {
+ bool saw_block_1;
+ bool saw_block_128;
+ } block_map;
+
+ std::string dtd_type(unsigned dtd);
+ std::string dtd_type() { return dtd_type(base.dtd_cnt); }
+ bool print_timings(const char *prefix, const struct timings *t,
+ const char *type, const char *flags = "",
+ bool detailed = false, bool do_checks = true);
+ bool print_timings(const char *prefix, const struct timings_ext &t,
+ bool detailed = false, bool do_checks = true)
+ {
+ return print_timings(prefix, &t.t, t.type.c_str(), t.flags.c_str(),
+ detailed, do_checks);
+ };
+ bool match_timings(const timings &t1, const timings &t2);
+ timings calc_gtf_mode(unsigned h_pixels, unsigned v_lines,
+ double ip_freq_rqd, bool int_rqd = false,
+ enum gtf_ip_parm ip_parm = gtf_ip_vert_freq,
+ bool margins_rqd = false, bool secondary = false,
+ double C = 40, double M = 600, double K = 128, double J = 20);
+ void edid_gtf_mode(unsigned refresh, struct timings &t);
+ timings calc_cvt_mode(unsigned h_pixels, unsigned v_lines,
+ double ip_freq_rqd, unsigned rb, bool int_rqd = false,
+ bool margins_rqd = false, bool alt = false,
+ unsigned rb_h_blank = 0);
+ void edid_cvt_mode(unsigned refresh, struct timings &t);
+ void detailed_cvt_descriptor(const char *prefix, const unsigned char *x, bool first);
+ void print_standard_timing(const char *prefix, unsigned char b1, unsigned char b2,
+ bool gtf_only = false, bool show_both = false);
+ void detailed_display_range_limits(const unsigned char *x);
+ void detailed_epi(const unsigned char *x);
+ void detailed_timings(const char *prefix, const unsigned char *x,
+ bool base_or_cta = true);
+ void preparse_detailed_block(const unsigned char *x);
+ void detailed_block(const unsigned char *x);
+ void parse_base_block(const unsigned char *x);
+ void check_base_block();
+ void list_dmts();
+ void list_established_timings();
+
+ void print_vic_index(const char *prefix, unsigned idx, const char *suffix, bool ycbcr420 = false);
+ void hdmi_latency(unsigned char vid_lat, unsigned char aud_lat, bool is_ilaced);
+ void cta_vcdb(const unsigned char *x, unsigned length);
+ void cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420);
+ void cta_y420cmdb(const unsigned char *x, unsigned length);
+ void cta_vfpdb(const unsigned char *x, unsigned length);
+ void cta_rcdb(const unsigned char *x, unsigned length);
+ void cta_sldb(const unsigned char *x, unsigned length);
+ void cta_preparse_sldb(const unsigned char *x, unsigned length);
+ void cta_hdmi_block(const unsigned char *x, unsigned length);
+ void cta_displayid_type_7(const unsigned char *x, unsigned length);
+ void cta_displayid_type_8(const unsigned char *x, unsigned length);
+ void cta_displayid_type_10(const unsigned char *x, unsigned length);
+ void cta_ext_block(const unsigned char *x, unsigned length, bool duplicate);
+ void cta_block(const unsigned char *x, bool duplicate);
+ void preparse_cta_block(const unsigned char *x);
+ void parse_cta_block(const unsigned char *x);
+ void cta_resolve_svr(vec_timings_ext::iterator iter);
+ void cta_resolve_svrs();
+ void check_cta_blocks();
+ void cta_list_vics();
+ void cta_list_hdmi_vics();
+
+ void parse_digital_interface(const unsigned char *x);
+ void parse_display_device(const unsigned char *x);
+ void parse_display_caps(const unsigned char *x);
+ void parse_display_xfer(const unsigned char *x);
+ void parse_di_ext_block(const unsigned char *x);
+
+ void check_displayid_datablock_revision(unsigned char hdr,
+ unsigned char valid_flags = 0,
+ unsigned char rev = 0);
+ void parse_displayid_product_id(const unsigned char *x);
+ std::string product_type(unsigned char x, bool heading);
+ void parse_displayid_interface_features(const unsigned char *x);
+ void parse_displayid_parameters(const unsigned char *x);
+ void parse_displayid_parameters_v2(const unsigned char *x, unsigned block_rev);
+ void parse_displayid_display_intf(const unsigned char *x);
+ void parse_displayid_color_characteristics(const unsigned char *x);
+ void parse_displayid_transfer_characteristics(const unsigned char *x);
+ void parse_displayid_stereo_display_intf(const unsigned char *x);
+ void parse_displayid_type_1_7_timing(const unsigned char *x,
+ bool type7, unsigned block_rev, bool is_cta = false);
+ void parse_displayid_type_2_timing(const unsigned char *x);
+ void parse_displayid_type_3_timing(const unsigned char *x);
+ void parse_displayid_type_4_8_timing(unsigned char type, unsigned short id, bool is_cta = false);
+ void parse_displayid_video_timing_range_limits(const unsigned char *x);
+ void parse_displayid_string(const unsigned char *x);
+ void parse_displayid_display_device(const unsigned char *x);
+ void parse_displayid_intf_power_sequencing(const unsigned char *x);
+ void parse_displayid_type_5_timing(const unsigned char *x);
+ void parse_displayid_tiled_display_topology(const unsigned char *x, bool is_v2);
+ void parse_displayid_type_6_timing(const unsigned char *x);
+ void parse_displayid_type_9_timing(const unsigned char *x);
+ void parse_displayid_dynamic_video_timings_range_limits(const unsigned char *x);
+ void parse_displayid_ContainerID(const unsigned char *x);
+ void parse_displayid_type_10_timing(const unsigned char *x, bool is_cta = false);
+ void preparse_displayid_block(const unsigned char *x);
+ void parse_displayid_block(const unsigned char *x);
+ void parse_displayid_vesa(const unsigned char *x);
+ void parse_displayid_cta_data_block(const unsigned char *x);
+ void check_displayid_blocks();
+
+ void parse_vtb_ext_block(const unsigned char *x);
+
+ void parse_string_table(const unsigned char *x);
+ void parse_ls_ext_block(const unsigned char *x);
+
+ void parse_block_map(const unsigned char *x);
+
+ void preparse_extension(const unsigned char *x);
+ void parse_extension(const unsigned char *x);
+ int parse_edid();
+};
+
+static inline void add_str(std::string &s, const std::string &add)
+{
+ if (s.empty())
+ s = add;
+ else if (!add.empty())
+ s = s + ", " + add;
+}
+
+void msg(bool is_warn, const char *fmt, ...);
+
+#ifdef _WIN32
+
+#define warn(fmt, ...) msg(true, fmt, __VA_ARGS__)
+#define warn_once(fmt, ...) \
+ do { \
+ static bool shown_warn; \
+ if (!shown_warn) { \
+ shown_warn = true; \
+ msg(true, fmt, __VA_ARGS__); \
+ } \
+ } while (0)
+#define fail(fmt, ...) msg(false, fmt, __VA_ARGS__)
+
+#else
+
+#define warn(fmt, args...) msg(true, fmt, ##args)
+#define warn_once(fmt, args...) \
+ do { \
+ static bool shown_warn; \
+ if (!shown_warn) { \
+ shown_warn = true; \
+ msg(true, fmt, ##args); \
+ } \
+ } while (0)
+#define fail(fmt, args...) msg(false, fmt, ##args)
+
+#endif
+
+void do_checksum(const char *prefix, const unsigned char *x, size_t len);
+std::string utohex(unsigned char x);
+std::string ouitohex(unsigned oui);
+std::string containerid2s(const unsigned char *x);
+bool memchk(const unsigned char *x, unsigned len, unsigned char v = 0);
+void hex_block(const char *prefix, const unsigned char *x, unsigned length,
+ bool show_ascii = true, unsigned step = 16);
+std::string block_name(unsigned char block);
+void calc_ratio(struct timings *t);
+const char *oui_name(unsigned oui, bool reverse = false);
+
+bool timings_close_match(const timings &t1, const timings &t2);
+const struct timings *find_dmt_id(unsigned char dmt_id);
+const struct timings *close_match_to_dmt(const timings &t, unsigned &dmt);
+const struct timings *find_vic_id(unsigned char vic);
+const struct timings *find_hdmi_vic_id(unsigned char hdmi_vic);
+const struct timings *cta_close_match_to_vic(const timings &t, unsigned &vic);
+unsigned char hdmi_vic_to_vic(unsigned char hdmi_vic);
+char *extract_string(const unsigned char *x, unsigned len);
+
+#endif
diff --git a/parse-base-block.cpp b/parse-base-block.cpp
new file mode 100644
index 0000000..2b1a6e0
--- /dev/null
+++ b/parse-base-block.cpp
@@ -0,0 +1,1710 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "edid-decode.h"
+
+static char *manufacturer_name(const unsigned char *x)
+{
+ static char name[4];
+
+ name[0] = ((x[0] & 0x7c) >> 2) + '@';
+ name[1] = ((x[0] & 0x03) << 3) + ((x[1] & 0xe0) >> 5) + '@';
+ name[2] = (x[1] & 0x1f) + '@';
+ name[3] = 0;
+
+ if (!isupper(name[0]) || !isupper(name[1]) || !isupper(name[2]))
+ fail("Manufacturer name field contains garbage.\n");
+
+ return name;
+}
+
+static const struct {
+ unsigned dmt_id;
+ unsigned std_id;
+ unsigned cvt_id;
+ struct timings t;
+} dmt_timings[] = {
+ { 0x01, 0x0000, 0x000000, { 640, 350, 64, 35, 31500, 0, false,
+ 32, 64, 96, true, 32, 3, 60, false } },
+
+ { 0x02, 0x3119, 0x000000, { 640, 400, 16, 10, 31500, 0, false,
+ 32, 64, 96, false, 1, 3, 41, true } },
+
+ { 0x03, 0x0000, 0x000000, { 720, 400, 9, 5, 35500, 0, false,
+ 36, 72, 108, false, 1, 3, 42, true } },
+
+ { 0x04, 0x3140, 0x000000, { 640, 480, 4, 3, 25175, 0, false,
+ 8, 96, 40, false, 2, 2, 25, false, 8, 8 } },
+ { 0x05, 0x314c, 0x000000, { 640, 480, 4, 3, 31500, 0, false,
+ 16, 40, 120, false, 1, 3, 20, false, 8, 8 } },
+ { 0x06, 0x314f, 0x000000, { 640, 480, 4, 3, 31500, 0, false,
+ 16, 64, 120, false, 1, 3, 16, false } },
+ { 0x07, 0x3159, 0x000000, { 640, 480, 4, 3, 36000, 0, false,
+ 56, 56, 80, false, 1, 3, 25, false } },
+
+ { 0x08, 0x0000, 0x000000, { 800, 600, 4, 3, 36000, 0, false,
+ 24, 72, 128, true, 1, 2, 22, true } },
+ { 0x09, 0x4540, 0x000000, { 800, 600, 4, 3, 40000, 0, false,
+ 40, 128, 88, true, 1, 4, 23, true } },
+ { 0x0a, 0x454c, 0x000000, { 800, 600, 4, 3, 50000, 0, false,
+ 56, 120, 64, true, 37, 6, 23, true } },
+ { 0x0b, 0x454f, 0x000000, { 800, 600, 4, 3, 49500, 0, false,
+ 16, 80, 160, true, 1, 3, 21, true } },
+ { 0x0c, 0x4559, 0x000000, { 800, 600, 4, 3, 56250, 0, false,
+ 32, 64, 152, true, 1, 3, 27, true } },
+ { 0x0d, 0x0000, 0x000000, { 800, 600, 4, 3, 73250, 1, false,
+ 48, 32, 80, true, 3, 4, 29, false } },
+
+ { 0x0e, 0x0000, 0x000000, { 848, 480, 16, 9, 33750, 0, false,
+ 16, 112, 112, true, 6, 8, 23, true } },
+
+ { 0x0f, 0x0000, 0x000000, { 1024, 768, 4, 3, 44900, 0, true,
+ 8, 176, 56, true, 0, 4, 20, true } },
+ { 0x10, 0x6140, 0x000000, { 1024, 768, 4, 3, 65000, 0, false,
+ 24, 136, 160, false, 3, 6, 29, false } },
+ { 0x11, 0x614c, 0x000000, { 1024, 768, 4, 3, 75000, 0, false,
+ 24, 136, 144, false, 3, 6, 29, false } },
+ { 0x12, 0x614f, 0x000000, { 1024, 768, 4, 3, 78750, 0, false,
+ 16, 96, 176, true, 1, 3, 28, true } },
+ { 0x13, 0x6159, 0x000000, { 1024, 768, 4, 3, 94500, 0, false,
+ 48, 96, 208, true, 1, 3, 36, true } },
+ { 0x14, 0x0000, 0x000000, { 1024, 768, 4, 3, 115500, 1, false,
+ 48, 32, 80, true, 3, 4, 38, false } },
+
+ { 0x15, 0x714f, 0x000000, { 1152, 864, 4, 3, 108000, 0, false,
+ 64, 128, 256, true, 1, 3, 32, true } },
+
+ { 0x55, 0x81c0, 0x000000, { 1280, 720, 16, 9, 74250, 0, false,
+ 110, 40, 220, true, 5, 5, 20, true } },
+
+ { 0x16, 0x0000, 0x7f1c21, { 1280, 768, 5, 3, 68250, 1, false,
+ 48, 32, 80, true, 3, 7, 12, false } },
+ { 0x17, 0x0000, 0x7f1c28, { 1280, 768, 5, 3, 79500, 0, false,
+ 64, 128, 192, false, 3, 7, 20, true } },
+ { 0x18, 0x0000, 0x7f1c44, { 1280, 768, 5, 3, 102250, 0, false,
+ 80, 128, 208, false, 3, 7, 27, true } },
+ { 0x19, 0x0000, 0x7f1c62, { 1280, 768, 5, 3, 117500, 0, false,
+ 80, 136, 216, false, 3, 7, 31, true } },
+ { 0x1a, 0x0000, 0x000000, { 1280, 768, 5, 3, 140250, 0, false,
+ 48, 32, 80, true, 3, 7, 35, false } },
+
+ { 0x1b, 0x0000, 0x8f1821, { 1280, 800, 16, 10, 71000, 1, false,
+ 48, 32, 80, true, 3, 6, 14, false } },
+ { 0x1c, 0x8100, 0x8f1828, { 1280, 800, 16, 10, 83500, 0, false,
+ 72, 128, 200, false, 3, 6, 22, true } },
+ { 0x1d, 0x810f, 0x8f1844, { 1280, 800, 16, 10, 106500, 0, false,
+ 80, 128, 208, false, 3, 6, 29, true } },
+ { 0x1e, 0x8119, 0x8f1862, { 1280, 800, 16, 10, 122500, 0, false,
+ 80, 136, 216, false, 3, 6, 34, true } },
+ { 0x1f, 0x0000, 0x000000, { 1280, 800, 16, 10, 146250, 1, false,
+ 48, 32, 80, true, 3, 6, 38, false } },
+
+ { 0x20, 0x8140, 0x000000, { 1280, 960, 4, 3, 108000, 0, false,
+ 96, 112, 312, true, 1, 3, 36, true } },
+ { 0x21, 0x8159, 0x000000, { 1280, 960, 4, 3, 148500, 0, false,
+ 64, 160, 224, true, 1, 3, 47, true } },
+ { 0x22, 0x0000, 0x000000, { 1280, 960, 4, 3, 175500, 1, false,
+ 48, 32, 80, true, 3, 4, 50, false } },
+
+ { 0x23, 0x8180, 0x000000, { 1280, 1024, 5, 4, 108000, 0, false,
+ 48, 112, 248, true, 1, 3, 38, true } },
+ { 0x24, 0x818f, 0x000000, { 1280, 1024, 5, 4, 135000, 0, false,
+ 16, 144, 248, true, 1, 3, 38, true } },
+ { 0x25, 0x8199, 0x000000, { 1280, 1024, 5, 4, 157500, 0, false,
+ 64, 160, 224, true, 1, 3, 44, true } },
+ { 0x26, 0x0000, 0x000000, { 1280, 1024, 5, 4, 187250, 1, false,
+ 48, 32, 80, true, 3, 7, 50, false } },
+
+ { 0x27, 0x0000, 0x000000, { 1360, 768, 85, 48, 85500, 0, false,
+ 64, 112, 256, true, 3, 6, 18, true } },
+ { 0x28, 0x0000, 0x000000, { 1360, 768, 85, 48, 148250, 1, false,
+ 48, 32, 80, true, 3, 5, 37, false } },
+
+ { 0x51, 0x0000, 0x000000, { 1366, 768, 85, 48, 85500, 0, false,
+ 70, 143, 213, true, 3, 3, 24, true } },
+ { 0x56, 0x0000, 0x000000, { 1366, 768, 85, 48, 72000, 1, false,
+ 14, 56, 64, true, 1, 3, 28, true } },
+
+ { 0x29, 0x0000, 0x0c2021, { 1400, 1050, 4, 3, 101000, 1, false,
+ 48, 32, 80, true, 3, 4, 23, false } },
+ { 0x2a, 0x9040, 0x0c2028, { 1400, 1050, 4, 3, 121750, 0, false,
+ 88, 144, 232, false, 3, 4, 32, true } },
+ { 0x2b, 0x904f, 0x0c2044, { 1400, 1050, 4, 3, 156000, 0, false,
+ 104, 144, 248, false, 3, 4, 42, true } },
+ { 0x2c, 0x9059, 0x0c2062, { 1400, 1050, 4, 3, 179500, 0, false,
+ 104, 152, 256, false, 3, 4, 48, true } },
+ { 0x2d, 0x0000, 0x000000, { 1400, 1050, 4, 3, 208000, 1, false,
+ 48, 32, 80, true, 3, 4, 55, false } },
+
+ { 0x2e, 0x0000, 0xc11821, { 1440, 900, 16, 10, 88750, 1, false,
+ 48, 32, 80, true, 3, 6, 17, false } },
+ { 0x2f, 0x9500, 0xc11828, { 1440, 900, 16, 10, 106500, 0, false,
+ 80, 152, 232, false, 3, 6, 25, true } },
+ { 0x30, 0x950f, 0xc11844, { 1440, 900, 16, 10, 136750, 0, false,
+ 96, 152, 248, false, 3, 6, 33, true } },
+ { 0x31, 0x9519, 0xc11868, { 1440, 900, 16, 10, 157000, 0, false,
+ 104, 152, 256, false, 3, 6, 39, true } },
+ { 0x32, 0x0000, 0x000000, { 1440, 900, 16, 10, 182750, 1, false,
+ 48, 32, 80, true, 3, 6, 44, false } },
+
+ { 0x53, 0xa9c0, 0x000000, { 1600, 900, 16, 9, 108000, 1, false,
+ 24, 80, 96, true, 1, 3, 96, true } },
+
+ { 0x33, 0xa940, 0x000000, { 1600, 1200, 4, 3, 162000, 0, false,
+ 64, 192, 304, true, 1, 3, 46, true } },
+ { 0x34, 0xa945, 0x000000, { 1600, 1200, 4, 3, 175500, 0, false,
+ 64, 192, 304, true, 1, 3, 46, true } },
+ { 0x35, 0xa94a, 0x000000, { 1600, 1200, 4, 3, 189000, 0, false,
+ 64, 192, 304, true, 1, 3, 46, true } },
+ { 0x36, 0xa94f, 0x000000, { 1600, 1200, 4, 3, 202500, 0, false,
+ 64, 192, 304, true, 1, 3, 46, true } },
+ { 0x37, 0xa959, 0x000000, { 1600, 1200, 4, 3, 229500, 0, false,
+ 64, 192, 304, true, 1, 3, 46, true } },
+ { 0x38, 0x0000, 0x000000, { 1600, 1200, 4, 3, 268250, 1, false,
+ 48, 32, 80, true, 3, 4, 64, false } },
+
+ { 0x39, 0x0000, 0x0c2821, { 1680, 1050, 16, 10, 119000, 1, false,
+ 48, 32, 80, true, 3, 6, 21, false } },
+ { 0x3a, 0xb300, 0x0c2828, { 1680, 1050, 16, 10, 146250, 0, false,
+ 104, 176, 280, false, 3, 6, 30, true } },
+ { 0x3b, 0xb30f, 0x0c2844, { 1680, 1050, 16, 10, 187000, 0, false,
+ 120, 176, 296, false, 3, 6, 40, true } },
+ { 0x3c, 0xb319, 0x0c2868, { 1680, 1050, 16, 10, 214750, 0, false,
+ 128, 176, 304, false, 3, 6, 46, true } },
+ { 0x3d, 0x0000, 0x000000, { 1680, 1050, 16, 10, 245500, 1, false,
+ 48, 32, 80, true, 3, 6, 53, false } },
+
+ { 0x3e, 0xc140, 0x000000, { 1792, 1344, 4, 3, 204750, 0, false,
+ 128, 200, 328, false, 1, 3, 46, true } },
+ { 0x3f, 0xc14f, 0x000000, { 1792, 1344, 4, 3, 261000, 0, false,
+ 96, 216, 352, false, 1, 3, 69, true } },
+ { 0x40, 0x0000, 0x000000, { 1792, 1344, 4, 3, 333250, 1, false,
+ 48, 32, 80, true, 3, 4, 72, false } },
+
+ { 0x41, 0xc940, 0x000000, { 1856, 1392, 4, 3, 218250, 0, false,
+ 96, 224, 352, false, 1, 3, 43, true } },
+ { 0x42, 0xc94f, 0x000000, { 1856, 1392, 4, 3, 288000, 0, false,
+ 128, 224, 352, false, 1, 3, 104, true } },
+ { 0x43, 0x0000, 0x000000, { 1856, 1392, 4, 3, 356500, 1, false,
+ 48, 32, 80, true, 3, 4, 74, false } },
+
+ { 0x52, 0xd1c0, 0x000000, { 1920, 1080, 16, 9, 148500, 0, false,
+ 88, 44, 148, true, 4, 5, 36, true } },
+
+ { 0x44, 0x0000, 0x572821, { 1920, 1200, 16, 10, 154000, 1, false,
+ 48, 32, 80, true, 3, 6, 26, false } },
+ { 0x45, 0xd100, 0x572828, { 1920, 1200, 16, 10, 193250, 0, false,
+ 136, 200, 336, false, 3, 6, 36, true } },
+ { 0x46, 0xd10f, 0x572844, { 1920, 1200, 16, 10, 245250, 0, false,
+ 136, 208, 344, false, 3, 6, 46, true } },
+ { 0x47, 0xd119, 0x572862, { 1920, 1200, 16, 10, 281250, 0, false,
+ 144, 208, 352, false, 3, 6, 53, true } },
+ { 0x48, 0x0000, 0x000000, { 1920, 1200, 16, 10, 317000, 1, false,
+ 48, 32, 80, true, 3, 6, 62, false } },
+
+ { 0x49, 0xd140, 0x000000, { 1920, 1440, 4, 3, 234000, 0, false,
+ 128, 208, 344, false, 1, 3, 56, true } },
+ { 0x4a, 0xd14f, 0x000000, { 1920, 1440, 4, 3, 297000, 0, false,
+ 144, 224, 352, false, 1, 3, 56, true } },
+ { 0x4b, 0x0000, 0x000000, { 1920, 1440, 4, 3, 380500, 1, false,
+ 48, 32, 80, true, 2, 3, 78, false } },
+
+ { 0x54, 0xe1c0, 0x000000, { 2048, 1152, 16, 9, 162000, 1, false,
+ 26, 80, 96, true, 1, 3, 44, true } },
+
+ { 0x4c, 0x0000, 0x1f3821, { 2560, 1600, 16, 10, 268500, 1, false,
+ 48, 32, 80, true, 3, 6, 37, false } },
+ { 0x4d, 0x0000, 0x1f3828, { 2560, 1600, 16, 10, 348500, 0, false,
+ 192, 280, 472, false, 3, 6, 49, true } },
+ { 0x4e, 0x0000, 0x1f3844, { 2560, 1600, 16, 10, 443250, 0, false,
+ 208, 280, 488, false, 3, 6, 63, true } },
+ { 0x4f, 0x0000, 0x1f3862, { 2560, 1600, 16, 10, 505250, 0, false,
+ 208, 280, 488, false, 3, 6, 73, true } },
+ { 0x50, 0x0000, 0x000000, { 2560, 1600, 16, 10, 552750, 1, false,
+ 48, 32, 80, true, 3, 6, 85, false } },
+
+ { 0x57, 0x0000, 0x000000, { 4096, 2160, 256, 135, 556744, 1, false,
+ 8, 32, 40, true, 48, 8, 6, false } },
+ { 0x58, 0x0000, 0x000000, { 4096, 2160, 256, 135, 556188, 1, false,
+ 8, 32, 40, true, 48, 8, 6, false } },
+};
+
+// The timings for the IBM/Apple modes are copied from the linux
+// kernel timings in drivers/gpu/drm/drm_edid.c, except for the
+// 1152x870 Apple format, which is copied from
+// drivers/video/fbdev/macmodes.c since the drm_edid.c version
+// describes a 1152x864 format.
+static const struct {
+ unsigned dmt_id;
+ struct timings t;
+ const char *type;
+} established_timings12[] = {
+ /* 0x23 bit 7 - 0 */
+ { 0x00, { 720, 400, 9, 5, 28320, 0, false,
+ 18, 108, 54, false, 21, 2, 26, true }, "IBM" },
+ { 0x00, { 720, 400, 9, 5, 35500, 0, false,
+ 18, 108, 54, false, 12, 2, 35, true }, "IBM" },
+ { 0x04 },
+ { 0x00, { 640, 480, 4, 3, 30240, 0, false,
+ 64, 64, 96, false, 3, 3, 39, false }, "Apple" },
+ { 0x05 },
+ { 0x06 },
+ { 0x08 },
+ { 0x09 },
+ /* 0x24 bit 7 - 0 */
+ { 0x0a },
+ { 0x0b },
+ { 0x00, { 832, 624, 4, 3, 57284, 0, false,
+ 32, 64, 224, false, 1, 3, 39, false }, "Apple" },
+ { 0x0f },
+ { 0x10 },
+ { 0x11 },
+ { 0x12 },
+ { 0x24 },
+ /* 0x25 bit 7 */
+ { 0x00, { 1152, 870, 192, 145, 100000, 0, false,
+ 48, 128, 128, true, 3, 3, 39, true }, "Apple" },
+};
+
+// The bits in the Established Timings III map to DMT timings,
+// this array has the DMT IDs.
+static const unsigned char established_timings3_dmt_ids[] = {
+ /* 0x06 bit 7 - 0 */
+ 0x01, // 640x350@85
+ 0x02, // 640x400@85
+ 0x03, // 720x400@85
+ 0x07, // 640x480@85
+ 0x0e, // 848x480@60
+ 0x0c, // 800x600@85
+ 0x13, // 1024x768@85
+ 0x15, // 1152x864@75
+ /* 0x07 bit 7 - 0 */
+ 0x16, // 1280x768@60 RB
+ 0x17, // 1280x768@60
+ 0x18, // 1280x768@75
+ 0x19, // 1280x768@85
+ 0x20, // 1280x960@60
+ 0x21, // 1280x960@85
+ 0x23, // 1280x1024@60
+ 0x25, // 1280x1024@85
+ /* 0x08 bit 7 - 0 */
+ 0x27, // 1360x768@60
+ 0x2e, // 1440x900@60 RB
+ 0x2f, // 1440x900@60
+ 0x30, // 1440x900@75
+ 0x31, // 1440x900@85
+ 0x29, // 1400x1050@60 RB
+ 0x2a, // 1400x1050@60
+ 0x2b, // 1400x1050@75
+ /* 0x09 bit 7 - 0 */
+ 0x2c, // 1400x1050@85
+ 0x39, // 1680x1050@60 RB
+ 0x3a, // 1680x1050@60
+ 0x3b, // 1680x1050@75
+ 0x3c, // 1680x1050@85
+ 0x33, // 1600x1200@60
+ 0x34, // 1600x1200@65
+ 0x35, // 1600x1200@70
+ /* 0x0a bit 7 - 0 */
+ 0x36, // 1600x1200@75
+ 0x37, // 1600x1200@85
+ 0x3e, // 1792x1344@60
+ 0x3f, // 1792x1344@75
+ 0x41, // 1856x1392@60
+ 0x42, // 1856x1392@75
+ 0x44, // 1920x1200@60 RB
+ 0x45, // 1920x1200@60
+ /* 0x0b bit 7 - 4 */
+ 0x46, // 1920x1200@75
+ 0x47, // 1920x1200@85
+ 0x49, // 1920x1440@60
+ 0x4a, // 1920x1440@75
+};
+
+const struct timings *find_dmt_id(unsigned char dmt_id)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(dmt_timings); i++)
+ if (dmt_timings[i].dmt_id == dmt_id)
+ return &dmt_timings[i].t;
+ return NULL;
+}
+
+static const struct timings *find_std_id(unsigned short std_id, unsigned char &dmt_id)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(dmt_timings); i++)
+ if (dmt_timings[i].std_id == std_id) {
+ dmt_id = dmt_timings[i].dmt_id;
+ return &dmt_timings[i].t;
+ }
+ return NULL;
+}
+
+void edid_state::list_established_timings()
+{
+ printf("Established Timings I & II, 'Byte' is the EDID address:\n\n");
+ for (unsigned i = 0; i < ARRAY_SIZE(established_timings12); i++) {
+ unsigned char dmt_id = established_timings12[i].dmt_id;
+ const struct timings *t;
+ char type[16];
+
+ if (dmt_id) {
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ t = find_dmt_id(dmt_id);
+ } else {
+ t = &established_timings12[i].t;
+ sprintf(type, "%-8s", established_timings12[i].type);
+ }
+ printf("Byte 0x%02x, Bit %u: ", 0x23 + i / 8, 7 - i % 8);
+ print_timings("", t, type, "", false, false);
+ }
+ printf("\nEstablished timings III, 'Byte' is the offset from the start of the descriptor:\n\n");
+ for (unsigned i = 0; i < ARRAY_SIZE(established_timings3_dmt_ids); i++) {
+ unsigned char dmt_id = established_timings3_dmt_ids[i];
+ char type[16];
+
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ printf("Byte 0x%02x, Bit %u: ", 6 + i / 8, 7 - i % 8);
+ print_timings("", find_dmt_id(dmt_id), type, "", false, false);
+ }
+}
+
+const struct timings *close_match_to_dmt(const timings &t, unsigned &dmt)
+{
+ for (unsigned i = 0; i < ARRAY_SIZE(dmt_timings); i++) {
+ if (timings_close_match(t, dmt_timings[i].t)) {
+ dmt = dmt_timings[i].dmt_id;
+ return &dmt_timings[i].t;
+ }
+ }
+ dmt = 0;
+ return NULL;
+}
+
+void edid_state::list_dmts()
+{
+ char type[16];
+
+ for (unsigned i = 0; i < ARRAY_SIZE(dmt_timings); i++) {
+ sprintf(type, "DMT 0x%02x", dmt_timings[i].dmt_id);
+ std::string flags;
+ if (dmt_timings[i].std_id)
+ flags += std::string("STD: ") +
+ utohex(dmt_timings[i].std_id >> 8) + " " +
+ utohex(dmt_timings[i].std_id & 0xff);
+ if (dmt_timings[i].cvt_id)
+ add_str(flags, std::string("CVT: ") +
+ utohex(dmt_timings[i].cvt_id >> 16) + " " +
+ utohex((dmt_timings[i].cvt_id >> 8) & 0xff) + " " +
+ utohex(dmt_timings[i].cvt_id & 0xff));
+ print_timings("", &dmt_timings[i].t, type, flags.c_str(), false, false);
+ }
+}
+
+void edid_state::detailed_cvt_descriptor(const char *prefix, const unsigned char *x, bool first)
+{
+ static const unsigned char empty[3] = { 0, 0, 0 };
+ struct timings cvt_t = {};
+ unsigned char preferred;
+
+ if (!first && !memcmp(x, empty, 3))
+ return;
+
+ cvt_t.vact = x[0];
+ if (!cvt_t.vact)
+ fail("CVT byte 0 is 0, which is a reserved value.\n");
+ cvt_t.vact |= (x[1] & 0xf0) << 4;
+ cvt_t.vact++;
+ cvt_t.vact *= 2;
+
+ switch (x[1] & 0x0c) {
+ case 0x00:
+ default: /* avoids 'width/ratio may be used uninitialized' warnings */
+ cvt_t.hratio = 4;
+ cvt_t.vratio = 3;
+ break;
+ case 0x04:
+ cvt_t.hratio = 16;
+ cvt_t.vratio = 9;
+ break;
+ case 0x08:
+ cvt_t.hratio = 16;
+ cvt_t.vratio = 10;
+ break;
+ case 0x0c:
+ cvt_t.hratio = 15;
+ cvt_t.vratio = 9;
+ break;
+ }
+ cvt_t.hact = 8 * (((cvt_t.vact * cvt_t.hratio) / cvt_t.vratio) / 8);
+
+ if (x[1] & 0x03)
+ fail("Reserved bits of CVT byte 1 are non-zero.\n");
+ if (x[2] & 0x80)
+ fail("Reserved bit of CVT byte 2 is non-zero.\n");
+ if (!(x[2] & 0x1f))
+ fail("CVT byte 2 does not support any vertical rates.\n");
+ preferred = (x[2] & 0x60) >> 5;
+ if (preferred == 1 && (x[2] & 0x01))
+ preferred = 4;
+ if (!(x[2] & (1 << (4 - preferred))))
+ fail("The preferred CVT Vertical Rate is not supported.\n");
+
+ static const char *s_pref = "preferred vertical rate";
+
+ if (x[2] & 0x10) {
+ edid_cvt_mode(50, cvt_t);
+ print_timings(prefix, &cvt_t, "CVT", preferred == 0 ? s_pref : "");
+ }
+ if (x[2] & 0x08) {
+ edid_cvt_mode(60, cvt_t);
+ print_timings(prefix, &cvt_t, "CVT", preferred == 1 ? s_pref : "");
+ }
+ if (x[2] & 0x04) {
+ edid_cvt_mode(75, cvt_t);
+ print_timings(prefix, &cvt_t, "CVT", preferred == 2 ? s_pref : "");
+ }
+ if (x[2] & 0x02) {
+ edid_cvt_mode(85, cvt_t);
+ print_timings(prefix, &cvt_t, "CVT", preferred == 3 ? s_pref : "");
+ }
+ if (x[2] & 0x01) {
+ cvt_t.rb = RB_CVT_V1;
+ edid_cvt_mode(60, cvt_t);
+ print_timings(prefix, &cvt_t, "CVT", preferred == 4 ? s_pref : "");
+ }
+}
+
+/* extract a string from a detailed subblock, checking for termination */
+char *extract_string(const unsigned char *x, unsigned len)
+{
+ static char s[EDID_PAGE_SIZE];
+ int seen_newline = 0;
+ unsigned i;
+
+ memset(s, 0, sizeof(s));
+
+ for (i = 0; i < len; i++) {
+ if (isgraph(x[i])) {
+ s[i] = x[i];
+ } else if (!seen_newline) {
+ if (x[i] == 0x0a) {
+ seen_newline = 1;
+ if (!i)
+ fail("Empty string.\n");
+ else if (s[i - 1] == 0x20)
+ fail("One or more trailing spaces.\n");
+ } else if (x[i] == 0x20) {
+ s[i] = x[i];
+ } else {
+ fail("Non-printable character.\n");
+ return s;
+ }
+ } else if (x[i] != 0x20) {
+ fail("Non-space after newline.\n");
+ return s;
+ }
+ }
+ /* Does the string end with a space? */
+ if (!seen_newline && s[len - 1] == 0x20)
+ fail("One or more trailing spaces.\n");
+
+ return s;
+}
+
+void edid_state::print_standard_timing(const char *prefix, unsigned char b1, unsigned char b2,
+ bool gtf_only, bool show_both)
+{
+ const struct timings *t;
+ struct timings formula = {};
+ unsigned hratio, vratio;
+ unsigned hact, vact, refresh;
+ unsigned char dmt_id = 0;
+
+ if (b1 <= 0x01) {
+ if (b1 != 0x01 || b2 != 0x01)
+ fail("Use 0x0101 as the invalid Standard Timings code, not 0x%02x%02x.\n", b1, b2);
+ return;
+ }
+
+ t = find_std_id((b1 << 8) | b2, dmt_id);
+ if (t) {
+ char type[16];
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ print_timings(prefix, t, type);
+ return;
+ }
+ hact = (b1 + 31) * 8;
+ switch ((b2 >> 6) & 0x3) {
+ case 0x00:
+ if (gtf_only || show_both || base.edid_minor >= 3) {
+ hratio = 16;
+ vratio = 10;
+ } else {
+ hratio = 1;
+ vratio = 1;
+ }
+ break;
+ case 0x01:
+ hratio = 4;
+ vratio = 3;
+ break;
+ case 0x02:
+ hratio = 5;
+ vratio = 4;
+ break;
+ case 0x03:
+ hratio = 16;
+ vratio = 9;
+ break;
+ }
+ vact = (double)hact * vratio / hratio;
+ vact = 8 * ((vact + 7) / 8);
+ refresh = (b2 & 0x3f) + 60;
+
+ formula.hact = hact;
+ formula.vact = vact;
+ formula.hratio = hratio;
+ formula.vratio = vratio;
+
+ if (!gtf_only && (show_both || base.edid_minor >= 4)) {
+ if (show_both || base.supports_cvt) {
+ edid_cvt_mode(refresh, formula);
+ print_timings(prefix, &formula, "CVT ",
+ show_both ? "" : "EDID 1.4 source");
+ }
+ /*
+ * An EDID 1.3 source will assume GTF, so both GTF and CVT
+ * have to be supported.
+ */
+ edid_gtf_mode(refresh, formula);
+ if (base.supports_cvt)
+ print_timings(prefix, &formula, "GTF ", "EDID 1.3 source");
+ else
+ print_timings(prefix, &formula, "GTF ");
+ } else if (gtf_only || base.edid_minor >= 2) {
+ edid_gtf_mode(refresh, formula);
+ print_timings(prefix, &formula, "GTF ");
+ } else {
+ printf("%sUnknown : %5ux%-5u %3u.000 Hz %3u:%u\n",
+ prefix, hact, vact, refresh, hratio, vratio);
+ min_vert_freq_hz = min(min_vert_freq_hz, refresh);
+ max_vert_freq_hz = max(max_vert_freq_hz, refresh);
+ }
+}
+
+void edid_state::detailed_display_range_limits(const unsigned char *x)
+{
+ int h_max_offset = 0, h_min_offset = 0;
+ int v_max_offset = 0, v_min_offset = 0;
+ int is_cvt = 0;
+ bool has_sec_gtf = false;
+ std::string range_class;
+
+ data_block = "Display Range Limits";
+ printf(" %s:\n", data_block.c_str());
+ base.has_display_range_descriptor = 1;
+
+ if (base.edid_minor >= 4) {
+ if (x[4] & 0x02) {
+ v_max_offset = 255;
+ if (x[4] & 0x01) {
+ v_min_offset = 255;
+ }
+ }
+ if (x[4] & 0x08) {
+ h_max_offset = 255;
+ if (x[4] & 0x04) {
+ h_min_offset = 255;
+ }
+ }
+ }
+
+ /*
+ * despite the values, this is not a bitfield.
+ */
+ switch (x[10]) {
+ case 0x00: /* default gtf */
+ range_class = "GTF";
+ if (base.edid_minor >= 4 && !base.supports_continuous_freq)
+ fail("GTF can't be combined with non-continuous frequencies.\n");
+ if (base.edid_minor >= 4)
+ warn("GTF support is deprecated in EDID 1.4.\n");
+ break;
+ case 0x01: /* range limits only */
+ range_class = "Bare Limits";
+ if (base.edid_minor < 4)
+ fail("'%s' is not allowed for EDID < 1.4.\n", range_class.c_str());
+ break;
+ case 0x02: /* secondary gtf curve */
+ range_class = "Secondary GTF";
+ if (base.edid_minor >= 4 && !base.supports_continuous_freq)
+ fail("GTF can't be combined with non-continuous frequencies.\n");
+ if (base.edid_minor >= 4)
+ warn("GTF support is deprecated in EDID 1.4.\n");
+ has_sec_gtf = true;
+ break;
+ case 0x04: /* cvt */
+ range_class = "CVT";
+ is_cvt = 1;
+ if (base.edid_minor < 4)
+ fail("'%s' is not allowed for EDID < 1.4.\n", range_class.c_str());
+ else if (!base.supports_continuous_freq)
+ fail("CVT can't be combined with non-continuous frequencies.\n");
+ break;
+ default: /* invalid */
+ fail("Unknown range class (0x%02x).\n", x[10]);
+ range_class = std::string("Unknown (") + utohex(x[10]) + ")";
+ break;
+ }
+
+ if (x[5] + v_min_offset > x[6] + v_max_offset)
+ fail("Min vertical rate > max vertical rate.\n");
+ base.min_display_vert_freq_hz = x[5] + v_min_offset;
+ base.max_display_vert_freq_hz = x[6] + v_max_offset;
+ if (x[7] + h_min_offset > x[8] + h_max_offset)
+ fail("Min horizontal freq > max horizontal freq.\n");
+ base.min_display_hor_freq_hz = (x[7] + h_min_offset) * 1000;
+ base.max_display_hor_freq_hz = (x[8] + h_max_offset) * 1000;
+ printf(" Monitor ranges (%s): %d-%d Hz V, %d-%d kHz H",
+ range_class.c_str(),
+ x[5] + v_min_offset, x[6] + v_max_offset,
+ x[7] + h_min_offset, x[8] + h_max_offset);
+
+ // For EDID 1.3 the horizontal frequency maxes out at 255 kHz.
+ // So to avoid false range-check warnings due to this limitation,
+ // just double the max_display_hor_freq_hz in this case.
+ if (base.edid_minor < 4 && x[8] == 0xff)
+ base.max_display_hor_freq_hz *= 2;
+
+ // For EDID 1.3 the vertical frequency maxes out at 255 Hz.
+ // So to avoid false range-check warnings due to this limitation,
+ // just double the max_display_vert_freq_hz in this case.
+ if (base.edid_minor < 4 && x[6] == 0xff)
+ base.max_display_vert_freq_hz *= 2;
+
+ if (x[9]) {
+ base.max_display_pixclk_khz = x[9] * 10000;
+ printf(", max dotclock %d MHz\n", x[9] * 10);
+ } else {
+ if (base.edid_minor >= 4)
+ fail("EDID 1.4 block does not set max dotclock.\n");
+ printf("\n");
+ }
+
+ if (has_sec_gtf) {
+ if (x[11])
+ fail("Byte 11 is 0x%02x instead of 0x00.\n", x[11]);
+ if (memchk(x + 12, 6)) {
+ fail("Zeroed Secondary Curve Block.\n");
+ } else {
+ printf(" GTF Secondary Curve Block:\n");
+ printf(" Start frequency: %u kHz\n", x[12] * 2);
+ printf(" C: %.1f%%\n", x[13] / 2.0);
+ printf(" M: %u%%/kHz\n", (x[15] << 8) | x[14]);
+ printf(" K: %u\n", x[16]);
+ printf(" J: %.1f%%\n", x[17] / 2.0);
+ }
+ } else if (is_cvt) {
+ int max_h_pixels = 0;
+
+ printf(" CVT version %d.%d\n", (x[11] & 0xf0) >> 4, x[11] & 0x0f);
+
+ if (x[12] & 0xfc) {
+ unsigned raw_offset = (x[12] & 0xfc) >> 2;
+
+ printf(" Real max dotclock: %.2f MHz\n",
+ (x[9] * 10) - (raw_offset * 0.25));
+ if (raw_offset >= 40)
+ warn("CVT block corrects dotclock by more than 9.75 MHz.\n");
+ }
+
+ max_h_pixels = x[12] & 0x03;
+ max_h_pixels <<= 8;
+ max_h_pixels |= x[13];
+ max_h_pixels *= 8;
+ if (max_h_pixels)
+ printf(" Max active pixels per line: %d\n", max_h_pixels);
+
+ printf(" Supported aspect ratios:%s%s%s%s%s\n",
+ x[14] & 0x80 ? " 4:3" : "",
+ x[14] & 0x40 ? " 16:9" : "",
+ x[14] & 0x20 ? " 16:10" : "",
+ x[14] & 0x10 ? " 5:4" : "",
+ x[14] & 0x08 ? " 15:9" : "");
+ if (x[14] & 0x07)
+ fail("Reserved bits of byte 14 are non-zero.\n");
+
+ printf(" Preferred aspect ratio: ");
+ switch ((x[15] & 0xe0) >> 5) {
+ case 0x00:
+ printf("4:3");
+ break;
+ case 0x01:
+ printf("16:9");
+ break;
+ case 0x02:
+ printf("16:10");
+ break;
+ case 0x03:
+ printf("5:4");
+ break;
+ case 0x04:
+ printf("15:9");
+ break;
+ default:
+ printf("Unknown (0x%02x)", (x[15] & 0xe0) >> 5);
+ fail("Invalid preferred aspect ratio 0x%02x.\n",
+ (x[15] & 0xe0) >> 5);
+ break;
+ }
+ printf("\n");
+
+ if (x[15] & 0x08)
+ printf(" Supports CVT standard blanking\n");
+ if (x[15] & 0x10)
+ printf(" Supports CVT reduced blanking\n");
+
+ if (x[15] & 0x07)
+ fail("Reserved bits of byte 15 are non-zero.\n");
+
+ if (x[16] & 0xf0) {
+ printf(" Supported display scaling:\n");
+ if (x[16] & 0x80)
+ printf(" Horizontal shrink\n");
+ if (x[16] & 0x40)
+ printf(" Horizontal stretch\n");
+ if (x[16] & 0x20)
+ printf(" Vertical shrink\n");
+ if (x[16] & 0x10)
+ printf(" Vertical stretch\n");
+ }
+
+ if (x[16] & 0x0f)
+ fail("Reserved bits of byte 16 are non-zero.\n");
+
+ if (x[17])
+ printf(" Preferred vertical refresh: %d Hz\n", x[17]);
+ else
+ warn("CVT block does not set preferred refresh rate.\n");
+ } else {
+ if (x[11] != 0x0a)
+ fail("Byte 11 is 0x%02x instead of 0x0a.\n", x[11]);
+ for (unsigned i = 12; i <= 17; i++) {
+ if (x[i] != 0x20) {
+ fail("Bytes 12-17 must be 0x20.\n");
+ break;
+ }
+ }
+ }
+}
+
+void edid_state::detailed_epi(const unsigned char *x)
+{
+ data_block = "EPI Descriptor";
+ printf(" %s:\n", data_block.c_str());
+
+ unsigned v = x[5] & 0x07;
+
+ printf(" Bits per pixel: %u\n", 18 + v * 6);
+ if (v > 2)
+ fail("Invalid bits per pixel.\n");
+ v = (x[5] & 0x18) >> 3;
+ printf(" Pixels per clock: %u\n", 1 << v);
+ if (v > 2)
+ fail("Invalid pixels per clock.\n");
+ v = (x[5] & 0x60) >> 5;
+ printf(" Data color mapping: %sconventional\n", v ? "non-" : "");
+ if (v > 1)
+ fail("Unknown data color mapping (0x%02x).\n", v);
+ if (x[5] & 0x80)
+ fail("Non-zero reserved field in byte 5.\n");
+
+ v = x[6] & 0x0f;
+ printf(" Interface type: ");
+ switch (v) {
+ case 0x00: printf("LVDS TFT\n"); break;
+ case 0x01: printf("monoSTN 4/8 Bit\n"); break;
+ case 0x02: printf("colorSTN 8/16 Bit\n"); break;
+ case 0x03: printf("18 Bit TFT\n"); break;
+ case 0x04: printf("24 Bit TFT\n"); break;
+ case 0x05: printf("TMDS\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Invalid interface type 0x%02x.\n", v);
+ break;
+ }
+ printf(" DE polarity: DE %s active\n",
+ (x[6] & 0x10) ? "low" : "high");
+ printf(" FPSCLK polarity: FPSCLK %sinverted\n",
+ (x[6] & 0x20) ? "" : "not ");
+ if (x[6] & 0xc0)
+ fail("Non-zero reserved field in byte 6.\n");
+
+ printf(" Vertical display mode: %s\n",
+ (x[7] & 0x01) ? "Up/Down reverse mode" : "normal");
+ printf(" Horizontal display mode: %s\n",
+ (x[7] & 0x02) ? "Left/Right reverse mode" : "normal");
+ if (x[7] & 0xfc)
+ fail("Non-zero reserved field in byte 7.\n");
+
+ v = x[8] & 0x0f;
+ printf(" Total power on sequencing delay: ");
+ if (v)
+ printf("%u ms\n", v * 10);
+ else
+ printf("VGA controller default\n");
+ v = (x[8] & 0xf0) >> 4;
+ printf(" Total power off sequencing delay: ");
+ if (v)
+ printf("%u ms\n", v * 10);
+ else
+ printf("VGA controller default\n");
+
+ v = x[9] & 0x0f;
+ printf(" Contrast power on sequencing delay: ");
+ if (v)
+ printf("%u ms\n", v * 10);
+ else
+ printf("VGA controller default\n");
+ v = (x[9] & 0xf0) >> 4;
+ printf(" Contrast power off sequencing delay: ");
+ if (v)
+ printf("%u ms\n", v * 10);
+ else
+ printf("VGA controller default\n");
+
+ v = x[10] & 0x2f;
+ const char *s = (x[10] & 0x80) ? "" : " (ignored)";
+
+ printf(" Backlight brightness control: %u steps%s\n", v, s);
+ printf(" Backlight enable at boot: %s%s\n",
+ (x[10] & 0x40) ? "off" : "on", s);
+ printf(" Backlight control enable: %s\n",
+ (x[10] & 0x80) ? "enabled" : "disabled");
+
+ v = x[11] & 0x2f;
+ s = (x[11] & 0x80) ? "" : " (ignored)";
+
+ printf(" Contrast voltable control: %u steps%s\n", v, s);
+ if (x[11] & 0x40)
+ fail("Non-zero reserved field in byte 11.\n");
+ printf(" Contrast control enable: %s\n",
+ (x[11] & 0x80) ? "enabled" : "disabled");
+
+ if (x[12] || x[13] || x[14] || x[15] || x[16])
+ fail("Non-zero values in reserved bytes 12-16.\n");
+
+ printf(" EPI Version: %u.%u\n", (x[17] & 0xf0) >> 4, x[17] & 0x0f);
+}
+
+void edid_state::detailed_timings(const char *prefix, const unsigned char *x,
+ bool base_or_cta)
+{
+ struct timings t = {};
+ unsigned hbl, vbl;
+ std::string s_sync, s_flags;
+
+ // Only count DTDs in base block 0 or CTA-861 extension blocks
+ if (base_or_cta)
+ base.dtd_cnt++;
+ data_block = "Detailed Timing Descriptor #" + std::to_string(base.dtd_cnt);
+ t.pixclk_khz = (x[0] + (x[1] << 8)) * 10;
+ if (t.pixclk_khz < 10000) {
+ printf("%sDetailed mode: ", prefix);
+ hex_block("", x, 18, true, 18);
+ if (!t.pixclk_khz)
+ fail("First two bytes are 0, invalid data.\n");
+ else
+ fail("Pixelclock < 10 MHz, assuming invalid data 0x%02x 0x%02x.\n",
+ x[0], x[1]);
+ return;
+ }
+
+ /*
+ * If the borders are non-zero, then it is unclear how to interpret
+ * the DTD blanking parameters.
+ *
+ * According to EDID 1.3 (3.12) the Hor/Vert Blanking includes the
+ * borders, and so does the Hor/Vert Sync Offset.
+ *
+ * According to EDID 1.4 (3.12) the Hor/Vert Blanking excludes the
+ * borders, and they are also excluded from the Hor/Vert Front Porch.
+ *
+ * But looking at what is really done in EDIDs is that the Hor/Vert
+ * Blanking follows EDID 1.3, but the Hor/Vert Front Porch does not
+ * include the border.
+ *
+ * So hbl/vbl includes the borders, so those need to be subtracted,
+ * but hfp/vfp is used as-is.
+ *
+ * In practice you really shouldn't use non-zero borders in DTDs
+ * since clearly nobody knows how to interpret the timing.
+ */
+ t.hact = (x[2] + ((x[4] & 0xf0) << 4));
+ t.hborder = x[15];
+ hbl = (x[3] + ((x[4] & 0x0f) << 8)) - t.hborder * 2;
+ t.hfp = (x[8] + ((x[11] & 0xc0) << 2));
+ t.hsync = (x[9] + ((x[11] & 0x30) << 4));
+ t.hbp = hbl - t.hsync - t.hfp;
+ t.vact = (x[5] + ((x[7] & 0xf0) << 4));
+ t.vborder = x[16];
+ vbl = (x[6] + ((x[7] & 0x0f) << 8)) - t.vborder * 2;
+ t.vfp = ((x[10] >> 4) + ((x[11] & 0x0c) << 2));
+ t.vsync = ((x[10] & 0x0f) + ((x[11] & 0x03) << 4));
+ t.vbp = vbl - t.vsync - t.vfp;
+
+ unsigned char flags = x[17];
+
+ if (base.has_spwg && base.detailed_block_cnt == 2)
+ flags = *(x - 1);
+
+ switch ((flags & 0x18) >> 3) {
+ case 0x00:
+ s_flags = "analog composite";
+ /* fall-through */
+ case 0x01:
+ if (s_flags.empty())
+ s_flags = "bipolar analog composite";
+ switch ((flags & 0x06) >> 1) {
+ case 0x00:
+ add_str(s_flags, "sync-on-green");
+ break;
+ case 0x01:
+ break;
+ case 0x02:
+ add_str(s_flags, "serrate, sync-on-green");
+ break;
+ case 0x03:
+ add_str(s_flags, "serrate");
+ break;
+ }
+ break;
+ case 0x02:
+ if (flags & (1 << 1))
+ t.pos_pol_hsync = true;
+ t.no_pol_vsync = true;
+ s_flags = "digital composite";
+ if (flags & (1 << 2))
+ add_str(s_flags, "serrate");
+ break;
+ case 0x03:
+ if (flags & (1 << 1))
+ t.pos_pol_hsync = true;
+ if (flags & (1 << 2))
+ t.pos_pol_vsync = true;
+ s_sync = t.pos_pol_hsync ? "+hsync " : "-hsync ";
+ s_sync += t.pos_pol_vsync ? "+vsync " : "-vsync ";
+ if (base.has_spwg && (flags & 0x01))
+ s_flags = "DE timing only";
+ break;
+ }
+ if (flags & 0x80) {
+ t.interlaced = true;
+ t.vact *= 2;
+ /*
+ * Check if this DTD matches VIC code 39 with special
+ * interlaced timings.
+ */
+ if (t.hact == 1920 && t.vact == 1080 && t.pixclk_khz == 72000 &&
+ t.hfp == 32 && t.hsync == 168 && t.hbp == 184 && !t.hborder &&
+ t.vfp == 23 && t.vsync == 5 && t.vbp == 57 && !t.vborder &&
+ !base.has_spwg && cta.preparsed_has_vic[0][39] && (flags & 0x1e) == 0x1a)
+ t.even_vtotal = true;
+ }
+ switch (flags & 0x61) {
+ case 0x20:
+ add_str(s_flags, "field sequential L/R");
+ break;
+ case 0x40:
+ add_str(s_flags, "field sequential R/L");
+ break;
+ case 0x21:
+ add_str(s_flags, "interleaved right even");
+ break;
+ case 0x41:
+ add_str(s_flags, "interleaved left even");
+ break;
+ case 0x60:
+ add_str(s_flags, "four way interleaved");
+ break;
+ case 0x61:
+ add_str(s_flags, "side by side interleaved");
+ break;
+ default:
+ break;
+ }
+
+ t.hsize_mm = x[12] + ((x[14] & 0xf0) << 4);
+ t.vsize_mm = x[13] + ((x[14] & 0x0f) << 8);
+
+ calc_ratio(&t);
+
+ std::string s_type = base_or_cta ? dtd_type() : "DTD";
+ bool ok = print_timings(prefix, &t, s_type.c_str(), s_flags.c_str(), true);
+ timings_ext te(t, s_type, s_flags);
+
+ if (block_nr == 0 && base.dtd_cnt == 1) {
+ te.type = "DTD 1";
+ base.preferred_timing = te;
+ if (has_cta) {
+ cta.preferred_timings.push_back(te);
+ cta.native_timings.push_back(te);
+ }
+ }
+ if (base_or_cta)
+ cta.vec_dtds.push_back(te);
+
+ if (t.hborder || t.vborder)
+ warn("The use of non-zero borders in a DTD is not recommended.\n");
+ if ((base.max_display_width_mm && !t.hsize_mm) ||
+ (base.max_display_height_mm && !t.vsize_mm)) {
+ fail("Mismatch of image size vs display size: image size is not set, but display size is.\n");
+ }
+ if (base.has_spwg && base.detailed_block_cnt == 2)
+ printf("%sSPWG Module Revision: %hhu\n", prefix, x[17]);
+ if (!ok) {
+ std::string s = prefix;
+
+ s += " ";
+ hex_block(s.c_str(), x, 18, true, 18);
+ }
+}
+
+void edid_state::preparse_detailed_block(const unsigned char *x)
+{
+ if (x[0] || x[1])
+ return;
+ if (x[3] != 0xfd)
+ return;
+
+ switch (x[10]) {
+ case 0x00: /* default gtf */
+ base.supports_gtf = true;
+ break;
+ case 0x02: /* secondary gtf curve */
+ base.supports_gtf = true;
+ base.supports_sec_gtf = !memchk(x + 12, 6);
+ base.sec_gtf_start_freq = x[12] * 2;
+ base.C = x[13] / 2.0;
+ base.M = (x[15] << 8) | x[14];
+ base.K = x[16];
+ base.J = x[17] / 2.0;
+ break;
+ case 0x04: /* cvt */
+ if (base.edid_minor >= 4) {
+ /* GTF is implied if CVT is signaled */
+ base.supports_gtf = true;
+ base.supports_cvt = true;
+ }
+ break;
+ }
+}
+
+void edid_state::detailed_block(const unsigned char *x)
+{
+ static const unsigned char zero_descr[18] = { 0 };
+ unsigned cnt;
+ unsigned i;
+
+ base.detailed_block_cnt++;
+ if (x[0] || x[1]) {
+ detailed_timings(" ", x);
+ if (base.seen_non_detailed_descriptor)
+ fail("Invalid detailed timing descriptor ordering.\n");
+ return;
+ }
+
+ data_block = "Display Descriptor #" + std::to_string(base.detailed_block_cnt);
+ /* Monitor descriptor block, not detailed timing descriptor. */
+ if (x[2] != 0) {
+ /* 1.3, 3.10.3 */
+ fail("Monitor descriptor block has byte 2 nonzero (0x%02x).\n", x[2]);
+ }
+ if ((base.edid_minor < 4 || x[3] != 0xfd) && x[4] != 0x00) {
+ /* 1.3, 3.10.3 */
+ fail("Monitor descriptor block has byte 4 nonzero (0x%02x).\n", x[4]);
+ }
+
+ base.seen_non_detailed_descriptor = true;
+ if (base.edid_minor == 0)
+ fail("Has descriptor blocks other than detailed timings.\n");
+
+ if (!memcmp(x, zero_descr, sizeof(zero_descr))) {
+ data_block = "Empty Descriptor";
+ printf(" %s\n", data_block.c_str());
+ fail("Use Dummy Descriptor instead of all zeroes.\n");
+ return;
+ }
+
+ switch (x[3]) {
+ case 0x0e:
+ detailed_epi(x);
+ return;
+ case 0x10:
+ data_block = "Dummy Descriptor";
+ printf(" %s:\n", data_block.c_str());
+ for (i = 5; i < 18; i++) {
+ if (x[i]) {
+ fail("Dummy block filled with garbage.\n");
+ break;
+ }
+ }
+ return;
+ case 0xf7:
+ data_block = "Established timings III";
+ printf(" %s:\n", data_block.c_str());
+ for (i = 0; i < ARRAY_SIZE(established_timings3_dmt_ids); i++)
+ if (x[6 + i / 8] & (1 << (7 - i % 8))) {
+ unsigned char dmt_id = established_timings3_dmt_ids[i];
+ char type[16];
+
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ print_timings(" ", find_dmt_id(dmt_id), type);
+ }
+ return;
+ case 0xf8:
+ data_block = "CVT 3 Byte Timing Codes";
+ printf(" %s:\n", data_block.c_str());
+ if (x[5] != 0x01) {
+ fail("Invalid version number %u.\n", x[5]);
+ return;
+ }
+ for (i = 0; i < 4; i++)
+ detailed_cvt_descriptor(" ", x + 6 + (i * 3), !i);
+ return;
+ case 0xf9:
+ data_block = "Display Color Management Data";
+ printf(" %s:\n", data_block.c_str());
+ printf(" Version : %d\n", x[5]);
+ printf(" Red a3 : %.2f\n", (short)(x[6] | (x[7] << 8)) / 100.0);
+ printf(" Red a2 : %.2f\n", (short)(x[8] | (x[9] << 8)) / 100.0);
+ printf(" Green a3: %.2f\n", (short)(x[10] | (x[11] << 8)) / 100.0);
+ printf(" Green a2: %.2f\n", (short)(x[12] | (x[13] << 8)) / 100.0);
+ printf(" Blue a3 : %.2f\n", (short)(x[14] | (x[15] << 8)) / 100.0);
+ printf(" Blue a2 : %.2f\n", (short)(x[16] | (x[17] << 8)) / 100.0);
+ return;
+ case 0xfa:
+ data_block = "Standard Timing Identifications";
+ printf(" %s:\n", data_block.c_str());
+ for (cnt = i = 0; i < 6; i++) {
+ if (x[5 + i * 2] != 0x01 || x[5 + i * 2 + 1] != 0x01)
+ cnt++;
+ print_standard_timing(" ", x[5 + i * 2], x[5 + i * 2 + 1]);
+ }
+ if (!cnt)
+ warn("%s block without any timings.\n", data_block.c_str());
+ return;
+ case 0xfb: {
+ unsigned w_x, w_y;
+ unsigned gamma;
+
+ data_block = "Color Point Data";
+ printf(" %s:\n", data_block.c_str());
+ w_x = (x[7] << 2) | ((x[6] >> 2) & 3);
+ w_y = (x[8] << 2) | (x[6] & 3);
+ gamma = x[9];
+ printf(" Index: %u White: 0.%04u, 0.%04u", x[5],
+ (w_x * 10000) / 1024, (w_y * 10000) / 1024);
+ if (gamma == 0xff)
+ printf(" Gamma: is defined in an extension block");
+ else
+ printf(" Gamma: %.2f", ((gamma + 100.0) / 100.0));
+ printf("\n");
+ if (x[10] == 0)
+ return;
+ w_x = (x[12] << 2) | ((x[11] >> 2) & 3);
+ w_y = (x[13] << 2) | (x[11] & 3);
+ gamma = x[14];
+ printf(" Index: %u White: 0.%04u, 0.%04u", x[10],
+ (w_x * 10000) / 1024, (w_y * 10000) / 1024);
+ if (gamma == 0xff)
+ printf(" Gamma: is defined in an extension block");
+ else
+ printf(" Gamma: %.2f", ((gamma + 100.0) / 100.0));
+ printf("\n");
+ return;
+ }
+ case 0xfc:
+ data_block = "Display Product Name";
+ base.has_name_descriptor = 1;
+ printf(" %s: '%s'\n", data_block.c_str(), extract_string(x + 5, 13));
+ return;
+ case 0xfd:
+ detailed_display_range_limits(x);
+ return;
+ case 0xfe:
+ if (!base.has_spwg || base.detailed_block_cnt < 3) {
+ data_block = "Alphanumeric Data String";
+ printf(" %s: '%s'\n", data_block.c_str(),
+ extract_string(x + 5, 13));
+ return;
+ }
+ if (base.detailed_block_cnt == 3) {
+ char buf[6] = { 0 };
+
+ data_block = "SPWG Descriptor #3";
+ printf(" %s:\n", data_block.c_str());
+ memcpy(buf, x + 5, 5);
+ if (strlen(buf) != 5)
+ fail("Invalid PC Maker P/N length.\n");
+ printf(" SPWG PC Maker P/N: '%s'\n", buf);
+ printf(" SPWG LCD Supplier EEDID Revision: %hhu\n", x[10]);
+ printf(" SPWG Manufacturer P/N: '%s'\n", extract_string(x + 11, 7));
+ } else {
+ data_block = "SPWG Descriptor #4";
+ printf(" %s:\n", data_block.c_str());
+ printf(" SMBUS Values: 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx"
+ " 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n",
+ x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12]);
+ printf(" LVDS Channels: %hhu\n", x[13]);
+ printf(" Panel Self Test %sPresent\n", x[14] ? "" : "Not ");
+ if (x[15] != 0x0a || x[16] != 0x20 || x[17] != 0x20)
+ fail("Invalid trailing data.\n");
+ }
+ return;
+ case 0xff: {
+ data_block = "Display Product Serial Number";
+ char *sn = extract_string(x + 5, 13);
+ if (hide_serial_numbers)
+ printf(" %s: ...\n", data_block.c_str());
+ else
+ printf(" %s: '%s'\n", data_block.c_str(), sn);
+ base.has_serial_string = 1;
+ return;
+ }
+ default:
+ printf(" %s Display Descriptor (0x%02hhx):",
+ x[3] <= 0x0f ? "Manufacturer-Specified" : "Unknown", x[3]);
+ hex_block(" ", x + 2, 16);
+ if (x[3] > 0x0f)
+ fail("Unknown Type 0x%02hhx.\n", x[3]);
+ return;
+ }
+}
+
+void edid_state::parse_base_block(const unsigned char *x)
+{
+ time_t the_time;
+ struct tm *ptm;
+ int analog;
+ unsigned col_x, col_y;
+ bool has_preferred_timing = false;
+
+ data_block = "EDID Structure Version & Revision";
+ printf(" %s: %hhu.%hhu\n", data_block.c_str(), x[0x12], x[0x13]);
+ if (x[0x12] == 1) {
+ base.edid_minor = x[0x13];
+ if (base.edid_minor > 4)
+ warn("Unknown EDID minor version %u, assuming 1.4 conformance.\n", base.edid_minor);
+ if (base.edid_minor < 3)
+ warn("EDID 1.%u is deprecated, do not use.\n", base.edid_minor);
+ } else {
+ fail("Unknown EDID major version.\n");
+ }
+
+ data_block = "Vendor & Product Identification";
+ printf(" %s:\n", data_block.c_str());
+ printf(" Manufacturer: %s\n Model: %u\n",
+ manufacturer_name(x + 0x08),
+ (unsigned short)(x[0x0a] + (x[0x0b] << 8)));
+ base.has_serial_number = x[0x0c] || x[0x0d] || x[0x0e] || x[0x0f];
+ if (base.has_serial_number) {
+ if (hide_serial_numbers)
+ printf(" Serial Number: ...\n");
+ else
+ printf(" Serial Number: %u\n",
+ (unsigned)(x[0x0c] + (x[0x0d] << 8) +
+ (x[0x0e] << 16) + (x[0x0f] << 24)));
+ }
+
+ time(&the_time);
+ ptm = localtime(&the_time);
+
+ unsigned char week = x[0x10];
+ int year = 1990 + x[0x11];
+
+ if (week) {
+ if (base.edid_minor <= 3 && week == 0xff)
+ fail("EDID 1.3 does not support week 0xff.\n");
+ // The max week is 53 in EDID 1.3 and 54 in EDID 1.4.
+ // No idea why there is a difference.
+ if (base.edid_minor <= 3 && week == 54)
+ fail("EDID 1.3 does not support week 54.\n");
+ if (week != 0xff && week > 54)
+ fail("Invalid week %u of manufacture.\n", week);
+ if (week != 0xff)
+ printf(" Made in: week %hhu of %d\n", week, year);
+ }
+ if (week == 0xff)
+ printf(" Model year: %d\n", year);
+ else if (!week)
+ printf(" Made in: %d\n", year);
+ if (year - 1 > ptm->tm_year + 1900)
+ fail("The year %d is more than one year in the future.\n", year);
+
+ /* display section */
+
+ data_block = "Basic Display Parameters & Features";
+ printf(" %s:\n", data_block.c_str());
+ if (x[0x14] & 0x80) {
+ analog = 0;
+ printf(" Digital display\n");
+ if (base.edid_minor >= 4) {
+ if ((x[0x14] & 0x70) == 0x00)
+ printf(" Color depth is undefined\n");
+ else if ((x[0x14] & 0x70) == 0x70)
+ fail("Color Bit Depth set to reserved value.\n");
+ else
+ printf(" Bits per primary color channel: %u\n",
+ ((x[0x14] & 0x70) >> 3) + 4);
+
+ printf(" ");
+ switch (x[0x14] & 0x0f) {
+ case 0x00: printf("Digital interface is not defined\n"); break;
+ case 0x01: printf("DVI interface\n"); break;
+ case 0x02: printf("HDMI-a interface\n"); break;
+ case 0x03: printf("HDMI-b interface\n"); break;
+ case 0x04: printf("MDDI interface\n"); break;
+ case 0x05: printf("DisplayPort interface\n"); break;
+ default:
+ printf("Unknown interface: 0x%02x\n", x[0x14] & 0x0f);
+ fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x[0x14] & 0x0f);
+ break;
+ }
+ } else if (base.edid_minor >= 2) {
+ if (x[0x14] & 0x01) {
+ printf(" DFP 1.x compatible TMDS\n");
+ }
+ if (x[0x14] & 0x7e)
+ fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x[0x14] & 0x7e);
+ } else if (x[0x14] & 0x7f) {
+ fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x[0x14] & 0x7f);
+ }
+ } else {
+ unsigned voltage = (x[0x14] & 0x60) >> 5;
+ unsigned sync = (x[0x14] & 0x0f);
+
+ analog = 1;
+ printf(" Analog display\n");
+ printf(" Input voltage level: %s V\n",
+ voltage == 3 ? "0.7/0.7" :
+ voltage == 2 ? "1.0/0.4" :
+ voltage == 1 ? "0.714/0.286" :
+ "0.7/0.3");
+
+ if (x[0x14] & 0x10)
+ printf(" Blank-to-black setup/pedestal\n");
+ else
+ printf(" Blank level equals black level\n");
+
+ if (sync)
+ printf(" Sync:%s%s%s%s\n",
+ sync & 0x08 ? " Separate" : "",
+ sync & 0x04 ? " Composite" : "",
+ sync & 0x02 ? " SyncOnGreen" : "",
+ sync & 0x01 ? " Serration" : "");
+ }
+
+ if (x[0x15] && x[0x16]) {
+ printf(" Maximum image size: %u cm x %u cm\n", x[0x15], x[0x16]);
+ base.max_display_width_mm = x[0x15] * 10;
+ base.max_display_height_mm = x[0x16] * 10;
+ if (x[0x15] < 10 || x[0x16] < 10)
+ warn("Dubious maximum image size (%ux%u is smaller than 10x10 cm).\n",
+ x[0x15], x[0x16]);
+ }
+ else if (base.edid_minor >= 4 && (x[0x15] || x[0x16])) {
+ if (x[0x15])
+ printf(" Aspect ratio: %.2f (landscape)\n", (x[0x15] + 99) / 100.0);
+ else
+ printf(" Aspect ratio: %.2f (portrait)\n", 100.0 / (x[0x16] + 99));
+ } else {
+ /* Either or both can be zero for 1.3 and before */
+ printf(" Image size is variable\n");
+ }
+
+ if (x[0x17] == 0xff)
+ printf(" Gamma is defined in an extension block\n");
+ else
+ printf(" Gamma: %.2f\n", ((x[0x17] + 100.0) / 100.0));
+
+ if (x[0x18] & 0xe0) {
+ printf(" DPMS levels:");
+ if (x[0x18] & 0x80) printf(" Standby");
+ if (x[0x18] & 0x40) printf(" Suspend");
+ if (x[0x18] & 0x20) printf(" Off");
+ printf("\n");
+ }
+
+ if (analog || base.edid_minor < 4) {
+ printf(" ");
+ switch (x[0x18] & 0x18) {
+ case 0x00: printf("Monochrome or grayscale display\n"); break;
+ case 0x08: printf("RGB color display\n"); break;
+ case 0x10: printf("Non-RGB color display\n"); break;
+ case 0x18: printf("Undefined display color type\n");
+ }
+ } else {
+ printf(" Supported color formats: RGB 4:4:4");
+ if (x[0x18] & 0x08)
+ printf(", YCrCb 4:4:4");
+ if (x[0x18] & 0x10)
+ printf(", YCrCb 4:2:2");
+ printf("\n");
+ }
+
+ if (x[0x18] & 0x04) {
+ /*
+ * The sRGB chromaticities are (x, y):
+ * red: 0.640, 0.330
+ * green: 0.300, 0.600
+ * blue: 0.150, 0.060
+ * white: 0.3127, 0.3290
+ */
+ static const unsigned char srgb_chromaticity[10] = {
+ 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54
+ };
+ printf(" Default (sRGB) color space is primary color space\n");
+ if (memcmp(x + 0x19, srgb_chromaticity, sizeof(srgb_chromaticity)))
+ fail("sRGB is signaled, but the chromaticities do not match.\n");
+ }
+ if (base.edid_minor >= 4) {
+ /* 1.4 always has a preferred timing and this bit means something else. */
+ has_preferred_timing = true;
+ base.preferred_is_also_native = x[0x18] & 0x02;
+ printf(" First detailed timing %s the native pixel format and preferred refresh rate\n",
+ base.preferred_is_also_native ? "includes" : "does not include");
+ } else {
+ if (x[0x18] & 0x02) {
+ printf(" First detailed timing is the preferred timing\n");
+ has_preferred_timing = true;
+ // 1.3 recommends that the preferred timing corresponds to the
+ // native timing, but it is not a requirement.
+ // That said, we continue with the assumption that it actually
+ // is the native timing.
+ base.preferred_is_also_native = true;
+ } else if (base.edid_minor == 3) {
+ fail("EDID 1.3 requires that the first detailed timing is the preferred timing.\n");
+ }
+ }
+
+ if (x[0x18] & 0x01) {
+ if (base.edid_minor >= 4) {
+ base.supports_continuous_freq = true;
+ printf(" Display is continuous frequency\n");
+ } else {
+ printf(" Supports GTF timings within operating range\n");
+ base.supports_gtf = true;
+ }
+ }
+
+ data_block = "Color Characteristics";
+ printf(" %s:\n", data_block.c_str());
+ col_x = (x[0x1b] << 2) | (x[0x19] >> 6);
+ col_y = (x[0x1c] << 2) | ((x[0x19] >> 4) & 3);
+ printf(" Red : 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+ col_x = (x[0x1d] << 2) | ((x[0x19] >> 2) & 3);
+ col_y = (x[0x1e] << 2) | (x[0x19] & 3);
+ printf(" Green: 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+ col_x = (x[0x1f] << 2) | (x[0x1a] >> 6);
+ col_y = (x[0x20] << 2) | ((x[0x1a] >> 4) & 3);
+ printf(" Blue : 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+ col_x = (x[0x21] << 2) | ((x[0x1a] >> 2) & 3);
+ col_y = (x[0x22] << 2) | (x[0x1a] & 3);
+ printf(" White: 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+
+ data_block = "Established Timings I & II";
+ if (x[0x23] || x[0x24] || x[0x25]) {
+ printf(" %s:\n", data_block.c_str());
+ for (unsigned i = 0; i < ARRAY_SIZE(established_timings12); i++) {
+ if (x[0x23 + i / 8] & (1 << (7 - i % 8))) {
+ unsigned char dmt_id = established_timings12[i].dmt_id;
+ const struct timings *t;
+ char type[16];
+
+ if (dmt_id) {
+ sprintf(type, "DMT 0x%02x", dmt_id);
+ t = find_dmt_id(dmt_id);
+ } else {
+ t = &established_timings12[i].t;
+ sprintf(type, "%-8s", established_timings12[i].type);
+ }
+ print_timings(" ", t, type);
+ }
+ }
+ } else {
+ printf(" %s: none\n", data_block.c_str());
+ }
+ base.has_640x480p60_est_timing = x[0x23] & 0x20;
+
+ /*
+ * Need to find the Display Range Limit info before reading
+ * the standard timings.
+ */
+ preparse_detailed_block(x + 0x36);
+ preparse_detailed_block(x + 0x48);
+ preparse_detailed_block(x + 0x5a);
+ preparse_detailed_block(x + 0x6c);
+
+ data_block = "Standard Timings";
+ bool found = false;
+ for (unsigned i = 0; i < 8; i++) {
+ if (x[0x26 + i * 2] != 0x01 || x[0x26 + i * 2 + 1] != 0x01) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ printf(" %s:\n", data_block.c_str());
+ for (unsigned i = 0; i < 8; i++)
+ print_standard_timing(" ", x[0x26 + i * 2], x[0x26 + i * 2 + 1]);
+ } else {
+ printf(" %s: none\n", data_block.c_str());
+ }
+
+ /* 18 byte descriptors */
+ if (has_preferred_timing && !x[0x36] && !x[0x37])
+ fail("Missing preferred timing.\n");
+
+ /* Look for SPWG Noteboook Panel EDID data blocks */
+ if ((x[0x36] || x[0x37]) &&
+ (x[0x48] || x[0x49]) &&
+ !x[0x5a] && !x[0x5b] && x[0x5d] == 0xfe &&
+ !x[0x6c] && !x[0x6d] && x[0x6f] == 0xfe &&
+ (x[0x79] == 1 || x[0x79] == 2) && x[0x7a] <= 1)
+ base.has_spwg = true;
+
+ for (unsigned i = 0; i < (base.has_spwg ? 2 : 4); i++)
+ if (x[0x36 + i * 18] || x[0x37 + i * 18])
+ cta.preparsed_total_dtds++;
+
+ data_block = "Detailed Timing Descriptors";
+ printf(" %s:\n", data_block.c_str());
+ detailed_block(x + 0x36);
+ detailed_block(x + 0x48);
+ detailed_block(x + 0x5a);
+ detailed_block(x + 0x6c);
+ base.has_spwg = false;
+ if (!base.preferred_is_also_native) {
+ cta.native_timings.clear();
+ base.preferred_timing = timings_ext();
+ }
+
+ data_block = block;
+ if (x[0x7e])
+ printf(" Extension blocks: %u\n", x[0x7e]);
+ if (x[0x7e] + 1U != num_blocks)
+ fail("EDID specified %u extension block(s), but found %u extension block(s).\n",
+ x[0x7e], num_blocks - 1);
+
+ block = block_name(0x00);
+ data_block.clear();
+ do_checksum("", x, EDID_PAGE_SIZE);
+ if (base.edid_minor >= 3) {
+ if (!base.has_name_descriptor)
+ fail("Missing Display Product Name.\n");
+ if ((base.edid_minor == 3 || base.supports_continuous_freq) &&
+ !base.has_display_range_descriptor)
+ fail("Missing Display Range Limits Descriptor.\n");
+ }
+}
+
+void edid_state::check_base_block()
+{
+ data_block = "Base EDID";
+
+ /*
+ * Allow for regular rounding of vertical and horizontal frequencies.
+ * The spec says that the pixelclock shall be rounded up, so there is
+ * no need to take rounding into account.
+ */
+ if (base.has_display_range_descriptor &&
+ (min_vert_freq_hz + 0.5 < base.min_display_vert_freq_hz ||
+ (max_vert_freq_hz >= base.max_display_vert_freq_hz + 0.5 && base.max_display_vert_freq_hz) ||
+ min_hor_freq_hz + 500 < base.min_display_hor_freq_hz ||
+ (max_hor_freq_hz >= base.max_display_hor_freq_hz + 500 && base.max_display_hor_freq_hz) ||
+ (max_pixclk_khz > base.max_display_pixclk_khz && base.max_display_pixclk_khz))) {
+ /*
+ * Check if it is really out of range, or if it could be a rounding error.
+ * The EDID spec is not very clear about rounding.
+ */
+ bool out_of_range =
+ min_vert_freq_hz + 1.0 <= base.min_display_vert_freq_hz ||
+ (max_vert_freq_hz >= base.max_display_vert_freq_hz + 1.0 && base.max_display_vert_freq_hz) ||
+ min_hor_freq_hz + 1000 <= base.min_display_hor_freq_hz ||
+ (max_hor_freq_hz >= base.max_display_hor_freq_hz + 1000 && base.max_display_hor_freq_hz) ||
+ (max_pixclk_khz >= base.max_display_pixclk_khz + 10000 && base.max_display_pixclk_khz);
+
+ std::string err("Some timings are out of range of the Monitor Ranges:\n");
+ char buf[512];
+
+ if (min_vert_freq_hz + 0.5 < base.min_display_vert_freq_hz ||
+ (max_vert_freq_hz >= base.max_display_vert_freq_hz + 0.5 && base.max_display_vert_freq_hz)) {
+ sprintf(buf, " Vertical Freq: %.3f - %.3f Hz (Monitor: %u.000 - %u.000 Hz)\n",
+ min_vert_freq_hz, max_vert_freq_hz,
+ base.min_display_vert_freq_hz, base.max_display_vert_freq_hz);
+ err += buf;
+ }
+
+ if (min_hor_freq_hz + 500 < base.min_display_hor_freq_hz ||
+ (max_hor_freq_hz >= base.max_display_hor_freq_hz + 500 && base.max_display_hor_freq_hz)) {
+ sprintf(buf, " Horizontal Freq: %.3f - %.3f kHz (Monitor: %.3f - %.3f kHz)\n",
+ min_hor_freq_hz / 1000.0, max_hor_freq_hz / 1000.0,
+ base.min_display_hor_freq_hz / 1000.0, base.max_display_hor_freq_hz / 1000.0);
+ err += buf;
+ }
+
+ if (max_pixclk_khz >= base.max_display_pixclk_khz && base.max_display_pixclk_khz) {
+ sprintf(buf, " Maximum Clock: %.3f MHz (Monitor: %.3f MHz)\n",
+ max_pixclk_khz / 1000.0, base.max_display_pixclk_khz / 1000.0);
+ err += buf;
+ }
+
+ if (!out_of_range)
+ err += " Could be due to a Monitor Range off-by-one rounding issue\n";
+
+ /*
+ * EDID 1.4 states (in an Errata) that explicitly defined
+ * timings supersede the monitor range definition.
+ */
+ msg(!out_of_range || base.edid_minor >= 4, "%s", err.c_str());
+ }
+ // The base block will only go up to 255x255 cm for the display size,
+ // so don't fail if one or more image sizes exceeds that.
+ if (!base.max_display_width_mm && !base.max_display_height_mm &&
+ dtd_max_hsize_mm && dtd_max_vsize_mm &&
+ dtd_max_hsize_mm <= 2559 && dtd_max_vsize_mm <= 2559) {
+ fail("The DTD image sizes all fit inside 255x255cm, but no display size was set.\n");
+ }
+ // Secondary GTF curves start at a specific frequency. Any legacy timings
+ // that have a positive hsync and negative vsync must be less than that
+ // frequency to avoid confusion.
+ if (base.supports_sec_gtf && base.max_pos_neg_hor_freq_khz >= base.sec_gtf_start_freq)
+ fail("Second GTF start frequency %u is less than the highest P/N frequency %u.\n",
+ base.sec_gtf_start_freq, base.max_pos_neg_hor_freq_khz);
+ if (base.edid_minor == 3 && num_blocks > 2 && !block_map.saw_block_1)
+ fail("EDID 1.3 requires a Block Map Extension in Block 1 if there are more than 2 blocks in the EDID.\n");
+ if (base.edid_minor == 3 && num_blocks > 128 && !block_map.saw_block_128)
+ fail("EDID 1.3 requires a Block Map Extension in Block 128 if there are more than 128 blocks in the EDID.\n");
+ if (block_map.saw_block_128 && num_blocks > 255)
+ fail("If there is a Block Map Extension in Block 128 then the maximum number of blocks is 255.\n");
+}
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
new file mode 100644
index 0000000..6ce8e6c
--- /dev/null
+++ b/parse-cta-block.cpp
@@ -0,0 +1,2574 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#include "edid-decode.h"
+
+static const struct timings edid_cta_modes1[] = {
+ /* VIC 1 */
+ { 640, 480, 4, 3, 25175, 0, false, 16, 96, 48, false, 10, 2, 33, false },
+ { 720, 480, 4, 3, 27000, 0, false, 16, 62, 60, false, 9, 6, 30, false },
+ { 720, 480, 16, 9, 27000, 0, false, 16, 62, 60, false, 9, 6, 30, false },
+ { 1280, 720, 16, 9, 74250, 0, false, 110, 40, 220, true, 5, 5, 20, true },
+ { 1920, 1080, 16, 9, 74250, 0, true, 88, 44, 148, true, 2, 5, 15, true },
+ { 1440, 480, 4, 3, 27000, 0, true, 38, 124, 114, false, 4, 3, 15, false },
+ { 1440, 480, 16, 9, 27000, 0, true, 38, 124, 114, false, 4, 3, 15, false },
+ { 1440, 240, 4, 3, 27000, 0, false, 38, 124, 114, false, 4, 3, 15, false },
+ { 1440, 240, 16, 9, 27000, 0, false, 38, 124, 114, false, 4, 3, 15, false },
+ { 2880, 480, 4, 3, 54000, 0, true, 76, 248, 228, false, 4, 3, 15, false },
+ /* VIC 11 */
+ { 2880, 480, 16, 9, 54000, 0, true, 76, 248, 228, false, 4, 3, 15, false },
+ { 2880, 240, 4, 3, 54000, 0, false, 76, 248, 228, false, 4, 3, 15, false },
+ { 2880, 240, 16, 9, 54000, 0, false, 76, 248, 228, false, 4, 3, 15, false },
+ { 1440, 480, 4, 3, 54000, 0, false, 32, 124, 120, false, 9, 6, 30, false },
+ { 1440, 480, 16, 9, 54000, 0, false, 32, 124, 120, false, 9, 6, 30, false },
+ { 1920, 1080, 16, 9, 148500, 0, false, 88, 44, 148, true, 4, 5, 36, true },
+ { 720, 576, 4, 3, 27000, 0, false, 12, 64, 68, false, 5, 5, 39, false },
+ { 720, 576, 16, 9, 27000, 0, false, 12, 64, 68, false, 5, 5, 39, false },
+ { 1280, 720, 16, 9, 74250, 0, false, 440, 40, 220, true, 5, 5, 20, true },
+ { 1920, 1080, 16, 9, 74250, 0, true, 528, 44, 148, true, 2, 5, 15, true },
+ /* VIC 21 */
+ { 1440, 576, 4, 3, 27000, 0, true, 24, 126, 138, false, 2, 3, 19, false },
+ { 1440, 576, 16, 9, 27000, 0, true, 24, 126, 138, false, 2, 3, 19, false },
+ { 1440, 288, 4, 3, 27000, 0, false, 24, 126, 138, false, 2, 3, 19, false },
+ { 1440, 288, 16, 9, 27000, 0, false, 24, 126, 138, false, 2, 3, 19, false },
+ { 2880, 576, 4, 3, 54000, 0, true, 48, 252, 276, false, 2, 3, 19, false },
+ { 2880, 576, 16, 9, 54000, 0, true, 48, 252, 276, false, 2, 3, 19, false },
+ { 2880, 288, 4, 3, 54000, 0, false, 48, 252, 276, false, 2, 3, 19, false },
+ { 2880, 288, 16, 9, 54000, 0, false, 48, 252, 276, false, 2, 3, 19, false },
+ { 1440, 576, 4, 3, 54000, 0, false, 24, 128, 136, false, 5, 5, 39, false },
+ { 1440, 576, 16, 9, 54000, 0, false, 24, 128, 136, false, 5, 5, 39, false },
+ /* VIC 31 */
+ { 1920, 1080, 16, 9, 148500, 0, false, 528, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 16, 9, 74250, 0, false, 638, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 16, 9, 74250, 0, false, 528, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 16, 9, 74250, 0, false, 88, 44, 148, true, 4, 5, 36, true },
+ { 2880, 480, 4, 3, 108000, 0, false, 64, 248, 240, false, 9, 6, 30, false },
+ { 2880, 480, 16, 9, 108000, 0, false, 64, 248, 240, false, 9, 6, 30, false },
+ { 2880, 576, 4, 3, 108000, 0, false, 48, 256, 272, false, 5, 5, 39, false },
+ { 2880, 576, 16, 9, 108000, 0, false, 48, 256, 272, false, 5, 5, 39, false },
+ { 1920, 1080, 16, 9, 72000, 0, true, 32, 168, 184, true, 23, 5, 57, false, 0, 0, true },
+ { 1920, 1080, 16, 9, 148500, 0, true, 528, 44, 148, true, 2, 5, 15, true },
+ /* VIC 41 */
+ { 1280, 720, 16, 9, 148500, 0, false, 440, 40, 220, true, 5, 5, 20, true },
+ { 720, 576, 4, 3, 54000, 0, false, 12, 64, 68, false, 5, 5, 39, false },
+ { 720, 576, 16, 9, 54000, 0, false, 12, 64, 68, false, 5, 5, 39, false },
+ { 1440, 576, 4, 3, 54000, 0, true, 24, 126, 138, false, 2, 3, 19, false },
+ { 1440, 576, 16, 9, 54000, 0, true, 24, 126, 138, false, 2, 3, 19, false },
+ { 1920, 1080, 16, 9, 148500, 0, true, 88, 44, 148, true, 2, 5, 15, true },
+ { 1280, 720, 16, 9, 148500, 0, false, 110, 40, 220, true, 5, 5, 20, true },
+ { 720, 480, 4, 3, 54000, 0, false, 16, 62, 60, false, 9, 6, 30, false },
+ { 720, 480, 16, 9, 54000, 0, false, 16, 62, 60, false, 9, 6, 30, false },
+ { 1440, 480, 4, 3, 54000, 0, true, 38, 124, 114, false, 4, 3, 15, false },
+ /* VIC 51 */
+ { 1440, 480, 16, 9, 54000, 0, true, 38, 124, 114, false, 4, 3, 15, false },
+ { 720, 576, 4, 3, 108000, 0, false, 12, 64, 68, false, 5, 5, 39, false },
+ { 720, 576, 16, 9, 108000, 0, false, 12, 64, 68, false, 5, 5, 39, false },
+ { 1440, 576, 4, 3, 108000, 0, true, 24, 126, 138, false, 2, 3, 19, false },
+ { 1440, 576, 16, 9, 108000, 0, true, 24, 126, 138, false, 2, 3, 19, false },
+ { 720, 480, 4, 3, 108000, 0, false, 16, 62, 60, false, 9, 6, 30, false },
+ { 720, 480, 16, 9, 108000, 0, false, 16, 62, 60, false, 9, 6, 30, false },
+ { 1440, 480, 4, 3, 108000, 0, true, 38, 124, 114, false, 4, 3, 15, false },
+ { 1440, 480, 16, 9, 108000, 0, true, 38, 124, 114, false, 4, 3, 15, false },
+ { 1280, 720, 16, 9, 59400, 0, false, 1760, 40, 220, true, 5, 5, 20, true },
+ /* VIC 61 */
+ { 1280, 720, 16, 9, 74250, 0, false, 2420, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 16, 9, 74250, 0, false, 1760, 40, 220, true, 5, 5, 20, true },
+ { 1920, 1080, 16, 9, 297000, 0, false, 88, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 16, 9, 297000, 0, false, 528, 44, 148, true, 4, 5, 36, true },
+ { 1280, 720, 64, 27, 59400, 0, false, 1760, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 64, 27, 74250, 0, false, 2420, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 64, 27, 74250, 0, false, 1760, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 64, 27, 74250, 0, false, 440, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 64, 27, 74250, 0, false, 110, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 64, 27, 148500, 0, false, 440, 40, 220, true, 5, 5, 20, true },
+ /* VIC 71 */
+ { 1280, 720, 64, 27, 148500, 0, false, 110, 40, 220, true, 5, 5, 20, true },
+ { 1920, 1080, 64, 27, 74250, 0, false, 638, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 74250, 0, false, 528, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 74250, 0, false, 88, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 148500, 0, false, 528, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 148500, 0, false, 88, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 297000, 0, false, 528, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 297000, 0, false, 88, 44, 148, true, 4, 5, 36, true },
+ { 1680, 720, 64, 27, 59400, 0, false, 1360, 40, 220, true, 5, 5, 20, true },
+ { 1680, 720, 64, 27, 59400, 0, false, 1228, 40, 220, true, 5, 5, 20, true },
+ /* VIC 81 */
+ { 1680, 720, 64, 27, 59400, 0, false, 700, 40, 220, true, 5, 5, 20, true },
+ { 1680, 720, 64, 27, 82500, 0, false, 260, 40, 220, true, 5, 5, 20, true },
+ { 1680, 720, 64, 27, 99000, 0, false, 260, 40, 220, true, 5, 5, 20, true },
+ { 1680, 720, 64, 27, 165000, 0, false, 60, 40, 220, true, 5, 5, 95, true },
+ { 1680, 720, 64, 27, 198000, 0, false, 60, 40, 220, true, 5, 5, 95, true },
+ { 2560, 1080, 64, 27, 99000, 0, false, 998, 44, 148, true, 4, 5, 11, true },
+ { 2560, 1080, 64, 27, 90000, 0, false, 448, 44, 148, true, 4, 5, 36, true },
+ { 2560, 1080, 64, 27, 118800, 0, false, 768, 44, 148, true, 4, 5, 36, true },
+ { 2560, 1080, 64, 27, 185625, 0, false, 548, 44, 148, true, 4, 5, 36, true },
+ { 2560, 1080, 64, 27, 198000, 0, false, 248, 44, 148, true, 4, 5, 11, true },
+ /* VIC 91 */
+ { 2560, 1080, 64, 27, 371250, 0, false, 218, 44, 148, true, 4, 5, 161, true },
+ { 2560, 1080, 64, 27, 495000, 0, false, 548, 44, 148, true, 4, 5, 161, true },
+ { 3840, 2160, 16, 9, 297000, 0, false, 1276, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 16, 9, 297000, 0, false, 1056, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 16, 9, 297000, 0, false, 176, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 16, 9, 594000, 0, false, 1056, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 16, 9, 594000, 0, false, 176, 88, 296, true, 8, 10, 72, true },
+ { 4096, 2160, 256, 135, 297000, 0, false, 1020, 88, 296, true, 8, 10, 72, true },
+ { 4096, 2160, 256, 135, 297000, 0, false, 968, 88, 128, true, 8, 10, 72, true },
+ { 4096, 2160, 256, 135, 297000, 0, false, 88, 88, 128, true, 8, 10, 72, true },
+ /* VIC 101 */
+ { 4096, 2160, 256, 135, 594000, 0, false, 968, 88, 128, true, 8, 10, 72, true },
+ { 4096, 2160, 256, 135, 594000, 0, false, 88, 88, 128, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 297000, 0, false, 1276, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 297000, 0, false, 1056, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 297000, 0, false, 176, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 594000, 0, false, 1056, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 594000, 0, false, 176, 88, 296, true, 8, 10, 72, true },
+ { 1280, 720, 16, 9, 90000, 0, false, 960, 40, 220, true, 5, 5, 20, true },
+ { 1280, 720, 64, 27, 90000, 0, false, 960, 40, 220, true, 5, 5, 20, true },
+ { 1680, 720, 64, 27, 99000, 0, false, 810, 40, 220, true, 5, 5, 20, true },
+ /* VIC 111 */
+ { 1920, 1080, 16, 9, 148500, 0, false, 638, 44, 148, true, 4, 5, 36, true },
+ { 1920, 1080, 64, 27, 148500, 0, false, 638, 44, 148, true, 4, 5, 36, true },
+ { 2560, 1080, 64, 27, 198000, 0, false, 998, 44, 148, true, 4, 5, 11, true },
+ { 3840, 2160, 16, 9, 594000, 0, false, 1276, 88, 296, true, 8, 10, 72, true },
+ { 4096, 2160, 256, 135, 594000, 0, false, 1020, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 594000, 0, false, 1276, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 16, 9, 1188000, 0, false, 1056, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 16, 9, 1188000, 0, false, 176, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 1188000, 0, false, 1056, 88, 296, true, 8, 10, 72, true },
+ { 3840, 2160, 64, 27, 1188000, 0, false, 176, 88, 296, true, 8, 10, 72, true },
+ /* VIC 121 */
+ { 5120, 2160, 64, 27, 396000, 0, false, 1996, 88, 296, true, 8, 10, 22, true },
+ { 5120, 2160, 64, 27, 396000, 0, false, 1696, 88, 296, true, 8, 10, 22, true },
+ { 5120, 2160, 64, 27, 396000, 0, false, 664, 88, 128, true, 8, 10, 22, true },
+ { 5120, 2160, 64, 27, 742500, 0, false, 746, 88, 296, true, 8, 10, 297, true },
+ { 5120, 2160, 64, 27, 742500, 0, false, 1096, 88, 296, true, 8, 10, 72, true },
+ { 5120, 2160, 64, 27, 742500, 0, false, 164, 88, 128, true, 8, 10, 72, true },
+ { 5120, 2160, 64, 27, 1485000, 0, false, 1096, 88, 296, true, 8, 10, 72, true },
+};
+
+static const struct timings edid_cta_modes2[] = {
+ /* VIC 193 */
+ { 5120, 2160, 64, 27, 1485000, 0, false, 164, 88, 128, true, 8, 10, 72, true },
+ { 7680, 4320, 16, 9, 1188000, 0, false, 2552, 176, 592, true, 16, 20, 144, true },
+ { 7680, 4320, 16, 9, 1188000, 0, false, 2352, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 16, 9, 1188000, 0, false, 552, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 16, 9, 2376000, 0, false, 2552, 176, 592, true, 16, 20, 144, true },
+ { 7680, 4320, 16, 9, 2376000, 0, false, 2352, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 16, 9, 2376000, 0, false, 552, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 16, 9, 4752000, 0, false, 2112, 176, 592, true, 16, 20, 144, true },
+ /* VIC 201 */
+ { 7680, 4320, 16, 9, 4752000, 0, false, 352, 176, 592, true, 16, 20, 144, true },
+ { 7680, 4320, 64, 27, 1188000, 0, false, 2552, 176, 592, true, 16, 20, 144, true },
+ { 7680, 4320, 64, 27, 1188000, 0, false, 2352, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 64, 27, 1188000, 0, false, 552, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 64, 27, 2376000, 0, false, 2552, 176, 592, true, 16, 20, 144, true },
+ { 7680, 4320, 64, 27, 2376000, 0, false, 2352, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 64, 27, 2376000, 0, false, 552, 176, 592, true, 16, 20, 44, true },
+ { 7680, 4320, 64, 27, 4752000, 0, false, 2112, 176, 592, true, 16, 20, 144, true },
+ { 7680, 4320, 64, 27, 4752000, 0, false, 352, 176, 592, true, 16, 20, 144, true },
+ { 10240, 4320, 64, 27, 1485000, 0, false, 1492, 176, 592, true, 16, 20, 594, true },
+ /* VIC 211 */
+ { 10240, 4320, 64, 27, 1485000, 0, false, 2492, 176, 592, true, 16, 20, 44, true },
+ { 10240, 4320, 64, 27, 1485000, 0, false, 288, 176, 296, true, 16, 20, 144, true },
+ { 10240, 4320, 64, 27, 2970000, 0, false, 1492, 176, 592, true, 16, 20, 594, true },
+ { 10240, 4320, 64, 27, 2970000, 0, false, 2492, 176, 592, true, 16, 20, 44, true },
+ { 10240, 4320, 64, 27, 2970000, 0, false, 288, 176, 296, true, 16, 20, 144, true },
+ { 10240, 4320, 64, 27, 5940000, 0, false, 2192, 176, 592, true, 16, 20, 144, true },
+ { 10240, 4320, 64, 27, 5940000, 0, false, 288, 176, 296, true, 16, 20, 144, true },
+ { 4096, 2160, 256, 135, 1188000, 0, false, 800, 88, 296, true, 8, 10, 72, true },
+ { 4096, 2160, 256, 135, 1188000, 0, false, 88, 88, 128, true, 8, 10, 72, true },
+};
+
+static const unsigned char edid_hdmi_mode_map[] = { 95, 94, 93, 98 };
+
+unsigned char hdmi_vic_to_vic(unsigned char hdmi_vic)
+{
+ if (hdmi_vic > 0 && hdmi_vic <= ARRAY_SIZE(edid_hdmi_mode_map))
+ return edid_hdmi_mode_map[hdmi_vic - 1];
+ return 0;
+}
+
+const struct timings *find_vic_id(unsigned char vic)
+{
+ if (vic > 0 && vic <= ARRAY_SIZE(edid_cta_modes1))
+ return edid_cta_modes1 + vic - 1;
+ if (vic >= 193 && vic < ARRAY_SIZE(edid_cta_modes2) + 193)
+ return edid_cta_modes2 + vic - 193;
+ return NULL;
+}
+
+const struct timings *find_hdmi_vic_id(unsigned char hdmi_vic)
+{
+ if (hdmi_vic > 0 && hdmi_vic <= ARRAY_SIZE(edid_hdmi_mode_map))
+ return find_vic_id(edid_hdmi_mode_map[hdmi_vic - 1]);
+ return NULL;
+}
+
+const struct timings *cta_close_match_to_vic(const timings &t, unsigned &vic)
+{
+ for (vic = 1; vic <= ARRAY_SIZE(edid_cta_modes1); vic++) {
+ if (timings_close_match(t, edid_cta_modes1[vic - 1]))
+ return &edid_cta_modes1[vic - 1];
+ }
+ for (vic = 193; vic < ARRAY_SIZE(edid_cta_modes2) + 193; vic++) {
+ if (timings_close_match(t, edid_cta_modes1[vic - 193]))
+ return &edid_cta_modes1[vic - 193];
+ }
+ vic = 0;
+ return NULL;
+}
+
+void edid_state::cta_list_vics()
+{
+ char type[16];
+ for (unsigned vic = 1; vic <= ARRAY_SIZE(edid_cta_modes1); vic++) {
+ sprintf(type, "VIC %3u", vic);
+ print_timings("", &edid_cta_modes1[vic - 1], type, "", false, false);
+ }
+ for (unsigned vic = 193; vic < ARRAY_SIZE(edid_cta_modes2) + 193; vic++) {
+ sprintf(type, "VIC %3u", vic);
+ print_timings("", &edid_cta_modes2[vic - 193], type, "", false, false);
+ }
+}
+
+void edid_state::cta_list_hdmi_vics()
+{
+ for (unsigned i = 0; i < ARRAY_SIZE(edid_hdmi_mode_map); i++) {
+ unsigned vic = edid_hdmi_mode_map[i];
+ char type[16];
+
+ sprintf(type, "HDMI VIC %u", i + 1);
+ print_timings("", find_vic_id(vic), type, "", false, false);
+ }
+}
+
+static std::string audio_ext_format(unsigned char x)
+{
+ if (x >= 1 && x <= 3)
+ fail("Obsolete Audio Ext Format 0x%02x.\n", x);
+ switch (x) {
+ case 1: return "HE AAC (Obsolete)";
+ case 2: return "HE AAC v2 (Obsolete)";
+ case 3: return "MPEG Surround (Obsolete)";
+ case 4: return "MPEG-4 HE AAC";
+ case 5: return "MPEG-4 HE AAC v2";
+ case 6: return "MPEG-4 AAC LC";
+ case 7: return "DRA";
+ case 8: return "MPEG-4 HE AAC + MPEG Surround";
+ case 10: return "MPEG-4 AAC LC + MPEG Surround";
+ case 11: return "MPEG-H 3D Audio";
+ case 12: return "AC-4";
+ case 13: return "L-PCM 3D Audio";
+ default: break;
+ }
+ fail("Unknown Audio Ext Format 0x%02x.\n", x);
+ return std::string("Unknown Audio Ext Format (") + utohex(x) + ")";
+}
+
+static std::string audio_format(unsigned char x)
+{
+ switch (x) {
+ case 1: return "Linear PCM";
+ case 2: return "AC-3";
+ case 3: return "MPEG 1 (Layers 1 & 2)";
+ case 4: return "MPEG 1 Layer 3 (MP3)";
+ case 5: return "MPEG2 (multichannel)";
+ case 6: return "AAC LC";
+ case 7: return "DTS";
+ case 8: return "ATRAC";
+ case 9: return "One Bit Audio";
+ case 10: return "Enhanced AC-3 (DD+)";
+ case 11: return "DTS-HD";
+ case 12: return "MAT (MLP)";
+ case 13: return "DST";
+ case 14: return "WMA Pro";
+ default: break;
+ }
+ fail("Unknown Audio Format 0x%02x.\n", x);
+ return std::string("Unknown Audio Format (") + utohex(x) + ")";
+}
+
+static std::string mpeg_h_3d_audio_level(unsigned char x)
+{
+ switch (x) {
+ case 0: return "Unspecified";
+ case 1: return "Level 1";
+ case 2: return "Level 2";
+ case 3: return "Level 3";
+ case 4: return "Level 4";
+ case 5: return "Level 5";
+ default: break;
+ }
+ fail("Unknown MPEG-H 3D Audio Level 0x%02x.\n", x);
+ return std::string("Unknown MPEG-H 3D Audio Level (") + utohex(x) + ")";
+}
+
+static void cta_audio_block(const unsigned char *x, unsigned length)
+{
+ unsigned i, format, ext_format;
+
+ if (length % 3) {
+ fail("Broken CTA-861 audio block length %d.\n", length);
+ return;
+ }
+
+ for (i = 0; i < length; i += 3) {
+ format = (x[i] & 0x78) >> 3;
+ if (format == 0) {
+ printf(" Reserved (0x00)\n");
+ fail("Audio Format Code 0x00 is reserved.\n");
+ continue;
+ }
+ if (format != 15) {
+ ext_format = 0;
+ printf(" %s:\n", audio_format(format).c_str());
+ } else {
+ ext_format = (x[i + 2] & 0xf8) >> 3;
+ printf(" %s:\n", audio_ext_format(ext_format).c_str());
+ }
+ if (format != 15)
+ printf(" Max channels: %u\n", (x[i] & 0x07)+1);
+ else if (ext_format == 11)
+ printf(" MPEG-H 3D Audio Level: %s\n",
+ mpeg_h_3d_audio_level(x[i] & 0x07).c_str());
+ else if (ext_format == 13)
+ printf(" Max channels: %u\n",
+ (((x[i + 1] & 0x80) >> 3) | ((x[i] & 0x80) >> 4) |
+ (x[i] & 0x07))+1);
+ else
+ printf(" Max channels: %u\n", (x[i] & 0x07)+1);
+
+ printf(" Supported sample rates (kHz):%s%s%s%s%s%s%s\n",
+ (x[i+1] & 0x40) ? " 192" : "",
+ (x[i+1] & 0x20) ? " 176.4" : "",
+ (x[i+1] & 0x10) ? " 96" : "",
+ (x[i+1] & 0x08) ? " 88.2" : "",
+ (x[i+1] & 0x04) ? " 48" : "",
+ (x[i+1] & 0x02) ? " 44.1" : "",
+ (x[i+1] & 0x01) ? " 32" : "");
+ if (format == 1 || ext_format == 13) {
+ printf(" Supported sample sizes (bits):%s%s%s\n",
+ (x[i+2] & 0x04) ? " 24" : "",
+ (x[i+2] & 0x02) ? " 20" : "",
+ (x[i+2] & 0x01) ? " 16" : "");
+ } else if (format <= 8) {
+ printf(" Maximum bit rate: %u kb/s\n", x[i+2] * 8);
+ } else if (format == 10) {
+ // As specified by the "Dolby Audio and Dolby Atmos over HDMI"
+ // specification (v1.0).
+ if (x[i+2] & 1)
+ printf(" Supports Joint Object Coding\n");
+ if (x[i+2] & 2)
+ printf(" Supports Joint Object Coding with ACMOD28\n");
+ } else if (format == 12) {
+ if (x[i+2] & 1) {
+ printf(" Supports Dolby TrueHD, object audio PCM and channel-based PCM\n");
+ printf(" Hash calculation %srequired for object audio PCM or channel-based PCM\n",
+ (x[i+2] & 2) ? "not " : "");
+ } else {
+ printf(" Supports only Dolby TrueHD\n");
+ }
+ } else if (format == 14) {
+ printf(" Profile: %u\n", x[i+2] & 7);
+ } else if (format >= 9 && format <= 13) {
+ printf(" Audio Format Code dependent value: 0x%02x\n", x[i+2]);
+ } else if (ext_format == 11 && (x[i+2] & 1)) {
+ printf(" Supports MPEG-H 3D Audio Low Complexity Profile\n");
+ } else if ((ext_format >= 4 && ext_format <= 6) ||
+ ext_format == 8 || ext_format == 10) {
+ printf(" AAC audio frame lengths:%s%s\n",
+ (x[i+2] & 4) ? " 1024_TL" : "",
+ (x[i+2] & 2) ? " 960_TL" : "");
+ if (ext_format >= 8 && (x[i+2] & 1))
+ printf(" Supports %s signaled MPEG Surround data\n",
+ (x[i+2] & 1) ? "implicitly and explicitly" : "only implicitly");
+ if (ext_format == 6 && (x[i+2] & 1))
+ printf(" Supports 22.2ch System H\n");
+ }
+ }
+}
+
+void edid_state::cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420)
+{
+ unsigned i;
+
+ for (i = 0; i < n; i++) {
+ const struct timings *t = NULL;
+ unsigned char svd = x[i];
+ unsigned char native;
+ unsigned char vic;
+
+ if ((svd & 0x7f) == 0)
+ continue;
+
+ if ((svd - 1) & 0x40) {
+ vic = svd;
+ native = 0;
+ } else {
+ vic = svd & 0x7f;
+ native = svd & 0x80;
+ }
+
+ t = find_vic_id(vic);
+ if (t) {
+ switch (vic) {
+ case 95:
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 0;
+ break;
+ case 94:
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 1;
+ break;
+ case 93:
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 2;
+ break;
+ case 98:
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 3;
+ break;
+ }
+ bool first_svd = cta.first_svd && !for_ycbcr420;
+ bool override_pref = first_svd && cta.first_svd_might_be_preferred;
+
+ char type[16];
+ sprintf(type, "VIC %3u", vic);
+ const char *flags = native ? "native" : "";
+
+ if (for_ycbcr420) {
+ struct timings tmp = *t;
+ tmp.ycbcr420 = true;
+ print_timings(" ", &tmp, type, flags);
+ } else {
+ print_timings(" ", t, type, flags);
+ }
+ if (override_pref) {
+ if (!cta.preferred_timings.empty()) {
+ if (match_timings(cta.preferred_timings[0].t, *t))
+ warn("For improved preferred timing interoperability, set 'Native detailed modes' to 1.\n");
+ else
+ warn("VIC %u is the preferred timing, overriding the first detailed timings. Is this intended?\n", vic);
+ }
+ cta.preferred_timings.insert(cta.preferred_timings.begin(),
+ timings_ext(*t, type, flags));
+ } else if (first_svd) {
+ cta.preferred_timings.push_back(timings_ext(*t, type, flags));
+ }
+ if (first_svd) {
+ cta.first_svd = false;
+ cta.first_svd_might_be_preferred = false;
+ }
+ if (native)
+ cta.native_timings.push_back(timings_ext(*t, type, flags));
+ } else {
+ printf(" Unknown (VIC %3u)\n", vic);
+ fail("Unknown VIC %u.\n", vic);
+ }
+
+ if (vic == 1 && !for_ycbcr420)
+ cta.has_vic_1 = 1;
+ if (++cta.vics[vic][for_ycbcr420] == 2)
+ fail("Duplicate %sVIC %u.\n", for_ycbcr420 ? "YCbCr 4:2:0 " : "", vic);
+ if (for_ycbcr420 && cta.preparsed_has_vic[0][vic])
+ fail("YCbCr 4:2:0-only VIC %u is also a regular VIC.\n", vic);
+ }
+}
+
+void edid_state::print_vic_index(const char *prefix, unsigned idx, const char *suffix, bool ycbcr420)
+{
+ if (!suffix)
+ suffix = "";
+ if (idx < cta.preparsed_svds[0].size()) {
+ unsigned char vic = cta.preparsed_svds[0][idx];
+ const struct timings *t = find_vic_id(vic);
+ char buf[16];
+
+ sprintf(buf, "VIC %3u", vic);
+
+ if (t) {
+ struct timings tmp = *t;
+ tmp.ycbcr420 = ycbcr420;
+ print_timings(prefix, &tmp, buf, suffix);
+ } else {
+ printf("%sUnknown (%s%s%s)\n", prefix, buf,
+ *suffix ? ", " : "", suffix);
+ }
+ } else {
+ // Should not happen!
+ printf("%sSVD Index %u is out of range", prefix, idx + 1);
+ if (*suffix)
+ printf(" (%s)", suffix);
+ printf("\n");
+ }
+}
+
+void edid_state::cta_y420cmdb(const unsigned char *x, unsigned length)
+{
+ unsigned max_idx = 0;
+ unsigned i;
+
+ if (!length) {
+ printf(" All VDB SVDs\n");
+ return;
+ }
+
+ if (memchk(x, length)) {
+ printf(" Empty Capability Map\n");
+ fail("Empty Capability Map.\n");
+ return;
+ }
+
+ for (i = 0; i < length; i++) {
+ unsigned char v = x[i];
+ unsigned j;
+
+ for (j = 0; j < 8; j++) {
+ if (!(v & (1 << j)))
+ continue;
+
+ print_vic_index(" ", i * 8 + j, "", true);
+ max_idx = i * 8 + j;
+ if (max_idx < cta.preparsed_svds[0].size()) {
+ unsigned vic = cta.preparsed_svds[0][max_idx];
+ if (cta.preparsed_has_vic[1][vic])
+ fail("VIC %u is also a YCbCr 4:2:0-only VIC.\n", vic);
+ }
+ }
+ }
+ if (max_idx >= cta.preparsed_svds[0].size())
+ fail("Max index %u > %u (#SVDs).\n",
+ max_idx + 1, cta.preparsed_svds[0].size());
+}
+
+void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
+{
+ unsigned i;
+
+ if (length == 0) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ cta.preferred_timings.clear();
+ for (i = 0; i < length; i++) {
+ unsigned char svr = x[i];
+ char suffix[16];
+
+ if ((svr > 0 && svr < 128) || (svr > 192 && svr < 254)) {
+ const struct timings *t;
+ unsigned char vic = svr;
+
+ sprintf(suffix, "VIC %3u", vic);
+
+ t = find_vic_id(vic);
+ if (t) {
+ print_timings(" ", t, suffix);
+ cta.preferred_timings.push_back(timings_ext(*t, suffix, ""));
+ } else {
+ printf(" %s: Unknown\n", suffix);
+ fail("Unknown VIC %u.\n", vic);
+ }
+
+ } else if (svr >= 129 && svr <= 144) {
+ sprintf(suffix, "DTD %3u", svr - 128);
+ if (svr >= cta.preparsed_total_dtds + 129) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid DTD %u.\n", svr - 128);
+ } else {
+ printf(" %s\n", suffix);
+ cta.preferred_timings.push_back(timings_ext(svr, suffix));
+ }
+ } else if (svr >= 145 && svr <= 160) {
+ sprintf(suffix, "VTDB %3u", svr - 144);
+ if (svr >= cta.preparsed_total_vtdbs + 145) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid VTDB %u.\n", svr - 144);
+ } else {
+ printf(" %s\n", suffix);
+ cta.preferred_timings.push_back(timings_ext(svr, suffix));
+ }
+ } else if (svr == 254) {
+ sprintf(suffix, "T8VTDB");
+ if (!cta.preparsed_has_t8vtdb) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid T8VTDB.\n");
+ } else {
+ printf(" %s\n", suffix);
+ cta.preferred_timings.push_back(timings_ext(svr, suffix));
+ }
+ }
+ }
+}
+
+static std::string hdmi_latency2s(unsigned char l, bool is_video)
+{
+ if (!l)
+ return "Unknown";
+ if (l == 0xff)
+ return is_video ? "Video not supported" : "Audio not supported";
+ return std::to_string(1 + 2 * l) + " ms";
+}
+
+void edid_state::hdmi_latency(unsigned char vid_lat, unsigned char aud_lat,
+ bool is_ilaced)
+{
+ const char *vid = is_ilaced ? "Interlaced video" : "Video";
+ const char *aud = is_ilaced ? "Interlaced audio" : "Audio";
+
+ printf(" %s latency: %s\n", vid, hdmi_latency2s(vid_lat, true).c_str());
+ printf(" %s latency: %s\n", aud, hdmi_latency2s(aud_lat, false).c_str());
+
+ if (vid_lat > 251 && vid_lat != 0xff)
+ fail("Invalid %s latency value %u.\n", vid, vid_lat);
+ if (aud_lat > 251 && aud_lat != 0xff)
+ fail("Invalid %s latency value %u.\n", aud, aud_lat);
+
+ if (!vid_lat || vid_lat > 251)
+ return;
+ if (!aud_lat || aud_lat > 251)
+ return;
+
+ unsigned vid_ms = 1 + 2 * vid_lat;
+ unsigned aud_ms = 1 + 2 * aud_lat;
+
+ // HDMI 2.0 latency checks for devices without HDMI output
+ if (aud_ms < vid_ms)
+ warn("%s latency < %s latency (%u ms < %u ms). This is discouraged for devices without HDMI output.\n",
+ aud, vid, aud_ms, vid_ms);
+ else if (vid_ms + 20 < aud_ms)
+ warn("%s latency + 20 < %s latency (%u + 20 ms < %u ms). This is forbidden for devices without HDMI output.\n",
+ vid, aud, vid_ms, aud_ms);
+ else if (vid_ms < aud_ms)
+ warn("%s latency < %s latency (%u ms < %u ms). This is discouraged for devices without HDMI output.\n",
+ vid, aud, vid_ms, aud_ms);
+}
+
+void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
+{
+ unsigned len_vic, len_3d;
+
+ if (length < 4) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ printf(" Source physical address: %x.%x.%x.%x\n", x[3] >> 4, x[3] & 0x0f,
+ x[4] >> 4, x[4] & 0x0f);
+
+ if (length < 6)
+ return;
+
+ if (x[5] & 0x80)
+ printf(" Supports_AI\n");
+ if (x[5] & 0x40)
+ printf(" DC_48bit\n");
+ if (x[5] & 0x20)
+ printf(" DC_36bit\n");
+ if (x[5] & 0x10)
+ printf(" DC_30bit\n");
+ if (x[5] & 0x08)
+ printf(" DC_Y444\n");
+ /* two reserved bits */
+ if (x[5] & 0x01)
+ printf(" DVI_Dual\n");
+
+ if (length < 7)
+ return;
+
+ printf(" Maximum TMDS clock: %u MHz\n", x[6] * 5);
+ if (x[6] * 5 > 340)
+ fail("HDMI VSDB Max TMDS rate is > 340.\n");
+
+ if (length < 8)
+ return;
+
+ if (x[7] & 0x0f) {
+ printf(" Supported Content Types:\n");
+ if (x[7] & 0x01)
+ printf(" Graphics\n");
+ if (x[7] & 0x02)
+ printf(" Photo\n");
+ if (x[7] & 0x04)
+ printf(" Cinema\n");
+ if (x[7] & 0x08)
+ printf(" Game\n");
+ }
+
+ unsigned b = 8;
+ if (x[7] & 0x80) {
+ hdmi_latency(x[b], x[b + 1], false);
+
+ if (x[7] & 0x40) {
+ if (x[b] == x[b + 2] &&
+ x[b + 1] == x[b + 3])
+ warn("Progressive and Interlaced latency values are identical, no need for both.\n");
+ b += 2;
+ hdmi_latency(x[b], x[b + 1], true);
+ }
+ b += 2;
+ }
+
+ if (!(x[7] & 0x20))
+ return;
+
+ bool mask = false;
+ bool formats = false;
+
+ printf(" Extended HDMI video details:\n");
+ if (x[b] & 0x80)
+ printf(" 3D present\n");
+ if ((x[b] & 0x60) == 0x20) {
+ printf(" All advertised VICs are 3D-capable\n");
+ formats = true;
+ }
+ if ((x[b] & 0x60) == 0x40) {
+ printf(" 3D-capable-VIC mask present\n");
+ formats = true;
+ mask = true;
+ }
+ switch (x[b] & 0x18) {
+ case 0x00: break;
+ case 0x08:
+ printf(" Base EDID image size is aspect ratio\n");
+ break;
+ case 0x10:
+ printf(" Base EDID image size is in units of 1 cm\n");
+ break;
+ case 0x18:
+ printf(" Base EDID image size is in units of 5 cm\n");
+ base.max_display_width_mm *= 5;
+ base.max_display_height_mm *= 5;
+ printf(" Recalculated image size: %u cm x %u cm\n",
+ base.max_display_width_mm / 10, base.max_display_height_mm / 10);
+ break;
+ }
+ b++;
+ len_vic = (x[b] & 0xe0) >> 5;
+ len_3d = (x[b] & 0x1f) >> 0;
+ b++;
+
+ if (len_vic) {
+ unsigned i;
+
+ printf(" HDMI VICs:\n");
+ for (i = 0; i < len_vic; i++) {
+ unsigned char vic = x[b + i];
+ const struct timings *t;
+
+ if (vic && vic <= ARRAY_SIZE(edid_hdmi_mode_map)) {
+ std::string suffix = "HDMI VIC " + std::to_string(vic);
+ cta.supported_hdmi_vic_codes |= 1 << (vic - 1);
+ t = find_vic_id(edid_hdmi_mode_map[vic - 1]);
+ print_timings(" ", t, suffix.c_str());
+ } else {
+ printf(" Unknown (HDMI VIC %u)\n", vic);
+ fail("Unknown HDMI VIC %u.\n", vic);
+ }
+ }
+
+ b += len_vic;
+ }
+
+ if (!len_3d)
+ return;
+
+ if (formats) {
+ /* 3D_Structure_ALL_15..8 */
+ if (x[b] & 0x80)
+ printf(" 3D: Side-by-side (half, quincunx)\n");
+ if (x[b] & 0x01)
+ printf(" 3D: Side-by-side (half, horizontal)\n");
+ /* 3D_Structure_ALL_7..0 */
+ b++;
+ if (x[b] & 0x40)
+ printf(" 3D: Top-and-bottom\n");
+ if (x[b] & 0x20)
+ printf(" 3D: L + depth + gfx + gfx-depth\n");
+ if (x[b] & 0x10)
+ printf(" 3D: L + depth\n");
+ if (x[b] & 0x08)
+ printf(" 3D: Side-by-side (full)\n");
+ if (x[b] & 0x04)
+ printf(" 3D: Line-alternative\n");
+ if (x[b] & 0x02)
+ printf(" 3D: Field-alternative\n");
+ if (x[b] & 0x01)
+ printf(" 3D: Frame-packing\n");
+ b++;
+ len_3d -= 2;
+ }
+
+ if (mask) {
+ int max_idx = -1;
+ unsigned i;
+
+ printf(" 3D VIC indices that support these capabilities:\n");
+ /* worst bit ordering ever */
+ for (i = 0; i < 8; i++)
+ if (x[b + 1] & (1 << i)) {
+ print_vic_index(" ", i, "");
+ max_idx = i;
+ }
+ for (i = 0; i < 8; i++)
+ if (x[b] & (1 << i)) {
+ print_vic_index(" ", i + 8, "");
+ max_idx = i + 8;
+ }
+ b += 2;
+ len_3d -= 2;
+ if (max_idx >= (int)cta.preparsed_svds[0].size())
+ fail("HDMI 3D VIC indices max index %d > %u (#SVDs).\n",
+ max_idx + 1, cta.preparsed_svds[0].size());
+ }
+
+ /*
+ * list of nibbles:
+ * 2D_VIC_Order_X
+ * 3D_Structure_X
+ * (optionally: 3D_Detail_X and reserved)
+ */
+ if (!len_3d)
+ return;
+
+ unsigned end = b + len_3d;
+ int max_idx = -1;
+
+ printf(" 3D VIC indices with specific capabilities:\n");
+ while (b < end) {
+ unsigned char idx = x[b] >> 4;
+ std::string s;
+
+ if (idx > max_idx)
+ max_idx = idx;
+ switch (x[b] & 0x0f) {
+ case 0: s = "frame packing"; break;
+ case 1: s = "field alternative"; break;
+ case 2: s = "line alternative"; break;
+ case 3: s = "side-by-side (full)"; break;
+ case 4: s = "L + depth"; break;
+ case 5: s = "L + depth + gfx + gfx-depth"; break;
+ case 6: s = "top-and-bottom"; break;
+ case 8:
+ s = "side-by-side";
+ switch (x[b + 1] >> 4) {
+ case 0x00: s += ", any subsampling"; break;
+ case 0x01: s += ", horizontal"; break;
+ case 0x02: case 0x03: case 0x04: case 0x05:
+ s += ", not in use";
+ fail("not-in-use 3D_Detail_X value 0x%02x.\n",
+ x[b + 1] >> 4);
+ break;
+ case 0x06: s += ", all quincunx combinations"; break;
+ case 0x07: s += ", quincunx odd/left, odd/right"; break;
+ case 0x08: s += ", quincunx odd/left, even/right"; break;
+ case 0x09: s += ", quincunx even/left, odd/right"; break;
+ case 0x0a: s += ", quincunx even/left, even/right"; break;
+ default:
+ s += ", reserved";
+ fail("reserved 3D_Detail_X value 0x%02x.\n",
+ x[b + 1] >> 4);
+ break;
+ }
+ break;
+ default:
+ s = "unknown (";
+ s += utohex(x[b] & 0x0f) + ")";
+ fail("Unknown 3D_Structure_X value 0x%02x.\n", x[b] & 0x0f);
+ break;
+ }
+ print_vic_index(" ", idx, s.c_str());
+ if ((x[b] & 0x0f) >= 8)
+ b++;
+ b++;
+ }
+ if (max_idx >= (int)cta.preparsed_svds[0].size())
+ fail("HDMI 2D VIC indices max index %d > %u (#SVDs).\n",
+ max_idx + 1, cta.preparsed_svds[0].size());
+}
+
+static const char *max_frl_rates[] = {
+ "Not Supported",
+ "3 Gbps per lane on 3 lanes",
+ "3 and 6 Gbps per lane on 3 lanes",
+ "3 and 6 Gbps per lane on 3 lanes, 6 Gbps on 4 lanes",
+ "3 and 6 Gbps per lane on 3 lanes, 6 and 8 Gbps on 4 lanes",
+ "3 and 6 Gbps per lane on 3 lanes, 6, 8 and 10 Gbps on 4 lanes",
+ "3 and 6 Gbps per lane on 3 lanes, 6, 8, 10 and 12 Gbps on 4 lanes",
+};
+
+static const char *dsc_max_slices[] = {
+ "Not Supported",
+ "up to 1 slice and up to (340 MHz/Ksliceadjust) pixel clock per slice",
+ "up to 2 slices and up to (340 MHz/Ksliceadjust) pixel clock per slice",
+ "up to 4 slices and up to (340 MHz/Ksliceadjust) pixel clock per slice",
+ "up to 8 slices and up to (340 MHz/Ksliceadjust) pixel clock per slice",
+ "up to 8 slices and up to (400 MHz/Ksliceadjust) pixel clock per slice",
+ "up to 12 slices and up to (400 MHz/Ksliceadjust) pixel clock per slice",
+ "up to 16 slices and up to (400 MHz/Ksliceadjust) pixel clock per slice",
+};
+
+static void cta_hf_eeodb(const unsigned char *x, unsigned length)
+{
+ printf(" EDID Extension Block Count: %u\n", x[0]);
+ if (length != 1 || x[0] == 0)
+ fail("Block is too long or reports a 0 block count.\n");
+}
+
+static void cta_hf_scdb(const unsigned char *x, unsigned length)
+{
+ unsigned rate = x[1] * 5;
+
+ printf(" Version: %u\n", x[0]);
+ if (rate) {
+ printf(" Maximum TMDS Character Rate: %u MHz\n", rate);
+ if (rate <= 340 || rate > 600)
+ fail("Max TMDS rate is > 0 and <= 340 or > 600.\n");
+ }
+ if (x[2] & 0x80)
+ printf(" SCDC Present\n");
+ if (x[2] & 0x40)
+ printf(" SCDC Read Request Capable\n");
+ if (x[2] & 0x10)
+ printf(" Supports Color Content Bits Per Component Indication\n");
+ if (x[2] & 0x08)
+ printf(" Supports scrambling for <= 340 Mcsc\n");
+ if (x[2] & 0x04)
+ printf(" Supports 3D Independent View signaling\n");
+ if (x[2] & 0x02)
+ printf(" Supports 3D Dual View signaling\n");
+ if (x[2] & 0x01)
+ printf(" Supports 3D OSD Disparity signaling\n");
+ if (x[3] & 0xf0) {
+ unsigned max_frl_rate = x[3] >> 4;
+
+ printf(" Max Fixed Rate Link: ");
+ if (max_frl_rate < ARRAY_SIZE(max_frl_rates)) {
+ printf("%s\n", max_frl_rates[max_frl_rate]);
+ } else {
+ printf("Unknown (0x%02x)\n", max_frl_rate);
+ fail("Unknown Max Fixed Rate Link (0x%02x).\n", max_frl_rate);
+ }
+ if (max_frl_rate == 1 && rate < 300)
+ fail("Max Fixed Rate Link is 1, but Max TMDS rate < 300.\n");
+ else if (max_frl_rate >= 2 && rate < 600)
+ fail("Max Fixed Rate Link is >= 2, but Max TMDS rate < 600.\n");
+ }
+ if (x[3] & 0x08)
+ printf(" Supports UHD VIC\n");
+ if (x[3] & 0x04)
+ printf(" Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding\n");
+ if (x[3] & 0x02)
+ printf(" Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding\n");
+ if (x[3] & 0x01)
+ printf(" Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding\n");
+
+ if (length <= 4)
+ return;
+
+ if (x[4] & 0x20)
+ printf(" Supports Mdelta\n");
+ if (x[4] & 0x10)
+ printf(" Supports media rates below VRRmin (CinemaVRR)\n");
+ if (x[4] & 0x08)
+ printf(" Supports negative Mvrr values\n");
+ if (x[4] & 0x04)
+ printf(" Supports Fast Vactive\n");
+ if (x[4] & 0x02)
+ printf(" Supports Auto Low-Latency Mode\n");
+ if (x[4] & 0x01)
+ printf(" Supports a FAPA in blanking after first active video line\n");
+
+ if (length <= 5)
+ return;
+
+ printf(" VRRmin: %d Hz\n", x[5] & 0x3f);
+ printf(" VRRmax: %d Hz\n", (x[5] & 0xc0) << 2 | x[6]);
+
+ if (length <= 7)
+ return;
+
+ if (x[7] & 0x80)
+ printf(" Supports VESA DSC 1.2a compression\n");
+ if (x[7] & 0x40)
+ printf(" Supports Compressed Video Transport for 4:2:0 Pixel Encoding\n");
+ if (x[7] & 0x08)
+ printf(" Supports Compressed Video Transport at any valid 1/16th bit bpp\n");
+ if (x[7] & 0x04)
+ printf(" Supports 16 bpc Compressed Video Transport\n");
+ if (x[7] & 0x02)
+ printf(" Supports 12 bpc Compressed Video Transport\n");
+ if (x[7] & 0x01)
+ printf(" Supports 10 bpc Compressed Video Transport\n");
+ if (x[8] & 0xf) {
+ unsigned max_slices = x[8] & 0xf;
+
+ printf(" DSC Max Slices: ");
+ if (max_slices < ARRAY_SIZE(dsc_max_slices)) {
+ printf("%s\n", dsc_max_slices[max_slices]);
+ } else {
+ printf("Unknown (0x%02x)\n", max_slices);
+ fail("Unknown DSC Max Slices (0x%02x).\n", max_slices);
+ }
+ }
+ if (x[8] & 0xf0) {
+ unsigned max_frl_rate = x[8] >> 4;
+
+ printf(" DSC Max Fixed Rate Link: ");
+ if (max_frl_rate < ARRAY_SIZE(max_frl_rates)) {
+ printf("%s\n", max_frl_rates[max_frl_rate]);
+ } else {
+ printf("Unknown (0x%02x)\n", max_frl_rate);
+ fail("Unknown DSC Max Fixed Rate Link (0x%02x).\n", max_frl_rate);
+ }
+ }
+ if (x[9] & 0x3f)
+ printf(" Maximum number of bytes in a line of chunks: %u\n",
+ 1024 * (1 + (x[9] & 0x3f)));
+}
+
+static void cta_amd(const unsigned char *x, unsigned length)
+{
+ // These Freesync values are reversed engineered by looking
+ // at existing EDIDs.
+ printf(" Version: %u.%u\n", x[0], x[1]);
+ printf(" Minimum Refresh Rate: %u Hz\n", x[2]);
+ printf(" Maximum Refresh Rate: %u Hz\n", x[3]);
+ // Freesync 1.x flags
+ // One or more of the 0xe6 bits signal that the VESA MCCS
+ // protocol is used to switch the Freesync range
+ printf(" Flags 1.x: 0x%02x%s\n", x[4],
+ (x[4] & 0xe6) ? " (MCCS)" : "");
+ if (length >= 10) {
+ // Freesync 2.x flags
+ // Bit 2 no doubt indicates if the monitor supports Local Dimming
+ // There are probably also bits to signal support of the
+ // FreeSync2_scRGB and FreeSync2_Gamma22 HDR display modes.
+ // I suspect bits 0 and 1.
+ printf(" Flags 2.x: 0x%02x\n", x[5]);
+ // The AMD tone mapping tutorial referred to in the URL below
+ // mentions that the Freesync HDR info reports max/min
+ // luminance of the monitor with and without local dimming.
+ //
+ // https://gpuopen.com/learn/using-amd-freesync-premium-pro-hdr-code-samples/
+ //
+ // So I assume that the first two luminance values are
+ // the max/min luminance of the display and the next two
+ // luminance values are the max/min luminance values when
+ // local dimming is disabled. The values I get seem to
+ // support that.
+ printf(" Maximum luminance: %u (%.3f cd/m^2)\n",
+ x[6], 50.0 * pow(2, x[6] / 32.0));
+ printf(" Minimum luminance: %u (%.3f cd/m^2)\n",
+ x[7], (50.0 * pow(2, x[6] / 32.0)) * pow(x[7] / 255.0, 2) / 100.0);
+ if (x[5] & 4) {
+ // One or both bytes can be 0. The meaning of that
+ // is unknown.
+ printf(" Maximum luminance (without local dimming): %u (%.3f cd/m^2)\n",
+ x[8], 50.0 * pow(2, x[8] / 32.0));
+ printf(" Minimum luminance (without local dimming): %u (%.3f cd/m^2)\n",
+ x[9], (50.0 * pow(2, x[8] / 32.0)) * pow(x[9] / 255.0, 2) / 100.0);
+ } else {
+ // These bytes are always 0x08 0x2f. If these values
+ // represent max/min luminance as well, then these
+ // would map to 59.460 and 0.020 cd/m^2 respectively.
+ // I wonder if this somehow relates to SDR.
+ printf(" Unknown: 0x%02x 0x%02x\n", x[8], x[9]);
+ }
+ }
+}
+
+static std::string display_use_case(unsigned char x)
+{
+ switch (x) {
+ case 1: return "Test equipment";
+ case 2: return "Generic display";
+ case 3: return "Television display";
+ case 4: return "Desktop productivity display";
+ case 5: return "Desktop gaming display";
+ case 6: return "Presentation display";
+ case 7: return "Virtual reality headset";
+ case 8: return "Augmented reality";
+ case 16: return "Video wall display";
+ case 17: return "Medical imaging display";
+ case 18: return "Dedicated gaming display";
+ case 19: return "Dedicated video monitor display";
+ case 20: return "Accessory display";
+ default: break;
+ }
+ fail("Unknown Display product primary use case 0x%02x.\n", x);
+ return std::string("Unknown display use case (") + utohex(x) + ")";
+}
+
+static void cta_microsoft(const unsigned char *x, unsigned length)
+{
+ // This VSDB is documented at:
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension
+ printf(" Version: %u\n", x[0]);
+ if (x[0] > 2) {
+ // In version 1 and 2 these bits should always be set to 0.
+ printf(" Desktop Usage: %u\n", (x[1] >> 6) & 1);
+ printf(" Third-Party Usage: %u\n", (x[1] >> 5) & 1);
+ }
+ printf(" Display Product Primary Use Case: %u (%s)\n", x[1] & 0x1f,
+ display_use_case(x[1] & 0x1f).c_str());
+ printf(" Container ID: %s\n", containerid2s(x + 2).c_str());
+}
+
+static void cta_hdr10plus(const unsigned char *x, unsigned length)
+{
+ printf(" Application Version: %u", x[0]);
+ if (length > 1)
+ hex_block(" ", x + 1, length - 1);
+ else
+ printf("\n");
+}
+
+static void cta_dolby_video(const unsigned char *x, unsigned length)
+{
+ unsigned char version = (x[0] >> 5) & 0x07;
+
+ printf(" Version: %u (%u bytes)\n", version, length + 5);
+ if (x[0] & 0x01)
+ printf(" Supports YUV422 12 bit\n");
+
+ if (version == 0) {
+ if (x[0] & 0x02)
+ printf(" Supports 2160p60\n");
+ if (x[0] & 0x04)
+ printf(" Supports global dimming\n");
+ unsigned char dm_version = x[16];
+ printf(" DM Version: %u.%u\n", dm_version >> 4, dm_version & 0xf);
+ printf(" Target Min PQ: %u\n", (x[14] << 4) | (x[13] >> 4));
+ printf(" Target Max PQ: %u\n", (x[15] << 4) | (x[13] & 0xf));
+ printf(" Rx, Ry: %.8f, %.8f\n",
+ ((x[1] >> 4) | (x[2] << 4)) / 4096.0,
+ ((x[1] & 0xf) | (x[3] << 4)) / 4096.0);
+ printf(" Gx, Gy: %.8f, %.8f\n",
+ ((x[4] >> 4) | (x[5] << 4)) / 4096.0,
+ ((x[4] & 0xf) | (x[6] << 4)) / 4096.0);
+ printf(" Bx, By: %.8f, %.8f\n",
+ ((x[7] >> 4) | (x[8] << 4)) / 4096.0,
+ ((x[7] & 0xf) | (x[9] << 4)) / 4096.0);
+ printf(" Wx, Wy: %.8f, %.8f\n",
+ ((x[10] >> 4) | (x[11] << 4)) / 4096.0,
+ ((x[10] & 0xf) | (x[12] << 4)) / 4096.0);
+ return;
+ }
+
+ if (version == 1) {
+ if (x[0] & 0x02)
+ printf(" Supports 2160p60\n");
+ if (x[1] & 0x01)
+ printf(" Supports global dimming\n");
+ unsigned char dm_version = (x[0] >> 2) & 0x07;
+ printf(" DM Version: %u.x\n", dm_version + 2);
+ printf(" Colorimetry: %s\n", (x[2] & 0x01) ? "P3-D65" : "ITU-R BT.709");
+ printf(" Low Latency: %s\n", (x[3] & 0x01) ? "Standard + Low Latency" : "Only Standard");
+ printf(" Target Max Luminance: %u cd/m^2\n", 100 + (x[1] >> 1) * 50);
+ double lm = (x[2] >> 1) / 127.0;
+ printf(" Target Min Luminance: %.8f cd/m^2\n", lm * lm);
+ if (length == 10) {
+ printf(" Rx, Ry: %.8f, %.8f\n", x[4] / 256.0, x[5] / 256.0);
+ printf(" Gx, Gy: %.8f, %.8f\n", x[6] / 256.0, x[7] / 256.0);
+ printf(" Bx, By: %.8f, %.8f\n", x[8] / 256.0, x[9] / 256.0);
+ } else {
+ double xmin = 0.625;
+ double xstep = (0.74609375 - xmin) / 31.0;
+ double ymin = 0.25;
+ double ystep = (0.37109375 - ymin) / 31.0;
+
+ printf(" Unique Rx, Ry: %.8f, %.8f\n",
+ xmin + xstep * (x[6] >> 3),
+ ymin + ystep * (((x[6] & 0x7) << 2) | (x[4] & 0x01) | ((x[5] & 0x01) << 1)));
+ xstep = 0.49609375 / 127.0;
+ ymin = 0.5;
+ ystep = (0.99609375 - ymin) / 127.0;
+ printf(" Unique Gx, Gy: %.8f, %.8f\n",
+ xstep * (x[4] >> 1), ymin + ystep * (x[5] >> 1));
+ xmin = 0.125;
+ xstep = (0.15234375 - xmin) / 7.0;
+ ymin = 0.03125;
+ ystep = (0.05859375 - ymin) / 7.0;
+ printf(" Unique Bx, By: %.8f, %.8f\n",
+ xmin + xstep * (x[3] >> 5),
+ ymin + ystep * ((x[3] >> 2) & 0x07));
+ }
+ return;
+ }
+
+ if (version == 2) {
+ if (x[0] & 0x02)
+ printf(" Supports Backlight Control\n");
+ if (x[1] & 0x04)
+ printf(" Supports global dimming\n");
+ unsigned char dm_version = (x[0] >> 2) & 0x07;
+ printf(" DM Version: %u.x\n", dm_version + 2);
+ printf(" Backlt Min Luma: %u cd/m^2\n", 25 + (x[1] & 0x03) * 25);
+ printf(" Interface: ");
+ switch (x[2] & 0x03) {
+ case 0: printf("Low-Latency\n"); break;
+ case 1: printf("Low-Latency + Low-Latency-HDMI\n"); break;
+ case 2: printf("Standard + Low-Latency\n"); break;
+ case 3: printf("Standard + Low-Latency + Low-Latency-HDMI\n"); break;
+ }
+ printf(" Supports 10b 12b 444: ");
+ switch ((x[3] & 0x01) << 1 | (x[4] & 0x01)) {
+ case 0: printf("Not supported\n"); break;
+ case 1: printf("10 bit\n"); break;
+ case 2: printf("12 bit\n"); break;
+ case 3: printf("Reserved\n"); break;
+ }
+ printf(" Target Min PQ v2: %u\n", 20 * (x[1] >> 3));
+ printf(" Target Max PQ v2: %u\n", 2055 + 65 * (x[2] >> 3));
+
+ double xmin = 0.625;
+ double xstep = (0.74609375 - xmin) / 31.0;
+ double ymin = 0.25;
+ double ystep = (0.37109375 - ymin) / 31.0;
+
+ printf(" Unique Rx, Ry: %.8f, %.8f\n",
+ xmin + xstep * (x[5] >> 3),
+ ymin + ystep * (x[6] >> 3));
+ xstep = 0.49609375 / 127.0;
+ ymin = 0.5;
+ ystep = (0.99609375 - ymin) / 127.0;
+ printf(" Unique Gx, Gy: %.8f, %.8f\n",
+ xstep * (x[3] >> 1), ymin + ystep * (x[4] >> 1));
+ xmin = 0.125;
+ xstep = (0.15234375 - xmin) / 7.0;
+ ymin = 0.03125;
+ ystep = (0.05859375 - ymin) / 7.0;
+ printf(" Unique Bx, By: %.8f, %.8f\n",
+ xmin + xstep * (x[5] & 0x07),
+ ymin + ystep * (x[6] & 0x07));
+ }
+}
+
+static void cta_dolby_audio(const unsigned char *x, unsigned length)
+{
+ unsigned char version = 1 + (x[0] & 0x07);
+
+ printf(" Version: %u (%u bytes)\n", version, length + 5);
+ if (x[0] & 0x80)
+ printf(" Headphone playback only\n");
+ if (x[0] & 0x40)
+ printf(" Height speaker zone present\n");
+ if (x[0] & 0x20)
+ printf(" Surround speaker zone present\n");
+ if (x[0] & 0x10)
+ printf(" Center speaker zone present\n");
+ if (x[1] & 0x01)
+ printf(" Supports Dolby MAT PCM decoding at 48 kHz only, does not support TrueHD\n");
+}
+
+static const char *speaker_map[] = {
+ "FL/FR - Front Left/Right",
+ "LFE1 - Low Frequency Effects 1",
+ "FC - Front Center",
+ "BL/BR - Back Left/Right",
+ "BC - Back Center",
+ "FLc/FRc - Front Left/Right of Center",
+ "RLC/RRC - Rear Left/Right of Center (Deprecated)",
+ "FLw/FRw - Front Left/Right Wide",
+ "TpFL/TpFR - Top Front Left/Right",
+ "TpC - Top Center",
+ "TpFC - Top Front Center",
+ "LS/RS - Left/Right Surround",
+ "LFE2 - Low Frequency Effects 2",
+ "TpBC - Top Back Center",
+ "SiL/SiR - Side Left/Right",
+ "TpSiL/TpSiR - Top Side Left/Right",
+ "TpBL/TpBR - Top Back Left/Right",
+ "BtFC - Bottom Front Center",
+ "BtFL/BtFR - Bottom Front Left/Right",
+ "TpLS/TpRS - Top Left/Right Surround (Deprecated for CTA-861)",
+ "LSd/RSd - Left/Right Surround Direct (HDMI only)",
+};
+
+static void cta_sadb(const unsigned char *x, unsigned length)
+{
+ unsigned sad;
+ unsigned i;
+
+ if (length < 3) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+
+ sad = ((x[2] << 16) | (x[1] << 8) | x[0]);
+
+ for (i = 0; i < ARRAY_SIZE(speaker_map); i++) {
+ if ((sad >> i) & 1)
+ printf(" %s\n", speaker_map[i]);
+ }
+}
+
+static void cta_vesa_dtcdb(const unsigned char *x, unsigned length)
+{
+ if (length != 7 && length != 15 && length != 31) {
+ fail("Invalid length %u.\n", length);
+ return;
+ }
+
+ switch (x[0] >> 6) {
+ case 0: printf(" White"); break;
+ case 1: printf(" Red"); break;
+ case 2: printf(" Green"); break;
+ case 3: printf(" Blue"); break;
+ }
+ unsigned v = x[0] & 0x3f;
+ printf(" transfer characteristics: %u", v);
+ for (unsigned i = 1; i < length; i++)
+ printf(" %u", v += x[i]);
+ printf(" 1023\n");
+}
+
+static void cta_vesa_vdddb(const unsigned char *x, unsigned length)
+{
+ if (length != 30) {
+ fail("Invalid length %u.\n", length);
+ return;
+ }
+
+ printf(" Interface Type: ");
+ unsigned char v = x[0];
+ switch (v >> 4) {
+ case 0: printf("Analog (");
+ switch (v & 0xf) {
+ case 0: printf("15HD/VGA"); break;
+ case 1: printf("VESA NAVI-V (15HD)"); break;
+ case 2: printf("VESA NAVI-D"); break;
+ default: printf("Reserved"); break;
+ }
+ printf(")\n");
+ break;
+ case 1: printf("LVDS %u lanes", v & 0xf); break;
+ case 2: printf("RSDS %u lanes", v & 0xf); break;
+ case 3: printf("DVI-D %u channels", v & 0xf); break;
+ case 4: printf("DVI-I analog"); break;
+ case 5: printf("DVI-I digital %u channels", v & 0xf); break;
+ case 6: printf("HDMI-A"); break;
+ case 7: printf("HDMI-B"); break;
+ case 8: printf("MDDI %u channels", v & 0xf); break;
+ case 9: printf("DisplayPort %u channels", v & 0xf); break;
+ case 10: printf("IEEE-1394"); break;
+ case 11: printf("M1 analog"); break;
+ case 12: printf("M1 digital %u channels", v & 0xf); break;
+ default: printf("Reserved"); break;
+ }
+ printf("\n");
+
+ printf(" Interface Standard Version: %u.%u\n", x[1] >> 4, x[1] & 0xf);
+ printf(" Content Protection Support: ");
+ switch (x[2]) {
+ case 0: printf("None\n"); break;
+ case 1: printf("HDCP\n"); break;
+ case 2: printf("DTCP\n"); break;
+ case 3: printf("DPCP\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+
+ printf(" Minimum Clock Frequency: %u MHz\n", x[3] >> 2);
+ printf(" Maximum Clock Frequency: %u MHz\n", ((x[3] & 0x03) << 8) | x[4]);
+ printf(" Device Native Pixel Format: %ux%u\n",
+ x[5] | (x[6] << 8), x[7] | (x[8] << 8));
+ printf(" Aspect Ratio: %.2f\n", (100 + x[9]) / 100.0);
+ v = x[0x0a];
+ printf(" Default Orientation: ");
+ switch ((v & 0xc0) >> 6) {
+ case 0x00: printf("Landscape\n"); break;
+ case 0x01: printf("Portrait\n"); break;
+ case 0x02: printf("Not Fixed\n"); break;
+ case 0x03: printf("Undefined\n"); break;
+ }
+ printf(" Rotation Capability: ");
+ switch ((v & 0x30) >> 4) {
+ case 0x00: printf("None\n"); break;
+ case 0x01: printf("Can rotate 90 degrees clockwise\n"); break;
+ case 0x02: printf("Can rotate 90 degrees counterclockwise\n"); break;
+ case 0x03: printf("Can rotate 90 degrees in either direction)\n"); break;
+ }
+ printf(" Zero Pixel Location: ");
+ switch ((v & 0x0c) >> 2) {
+ case 0x00: printf("Upper Left\n"); break;
+ case 0x01: printf("Upper Right\n"); break;
+ case 0x02: printf("Lower Left\n"); break;
+ case 0x03: printf("Lower Right\n"); break;
+ }
+ printf(" Scan Direction: ");
+ switch (v & 0x03) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("Fast Scan is on the Major (Long) Axis and Slow Scan is on the Minor Axis\n"); break;
+ case 0x02: printf("Fast Scan is on the Minor (Short) Axis and Slow Scan is on the Major Axis\n"); break;
+ case 0x03: printf("Reserved\n");
+ fail("Scan Direction used the reserved value 0x03.\n");
+ break;
+ }
+ printf(" Subpixel Information: ");
+ switch (x[0x0b]) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("RGB vertical stripes\n"); break;
+ case 0x02: printf("RGB horizontal stripes\n"); break;
+ case 0x03: printf("Vertical stripes using primary order\n"); break;
+ case 0x04: printf("Horizontal stripes using primary order\n"); break;
+ case 0x05: printf("Quad sub-pixels, red at top left\n"); break;
+ case 0x06: printf("Quad sub-pixels, red at bottom left\n"); break;
+ case 0x07: printf("Delta (triad) RGB sub-pixels\n"); break;
+ case 0x08: printf("Mosaic\n"); break;
+ case 0x09: printf("Quad sub-pixels, RGB + 1 additional color\n"); break;
+ case 0x0a: printf("Five sub-pixels, RGB + 2 additional colors\n"); break;
+ case 0x0b: printf("Six sub-pixels, RGB + 3 additional colors\n"); break;
+ case 0x0c: printf("Clairvoyante, Inc. PenTile Matrix (tm) layout\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ printf(" Horizontal and vertical dot/pixel pitch: %.2f x %.2f mm\n",
+ (double)(x[0x0c]) / 100.0, (double)(x[0x0d]) / 100.0);
+ v = x[0x0e];
+ printf(" Dithering: ");
+ switch (v >> 6) {
+ case 0: printf("None\n"); break;
+ case 1: printf("Spatial\n"); break;
+ case 2: printf("Temporal\n"); break;
+ case 3: printf("Spatial and Temporal\n"); break;
+ }
+ printf(" Direct Drive: %s\n", (v & 0x20) ? "Yes" : "No");
+ printf(" Overdrive %srecommended\n", (v & 0x10) ? "not " : "");
+ printf(" Deinterlacing: %s\n", (v & 0x08) ? "Yes" : "No");
+
+ v = x[0x0f];
+ printf(" Audio Support: %s\n", (v & 0x80) ? "Yes" : "No");
+ printf(" Separate Audio Inputs Provided: %s\n", (v & 0x40) ? "Yes" : "No");
+ printf(" Audio Input Override: %s\n", (v & 0x20) ? "Yes" : "No");
+ v = x[0x10];
+ if (v)
+ printf(" Audio Delay: %s%u ms\n", (v & 0x80) ? "" : "-", (v & 0x7f) * 2);
+ else
+ printf(" Audio Delay: no information provided\n");
+ v = x[0x11];
+ printf(" Frame Rate/Mode Conversion: ");
+ switch (v >> 6) {
+ case 0: printf("None\n"); break;
+ case 1: printf("Single Buffering\n"); break;
+ case 2: printf("Double Buffering\n"); break;
+ case 3: printf("Advanced Frame Rate Conversion\n"); break;
+ }
+ if (v & 0x3f)
+ printf(" Frame Rate Range: %u fps +/- %u fps\n",
+ x[0x12], v & 0x3f);
+ else
+ printf(" Nominal Frame Rate: %u fps\n", x[0x12]);
+ printf(" Color Bit Depth: %u @ interface, %u @ display\n",
+ (x[0x13] >> 4) + 1, (x[0x13] & 0xf) + 1);
+ v = x[0x15] & 3;
+ if (v) {
+ printf(" Additional Primary Chromaticities:\n");
+ unsigned col_x = (x[0x16] << 2) | (x[0x14] >> 6);
+ unsigned col_y = (x[0x17] << 2) | ((x[0x14] >> 4) & 3);
+ printf(" Primary 4: 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+ if (v > 1) {
+ col_x = (x[0x18] << 2) | ((x[0x14] >> 2) & 3);
+ col_y = (x[0x19] << 2) | (x[0x14] & 3);
+ printf(" Primary 5: 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+ if (v > 2) {
+ col_x = (x[0x1a] << 2) | (x[0x15] >> 6);
+ col_y = (x[0x1b] << 2) | ((x[0x15] >> 4) & 3);
+ printf(" Primary 6: 0.%04u, 0.%04u\n",
+ (col_x * 10000) / 1024, (col_y * 10000) / 1024);
+ }
+ }
+ }
+
+ v = x[0x1c];
+ printf(" Response Time %s: %u ms\n",
+ (v & 0x80) ? "White -> Black" : "Black -> White", v & 0x7f);
+ v = x[0x1d];
+ printf(" Overscan: %u%% x %u%%\n", v >> 4, v & 0xf);
+}
+
+static double decode_uchar_as_double(unsigned char x)
+{
+ signed char s = (signed char)x;
+
+ return s / 64.0;
+}
+
+void edid_state::cta_rcdb(const unsigned char *x, unsigned length)
+{
+ unsigned spm = ((x[3] << 16) | (x[2] << 8) | x[1]);
+ unsigned i;
+
+ if (length < 4) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+
+ if ((x[0] & 0x20) && !cta.has_sldb)
+ fail("'SLD' flag is 1, but no Speaker Location Data Block is found.\n");
+ else if (!(x[0] & 0x20) && cta.has_sldb)
+ fail("'SLD' flag is 0, but a Speaker Location Data Block is present.\n");
+
+ if (x[0] & 0x40) {
+ printf(" Speaker count: %u\n", (x[0] & 0x1f) + 1);
+ } else {
+ if (x[0] & 0x1f)
+ fail("'Speaker' flag is 0, but 'Speaker Count' is != 0.\n");
+ if (x[0] & 0x20)
+ fail("'SLD' flag is 1, but 'Speaker' is 0.\n");
+ }
+
+ printf(" Speaker Presence Mask:\n");
+ for (i = 0; i < ARRAY_SIZE(speaker_map); i++) {
+ if ((spm >> i) & 1)
+ printf(" %s\n", speaker_map[i]);
+ }
+
+ if ((x[0] & 0xa0) == 0x80)
+ fail("'Display' flag set, but not the 'SLD' flag.\n");
+
+ bool valid_max = cta.preparsed_sld_has_coord || (x[0] & 0x80);
+
+ if (valid_max && length >= 7) {
+ printf(" Xmax: %u dm\n", x[4]);
+ printf(" Ymax: %u dm\n", x[5]);
+ printf(" Zmax: %u dm\n", x[6]);
+ } else if (!valid_max && length >= 7) {
+ // The RCDB should have been truncated.
+ warn("'Display' flag is 0 and 'Coord' is 0 for all SLDs, but the Max coordinates are still present.\n");
+ }
+ if ((x[0] & 0x80) && length >= 10) {
+ printf(" DisplayX: %.3f * Xmax\n", decode_uchar_as_double(x[7]));
+ printf(" DisplayY: %.3f * Ymax\n", decode_uchar_as_double(x[8]));
+ printf(" DisplayZ: %.3f * Zmax\n", decode_uchar_as_double(x[9]));
+ } else if (!(x[0] & 0x80) && length >= 10) {
+ // The RCDB should have been truncated.
+ warn("'Display' flag is 0, but the Display coordinates are still present.\n");
+ }
+}
+
+static const char *speaker_location[] = {
+ "FL - Front Left",
+ "FR - Front Right",
+ "FC - Front Center",
+ "LFE1 - Low Frequency Effects 1",
+ "BL - Back Left",
+ "BR - Back Right",
+ "FLC - Front Left of Center",
+ "FRC - Front Right of Center",
+ "BC - Back Center",
+ "LFE2 - Low Frequency Effects 2",
+ "SiL - Side Left",
+ "SiR - Side Right",
+ "TpFL - Top Front Left",
+ "TpFR - Top Front Right",
+ "TpFC - Top Front Center",
+ "TpC - Top Center",
+ "TpBL - Top Back Left",
+ "TpBR - Top Back Right",
+ "TpSiL - Top Side Left",
+ "TpSiR - Top Side Right",
+ "TpBC - Top Back Center",
+ "BtFC - Bottom Front Center",
+ "BtFL - Bottom Front Left",
+ "BtFR - Bottom Front Right",
+ "FLW - Front Left Wide",
+ "FRW - Front Right Wide",
+ "LS - Left Surround",
+ "RS - Right Surround",
+};
+
+void edid_state::cta_sldb(const unsigned char *x, unsigned length)
+{
+ if (length < 2) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+
+ unsigned active_cnt = 0;
+ unsigned channel_is_active = 0;
+
+ while (length >= 2) {
+ printf(" Channel: %u (%sactive)\n", x[0] & 0x1f,
+ (x[0] & 0x20) ? "" : "not ");
+ if (x[0] & 0x20) {
+ if (channel_is_active & (1U << (x[0] & 0x1f)))
+ fail("Channel Index %u was already marked 'Active'.\n",
+ x[0] & 0x1f);
+ channel_is_active |= 1U << (x[0] & 0x1f);
+ active_cnt++;
+ }
+ if ((x[1] & 0x1f) < ARRAY_SIZE(speaker_location))
+ printf(" Speaker: %s\n", speaker_location[x[1] & 0x1f]);
+ if (length >= 5 && (x[0] & 0x40)) {
+ printf(" X: %.3f * Xmax\n", decode_uchar_as_double(x[2]));
+ printf(" Y: %.3f * Ymax\n", decode_uchar_as_double(x[3]));
+ printf(" Z: %.3f * Zmax\n", decode_uchar_as_double(x[4]));
+ length -= 3;
+ x += 3;
+ }
+
+ length -= 2;
+ x += 2;
+ }
+ if (active_cnt != cta.preparsed_speaker_count)
+ fail("There are %u active speakers, but 'Speaker Count' is %u.\n",
+ active_cnt, cta.preparsed_speaker_count);
+}
+
+void edid_state::cta_preparse_sldb(const unsigned char *x, unsigned length)
+{
+ cta.has_sldb = true;
+ while (length >= 2) {
+ if (length >= 5 && (x[0] & 0x40)) {
+ cta.preparsed_sld_has_coord = true;
+ return;
+ }
+ length -= 2;
+ x += 2;
+ }
+}
+
+void edid_state::cta_vcdb(const unsigned char *x, unsigned length)
+{
+ unsigned char d = x[0];
+
+ cta.has_vcdb = true;
+ if (length < 1) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ printf(" YCbCr quantization: %s\n",
+ (d & 0x80) ? "Selectable (via AVI YQ)" : "No Data");
+ printf(" RGB quantization: %s\n",
+ (d & 0x40) ? "Selectable (via AVI Q)" : "No Data");
+ /*
+ * If this bit is not set then that will result in interoperability
+ * problems (specifically with PCs/laptops) that quite often do not
+ * follow the default rules with respect to RGB Quantization Range
+ * handling.
+ *
+ * Starting with the CTA-861-H spec this bit is now required to be
+ * 1 for new designs.
+ */
+ if (!(d & 0x40))
+ fail("Set Selectable RGB Quantization to avoid interop issues.\n");
+ /*
+ * Since most YCbCr formats use limited range, the interop issues are
+ * less noticable than for RGB formats.
+ *
+ * Starting with the CTA-861-H spec this bit is now required to be
+ * 1 for new designs, but just warn about it (for now).
+ */
+ if ((cta.byte3 & 0x30) && !(d & 0x80))
+ warn("Set Selectable YCbCr Quantization to avoid interop issues.\n");
+
+ unsigned char s_pt = (d >> 4) & 0x03;
+ unsigned char s_it = (d >> 2) & 0x03;
+ unsigned char s_ce = d & 0x03;
+
+ printf(" PT scan behavior: ");
+ switch (s_pt) {
+ case 0: printf("No Data\n"); break;
+ case 1: printf("Always Overscanned\n"); break;
+ case 2: printf("Always Underscanned\n"); break;
+ case 3: printf("Supports both over- and underscan\n"); break;
+ }
+ printf(" IT scan behavior: ");
+ switch (s_it) {
+ case 0: printf("IT video formats not supported\n"); break;
+ case 1:
+ printf("Always Overscanned\n");
+ // See Table 52 of CTA-861-G for a description of Byte 3
+ if (cta.byte3 & 0x80)
+ fail("IT video formats are always overscanned, but bit 7 of Byte 3 of the CTA-861 Extension header is set to underscanned.\n");
+ break;
+ case 2:
+ printf("Always Underscanned\n");
+ // See Table 52 of CTA-861-G for a description of Byte 3
+ if (!(cta.byte3 & 0x80))
+ fail("IT video formats are always underscanned, but bit 7 of Byte 3 of the CTA-861 Extension header is set to overscanned.\n");
+ break;
+ case 3: printf("Supports both over- and underscan\n"); break;
+ }
+ if (s_it < 2)
+ warn("IT scan behavior is expected to support underscanned.\n");
+ printf(" CE scan behavior: ");
+ switch (s_ce) {
+ case 0: printf("CE video formats not supported\n"); break;
+ case 1: printf("Always Overscanned\n"); break;
+ case 2: printf("Always Underscanned\n"); break;
+ case 3: printf("Supports both over- and underscan\n"); break;
+ }
+ if (s_ce == 0)
+ warn("'CE video formats not supported' makes no sense.\n");
+ else if (s_pt == s_it && s_pt == s_ce)
+ warn("S_PT is equal to S_IT and S_CE, so should be set to 0 instead.\n");
+}
+
+static const char *colorimetry_map[] = {
+ "xvYCC601",
+ "xvYCC709",
+ "sYCC601",
+ "opYCC601",
+ "opRGB",
+ "BT2020cYCC",
+ "BT2020YCC",
+ "BT2020RGB",
+};
+
+static void cta_colorimetry_block(const unsigned char *x, unsigned length)
+{
+ unsigned i;
+
+ if (length < 2) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(colorimetry_map); i++) {
+ if (x[0] & (1 << i))
+ printf(" %s\n", colorimetry_map[i]);
+ }
+ if (x[1] & 0x80)
+ printf(" DCI-P3\n");
+ if (x[1] & 0x40)
+ printf(" ICtCp\n");
+}
+
+static const char *eotf_map[] = {
+ "Traditional gamma - SDR luminance range",
+ "Traditional gamma - HDR luminance range",
+ "SMPTE ST2084",
+ "Hybrid Log-Gamma",
+};
+
+static void cta_hdr_static_metadata_block(const unsigned char *x, unsigned length)
+{
+ unsigned i;
+
+ if (length < 2) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ printf(" Electro optical transfer functions:\n");
+ for (i = 0; i < 6; i++) {
+ if (x[0] & (1 << i)) {
+ if (i < ARRAY_SIZE(eotf_map)) {
+ printf(" %s\n", eotf_map[i]);
+ } else {
+ printf(" Unknown (%u)\n", i);
+ fail("Unknown EOTF (%u).\n", i);
+ }
+ }
+ }
+ printf(" Supported static metadata descriptors:\n");
+ for (i = 0; i < 8; i++) {
+ if (x[1] & (1 << i))
+ printf(" Static metadata type %u\n", i + 1);
+ }
+
+ if (length >= 3)
+ printf(" Desired content max luminance: %u (%.3f cd/m^2)\n",
+ x[2], 50.0 * pow(2, x[2] / 32.0));
+
+ if (length >= 4)
+ printf(" Desired content max frame-average luminance: %u (%.3f cd/m^2)\n",
+ x[3], 50.0 * pow(2, x[3] / 32.0));
+
+ if (length >= 5)
+ printf(" Desired content min luminance: %u (%.3f cd/m^2)\n",
+ x[4], (50.0 * pow(2, x[2] / 32.0)) * pow(x[4] / 255.0, 2) / 100.0);
+}
+
+static void cta_hdr_dyn_metadata_block(const unsigned char *x, unsigned length)
+{
+ if (length < 3) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ while (length >= 3) {
+ unsigned type_len = x[0];
+ unsigned type = x[1] | (x[2] << 8);
+
+ if (length < type_len + 1)
+ return;
+ printf(" HDR Dynamic Metadata Type %u\n", type);
+ switch (type) {
+ case 1:
+ case 4:
+ if (type_len > 2)
+ printf(" Version: %u\n", x[3] & 0xf);
+ break;
+ case 2:
+ if (type_len > 2) {
+ unsigned version = x[3] & 0xf;
+ printf(" Version: %u\n", version);
+ if (version >= 1) {
+ if (x[3] & 0x10) printf(" Supports SL-HDR1 (ETSI TS 103 433-1)\n");
+ if (x[3] & 0x20) printf(" Supports SL-HDR2 (ETSI TS 103 433-2)\n");
+ if (x[3] & 0x40) printf(" Supports SL-HDR3 (ETSI TS 103 433-3)\n");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ length -= type_len + 1;
+ x += type_len + 1;
+ }
+}
+
+static void cta_ifdb(const unsigned char *x, unsigned length)
+{
+ unsigned len_hdr = x[0] >> 5;
+
+ if (length < 2) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ printf(" VSIFs: %u\n", x[1]);
+ if (length < len_hdr + 2)
+ return;
+ length -= len_hdr + 2;
+ x += len_hdr + 2;
+ while (length > 0) {
+ int payload_len = x[0] >> 5;
+
+ if ((x[0] & 0x1f) == 1 && length >= 4) {
+ unsigned oui = (x[3] << 16) | (x[2] << 8) | x[1];
+
+ printf(" InfoFrame Type Code %u, OUI %s\n",
+ x[0] & 0x1f, ouitohex(oui).c_str());
+ x += 4;
+ length -= 4;
+ } else {
+ printf(" InfoFrame Type Code %u\n", x[0] & 0x1f);
+ x++;
+ length--;
+ }
+ x += payload_len;
+ length -= payload_len;
+ }
+}
+
+void edid_state::cta_displayid_type_7(const unsigned char *x, unsigned length)
+{
+ check_displayid_datablock_revision(x[0], 0x00, 2);
+
+ if (length < 21U + ((x[0] & 0x70) >> 4)) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ parse_displayid_type_1_7_timing(x + 1, true, 2, true);
+}
+
+void edid_state::cta_displayid_type_8(const unsigned char *x, unsigned length)
+{
+ check_displayid_datablock_revision(x[0], 0xe8, 1);
+ if (length < ((x[0] & 0x08) ? 3 : 2)) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+
+ unsigned sz = (x[0] & 0x08) ? 2 : 1;
+ unsigned type = x[0] >> 6;
+
+ if (type) {
+ fail("Only code type 0 is supported.\n");
+ return;
+ }
+
+ if (x[0] & 0x20)
+ printf(" Also supports YCbCr 4:2:0\n");
+
+ x++;
+ length--;
+ for (unsigned i = 0; i < length / sz; i++) {
+ unsigned id = x[i * sz];
+
+ if (sz == 2)
+ id |= x[i * sz + 1] << 8;
+ parse_displayid_type_4_8_timing(type, id, true);
+ }
+}
+
+void edid_state::cta_displayid_type_10(const unsigned char *x, unsigned length)
+{
+ check_displayid_datablock_revision(x[0], 0x70);
+ if (length < 7U + ((x[0] & 0x70) >> 4)) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+
+ unsigned sz = 6U + ((x[0] & 0x70) >> 4);
+ x++;
+ length--;
+ for (unsigned i = 0; i < length / sz; i++)
+ parse_displayid_type_10_timing(x + i * sz, true);
+}
+
+static void cta_hdmi_audio_block(const unsigned char *x, unsigned length)
+{
+ unsigned num_descs;
+
+ if (length < 2) {
+ fail("Empty Data Block with length %u.\n", length);
+ return;
+ }
+ if (x[0] & 3)
+ printf(" Max Stream Count: %u\n", (x[0] & 3) + 1);
+ if (x[0] & 4)
+ printf(" Supports MS NonMixed\n");
+
+ num_descs = x[1] & 7;
+ if (num_descs == 0)
+ return;
+ length -= 2;
+ x += 2;
+ while (length >= 4) {
+ if (length > 4) {
+ unsigned format = x[0] & 0xf;
+
+ printf(" %s, max channels %u\n", audio_format(format).c_str(),
+ (x[1] & 0x1f)+1);
+ printf(" Supported sample rates (kHz):%s%s%s%s%s%s%s\n",
+ (x[2] & 0x40) ? " 192" : "",
+ (x[2] & 0x20) ? " 176.4" : "",
+ (x[2] & 0x10) ? " 96" : "",
+ (x[2] & 0x08) ? " 88.2" : "",
+ (x[2] & 0x04) ? " 48" : "",
+ (x[2] & 0x02) ? " 44.1" : "",
+ (x[2] & 0x01) ? " 32" : "");
+ if (format == 1)
+ printf(" Supported sample sizes (bits):%s%s%s\n",
+ (x[3] & 0x04) ? " 24" : "",
+ (x[3] & 0x02) ? " 20" : "",
+ (x[3] & 0x01) ? " 16" : "");
+ } else {
+ unsigned sad = ((x[2] << 16) | (x[1] << 8) | x[0]);
+ unsigned i;
+
+ switch (x[3] >> 4) {
+ case 1:
+ printf(" Speaker Allocation for 10.2 channels:\n");
+ break;
+ case 2:
+ printf(" Speaker Allocation for 22.2 channels:\n");
+ break;
+ case 3:
+ printf(" Speaker Allocation for 30.2 channels:\n");
+ break;
+ default:
+ printf(" Unknown Speaker Allocation (0x%02x)\n", x[3] >> 4);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(speaker_map); i++) {
+ if ((sad >> i) & 1)
+ printf(" %s\n", speaker_map[i]);
+ }
+ }
+ length -= 4;
+ x += 4;
+ }
+}
+
+void edid_state::cta_ext_block(const unsigned char *x, unsigned length,
+ bool duplicate)
+{
+ const char *name;
+ unsigned oui;
+ bool reverse = false;
+ bool audio_block = false;
+
+ switch (x[0]) {
+ case 0x00: data_block = "Video Capability Data Block"; break;
+ case 0x01: data_block.clear(); break;
+ case 0x02: data_block = "VESA Video Display Device Data Block"; break;
+ case 0x03: data_block = "VESA Video Timing Block Extension"; break;
+ case 0x04: data_block = "Reserved for HDMI Video Data Block"; break;
+ case 0x05: data_block = "Colorimetry Data Block"; break;
+ case 0x06: data_block = "HDR Static Metadata Data Block"; break;
+ case 0x07: data_block = "HDR Dynamic Metadata Data Block"; break;
+
+ case 0x0d: data_block = "Video Format Preference Data Block"; break;
+ case 0x0e: data_block = "YCbCr 4:2:0 Video Data Block"; break;
+ case 0x0f: data_block = "YCbCr 4:2:0 Capability Map Data Block"; break;
+ case 0x10: data_block = "Reserved for CTA-861 Miscellaneous Audio Fields"; break;
+ case 0x11: data_block.clear(); audio_block = true; break;
+ case 0x12: data_block = "HDMI Audio Data Block"; audio_block = true; break;
+ case 0x13: data_block = "Room Configuration Data Block"; audio_block = true; break;
+ case 0x14: data_block = "Speaker Location Data Block"; audio_block = true; break;
+
+ case 0x20: data_block = "InfoFrame Data Block"; break;
+
+ case 0x34: data_block = "DisplayID Type VII Video Timing Data Block"; break;
+ case 0x35: data_block = "DisplayID Type VIII Video Timing Data Block"; break;
+ case 0x42: data_block = "DisplayID Type X Video Timing Data Block"; break;
+
+ case 0x78: data_block = "HDMI Forum EDID Extension Override Data Block"; break;
+ case 0x79: data_block = "HDMI Forum Sink Capability Data Block"; break;
+ default:
+ if (x[0] <= 12)
+ printf(" Unknown CTA-861 Video-Related");
+ else if (x[0] <= 31)
+ printf(" Unknown CTA-861 Audio-Related");
+ else if (x[0] >= 120 && x[0] <= 127)
+ printf(" Unknown CTA-861 HDMI-Related");
+ else
+ printf(" Unknown CTA-861");
+ printf(" Data Block (extended tag 0x%02x, length %u)\n", x[0], length);
+ hex_block(" ", x + 1, length);
+ data_block.clear();
+ warn("Unknown Extended CTA-861 Data Block 0x%02x.\n", x[0]);
+ return;
+ }
+
+ switch (x[0]) {
+ case 0x00:
+ case 0x02:
+ case 0x05:
+ case 0x06:
+ case 0x0d:
+ case 0x0f:
+ case 0x12:
+ case 0x13:
+ case 0x78:
+ case 0x79:
+ if (duplicate)
+ fail("Only one instance of this Data Block is allowed.\n");
+ break;
+ }
+
+
+ // See Table 52 of CTA-861-G for a description of Byte 3
+ if (audio_block && !(cta.byte3 & 0x40))
+ fail("audio information is present, but bit 6 of Byte 3 of the CTA-861 Extension header indicates no Basic Audio support.\n");
+
+ if (data_block.length())
+ printf(" %s:\n", data_block.c_str());
+
+ switch (x[0]) {
+ case 0x00: cta_vcdb(x + 1, length); return;
+ case 0x01:
+ if (length < 3) {
+ data_block = std::string("Vendor-Specific Video Data Block");
+ fail("Invalid length %u < 3.\n", length);
+ return;
+ }
+ oui = (x[3] << 16) + (x[2] << 8) + x[1];
+ name = oui_name(oui);
+ if (!name) {
+ name = oui_name(oui, true);
+ if (name)
+ reverse = true;
+ }
+ if (!name) {
+ printf(" Vendor-Specific Video Data Block, OUI %s:\n",
+ ouitohex(oui).c_str());
+ hex_block(" ", x + 4, length - 3);
+ data_block.clear();
+ warn("Unknown Extended Vendor-Specific Video Data Block, OUI %s.\n",
+ ouitohex(oui).c_str());
+ return;
+ }
+ data_block = std::string("Vendor-Specific Video Data Block (") + name + ")";
+ if (reverse)
+ fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order\n").c_str());
+ printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
+ if (oui == 0x90848b)
+ cta_hdr10plus(x + 4, length - 3);
+ else if (oui == 0x00d046)
+ cta_dolby_video(x + 4, length - 3);
+ else
+ hex_block(" ", x + 4, length - 3);
+ return;
+ case 0x02: cta_vesa_vdddb(x + 1, length); return;
+ case 0x05: cta_colorimetry_block(x + 1, length); return;
+ case 0x06: cta_hdr_static_metadata_block(x + 1, length); return;
+ case 0x07: cta_hdr_dyn_metadata_block(x + 1, length); return;
+ case 0x0d: cta_vfpdb(x + 1, length); return;
+ case 0x0e: cta_svd(x + 1, length, true); return;
+ case 0x0f: cta_y420cmdb(x + 1, length); return;
+ case 0x11:
+ if (length < 3) {
+ data_block = std::string("Vendor-Specific Audio Data Block");
+ fail("Invalid length %u < 3.\n", length);
+ return;
+ }
+ oui = (x[3] << 16) + (x[2] << 8) + x[1];
+ name = oui_name(oui);
+ if (!name) {
+ name = oui_name(oui, true);
+ if (name)
+ reverse = true;
+ }
+ if (!name) {
+ printf(" Vendor-Specific Audio Data Block, OUI %s:\n",
+ ouitohex(oui).c_str());
+ hex_block(" ", x + 4, length - 3);
+ data_block.clear();
+ warn("Unknown Extended Vendor-Specific Audio Data Block, OUI %s.\n",
+ ouitohex(oui).c_str());
+ return;
+ }
+ data_block = std::string("Vendor-Specific Audio Data Block (") + name + ")";
+ if (reverse)
+ fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order\n").c_str());
+ printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
+ if (oui == 0x00d046)
+ cta_dolby_audio(x + 4, length - 3);
+ else
+ hex_block(" ", x + 4, length - 3);
+ return;
+ case 0x12: cta_hdmi_audio_block(x + 1, length); return;
+ case 0x13: cta_rcdb(x + 1, length); return;
+ case 0x14: cta_sldb(x + 1, length); return;
+ case 0x20: cta_ifdb(x + 1, length); return;
+ case 0x34: cta_displayid_type_7(x + 1, length); return;
+ case 0x35: cta_displayid_type_8(x + 1, length); return;
+ case 0x42: cta_displayid_type_10(x + 1, length); return;
+ case 0x78:
+ cta_hf_eeodb(x + 1, length);
+ // This must be the first CTA-861 block
+ if (!cta.first_block)
+ fail("Block starts at a wrong offset.\n");
+ return;
+ case 0x79:
+ if (!cta.last_block_was_hdmi_vsdb)
+ fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB.\n");
+ if (cta.have_hf_scdb || cta.have_hf_vsdb)
+ fail("Duplicate HDMI Forum VSDB/SCDB.\n");
+ if (length < 2) {
+ data_block = std::string("HDMI Forum SCDB");
+ fail("Invalid length %u < 2.\n", length);
+ return;
+ }
+ if (x[1] || x[2])
+ printf(" Non-zero SCDB reserved fields!\n");
+ cta_hf_scdb(x + 3, length - 2);
+ cta.have_hf_scdb = 1;
+ return;
+ }
+
+ hex_block(" ", x + 1, length);
+}
+
+void edid_state::cta_block(const unsigned char *x, bool duplicate)
+{
+ unsigned length = x[0] & 0x1f;
+ const char *name;
+ unsigned oui;
+ bool reverse = false;
+ bool audio_block = false;
+
+ switch ((x[0] & 0xe0) >> 5) {
+ case 0x01:
+ data_block = "Audio Data Block";
+ printf(" %s:\n", data_block.c_str());
+ cta_audio_block(x + 1, length);
+ audio_block = true;
+ break;
+ case 0x02:
+ data_block = "Video Data Block";
+ printf(" %s:\n", data_block.c_str());
+ cta_svd(x + 1, length, false);
+ break;
+ case 0x03:
+ oui = (x[3] << 16) + (x[2] << 8) + x[1];
+ name = oui_name(oui);
+ if (!name) {
+ name = oui_name(oui, true);
+ if (name)
+ reverse = true;
+ }
+ if (!name) {
+ printf(" Vendor-Specific Data Block, OUI %s:\n", ouitohex(oui).c_str());
+ hex_block(" ", x + 4, length - 3);
+ data_block.clear();
+ warn("Unknown Vendor-Specific Data Block, OUI %s.\n",
+ ouitohex(oui).c_str());
+ return;
+ }
+ data_block = std::string("Vendor-Specific Data Block (") + name + ")";
+ if (reverse)
+ fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order\n").c_str());
+ printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
+ if (oui == 0x000c03) {
+ cta_hdmi_block(x + 1, length);
+ cta.last_block_was_hdmi_vsdb = 1;
+ cta.first_block = 0;
+ // The HDMI OUI is present, so this EDID represents an HDMI
+ // interface. And HDMI interfaces must use EDID version 1.3
+ // according to the HDMI Specification, so check for this.
+ if (base.edid_minor != 3)
+ fail("The HDMI Specification requires EDID 1.3 instead of 1.%u.\n",
+ base.edid_minor);
+ return;
+ }
+ if (oui == 0xc45dd8) {
+ if (!cta.last_block_was_hdmi_vsdb)
+ fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB.\n");
+ if (cta.have_hf_scdb || cta.have_hf_vsdb)
+ fail("Duplicate HDMI Forum VSDB/SCDB.\n");
+ cta_hf_scdb(x + 4, length - 3);
+ cta.have_hf_vsdb = 1;
+ break;
+ }
+ if (oui == 0x00001a) {
+ cta_amd(x + 4, length - 3);
+ break;
+ }
+ if (oui == 0xca125c && length == 0x15) {
+ cta_microsoft(x + 4, length - 3);
+ break;
+ }
+ hex_block(" ", x + 4, length - 3);
+ break;
+ case 0x04:
+ data_block = "Speaker Allocation Data Block";
+ printf(" %s:\n", data_block.c_str());
+ cta_sadb(x + 1, length);
+ audio_block = true;
+ if (duplicate)
+ fail("Only one instance of this Data Block is allowed.\n");
+ break;
+ case 0x05:
+ data_block = "VESA Display Transfer Characteristics Data Block";
+ printf(" %s:\n", data_block.c_str());
+ cta_vesa_dtcdb(x + 1, length);
+ if (duplicate)
+ fail("Only one instance of this Data Block is allowed.\n");
+ break;
+ case 0x07:
+ cta_ext_block(x + 1, length - 1, duplicate);
+ break;
+ default: {
+ unsigned tag = (*x & 0xe0) >> 5;
+ unsigned length = *x & 0x1f;
+
+ printf(" Unknown CTA-861 tag 0x%02x, length %u\n", tag, length);
+ hex_block(" ", x + 1, length);
+ data_block.clear();
+ warn("Unknown CTA-861 Data Block %u.\n", tag);
+ break;
+ }
+ }
+
+ // See Table 52 of CTA-861-G for a description of Byte 3
+ if (audio_block && !(cta.byte3 & 0x40))
+ fail("audio information is present, but bit 6 of Byte 3 of the CTA-861 Extension header indicates no Basic Audio support.\n");
+ cta.first_block = 0;
+ cta.last_block_was_hdmi_vsdb = 0;
+}
+
+void edid_state::preparse_cta_block(const unsigned char *x)
+{
+ unsigned version = x[1];
+ unsigned offset = x[2];
+
+ if (offset >= 4) {
+ const unsigned char *detailed;
+
+ for (detailed = x + offset; detailed + 17 < x + 127; detailed += 18) {
+ if (memchk(detailed, 18))
+ break;
+ if (detailed[0] || detailed[1])
+ cta.preparsed_total_dtds++;
+ }
+ }
+
+ if (version < 3)
+ return;
+
+ for (unsigned i = 4; i < offset; i += (x[i] & 0x1f) + 1) {
+ bool for_ycbcr420 = false;
+ unsigned oui;
+
+ switch ((x[i] & 0xe0) >> 5) {
+ case 0x03:
+ oui = (x[i + 3] << 16) + (x[i + 2] << 8) + x[i + 1];
+ if (oui == 0x000c03) {
+ cta.has_hdmi = true;
+ cta.preparsed_phys_addr = (x[i + 4] << 8) | x[i + 5];
+ }
+ break;
+ case 0x07:
+ if (x[i + 1] == 0x0d)
+ cta.has_vfpdb = true;
+ if (x[i + 1] == 0x13 && (x[i + 2] & 0x40)) {
+ cta.preparsed_speaker_count = 1 + (x[i + 2] & 0x1f);
+ cta.preparsed_sld = x[i + 2] & 0x20;
+ }
+ if (x[i + 1] == 0x14)
+ cta_preparse_sldb(x + i + 2, (x[i] & 0x1f) - 1);
+ if (x[i + 1] == 0x22)
+ cta.preparsed_total_vtdbs++;
+ if (x[i + 1] == 0x23)
+ cta.preparsed_has_t8vtdb = true;
+ if (x[i + 1] == 0x32)
+ cta.preparsed_total_vtdbs +=
+ ((x[i] & 0x1f) - 2) / (6 + ((x[i + 2] & 0x70) >> 4));
+ if (x[i + 1] != 0x0e)
+ continue;
+ for_ycbcr420 = true;
+ /* fall-through */
+ case 0x02:
+ for (unsigned j = 1 + for_ycbcr420; j <= (x[i] & 0x1f); j++) {
+ unsigned char vic = x[i + j];
+
+ if ((vic & 0x7f) <= 64)
+ vic &= 0x7f;
+ cta.preparsed_svds[for_ycbcr420].push_back(vic);
+ cta.preparsed_has_vic[for_ycbcr420][vic] = true;
+ }
+ break;
+ }
+ }
+}
+
+void edid_state::parse_cta_block(const unsigned char *x)
+{
+ unsigned version = x[1];
+ unsigned offset = x[2];
+ const unsigned char *detailed;
+
+ // See Table 52 of CTA-861-G for a description of Byte 3
+
+ printf(" Revision: %u\n", version);
+ if (version == 0)
+ fail("Invalid CTA-861 Extension revision 0.\n");
+ if (version == 2)
+ fail("Deprecated CTA-861 Extension revision 2.\n");
+ if (cta.has_hdmi && version != 3)
+ fail("The HDMI Specification requires CTA Extension revision 3.\n");
+ if (version > 3)
+ warn("Unknown CTA-861 Extension revision %u.\n", version);
+
+ if (version >= 1) do {
+ if (version == 1 && x[3] != 0)
+ fail("Non-zero byte 3.\n");
+
+ if (offset < 4)
+ break;
+
+ if (version < 3 && ((offset - 4) / 8)) {
+ printf(" 8-byte timing descriptors: %u\n", (offset - 4) / 8);
+ fail("8-byte descriptors were never used.\n");
+ }
+
+ if (version >= 2) {
+ if (x[3] & 0x80)
+ printf(" Underscans IT Video Formats by default\n");
+ else
+ warn("IT Video Formats are overscanned by default, but normally this should be underscanned.\n");
+ if (x[3] & 0x40)
+ printf(" Basic audio support\n");
+ if (x[3] & 0x20)
+ printf(" Supports YCbCr 4:4:4\n");
+ if (x[3] & 0x10)
+ printf(" Supports YCbCr 4:2:2\n");
+ // Disable this test: this fails a lot of EDIDs, and there are
+ // also some corner cases where you only want to receive 4:4:4
+ // and refuse a fallback to 4:2:2.
+// if ((x[3] & 0x30) && (x[3] & 0x30) != 0x30)
+// msg(!cta.has_hdmi, "If YCbCr support is indicated, then both 4:2:2 and 4:4:4 %s be supported.\n",
+// cta.has_hdmi ? "shall" : "should");
+ printf(" Native detailed modes: %u\n", x[3] & 0x0f);
+ if (cta.first_block)
+ cta.byte3 = x[3];
+ else if (x[3] != cta.byte3)
+ fail("Byte 3 must be the same for all CTA-861 Extension Blocks.\n");
+ if (cta.first_block) {
+ unsigned native_dtds = x[3] & 0x0f;
+
+ cta.native_timings.clear();
+ if (!native_dtds && !cta.has_vfpdb) {
+ cta.first_svd_might_be_preferred = true;
+ } else if (native_dtds > cta.preparsed_total_dtds) {
+ fail("There are more Native DTDs (%u) than DTDs (%u).\n",
+ native_dtds, cta.preparsed_total_dtds);
+ }
+ if (native_dtds > cta.preparsed_total_dtds)
+ native_dtds = cta.preparsed_total_dtds;
+ for (unsigned i = 0; i < native_dtds; i++) {
+ char type[16];
+
+ sprintf(type, "DTD %3u", i + 1);
+ cta.native_timings.push_back(timings_ext(i + 129, type));
+ }
+ if (cta.has_hdmi && block_nr != (block_map.saw_block_1 ? 2 : 1))
+ fail("The HDMI Specification requires that the first Extension Block (that is not a Block Map) is an CTA-861 Extension Block.\n");
+ }
+ }
+ if (version >= 3) {
+ unsigned i;
+
+ for (i = 4; i < offset; i += (x[i] & 0x1f) + 1) {
+ unsigned tag = (x[i] & 0xe0) << 3;
+
+ if (tag == 0x700)
+ tag |= x[i + 1];
+ bool duplicate = cta.found_tags.find(tag) != cta.found_tags.end();
+
+ cta_block(x + i, duplicate);
+ if (!duplicate)
+ cta.found_tags.insert(tag);
+ }
+
+ data_block.clear();
+ if (i != offset)
+ fail("Offset is %u, but should be %u.\n", offset, i);
+ }
+
+ data_block = "Detailed Timing Descriptors";
+ base.seen_non_detailed_descriptor = false;
+ bool first = true;
+ for (detailed = x + offset; detailed + 17 < x + 127; detailed += 18) {
+ if (memchk(detailed, 18))
+ break;
+ if (first) {
+ first = false;
+ printf(" %s:\n", data_block.c_str());
+ }
+ detailed_block(detailed);
+ }
+ if (!memchk(detailed, x + 127 - detailed)) {
+ data_block = "Padding";
+ fail("CTA-861 padding contains non-zero bytes.\n");
+ }
+ } while (0);
+
+ data_block.clear();
+ if (base.has_serial_number && base.has_serial_string)
+ warn("Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0.\n");
+ if (!cta.has_vic_1 && !base.has_640x480p60_est_timing)
+ fail("Required 640x480p60 timings are missing in the established timings"
+ " and the SVD list (VIC 1).\n");
+ if ((cta.supported_hdmi_vic_vsb_codes & cta.supported_hdmi_vic_codes) !=
+ cta.supported_hdmi_vic_codes)
+ fail("HDMI VIC Codes must have their CTA-861 VIC equivalents in the VSB.\n");
+ if (!cta.has_vcdb)
+ fail("Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues.\n");
+}
+
+void edid_state::cta_resolve_svr(vec_timings_ext::iterator iter)
+{
+ if (iter->svr() == 254) {
+ iter->flags = cta.t8vtdb.flags;
+ iter->t = cta.t8vtdb.t;
+ } else if (iter->svr() <= 144) {
+ iter->flags = cta.vec_dtds[iter->svr() - 129].flags;
+ iter->t = cta.vec_dtds[iter->svr() - 129].t;
+ } else {
+ iter->flags = cta.vec_vtdbs[iter->svr() - 145].flags;
+ iter->t = cta.vec_vtdbs[iter->svr() - 145].t;
+ }
+}
+
+void edid_state::cta_resolve_svrs()
+{
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter) {
+ if (iter->has_svr())
+ cta_resolve_svr(iter);
+ }
+
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter) {
+ if (iter->has_svr())
+ cta_resolve_svr(iter);
+ }
+}
+
+void edid_state::check_cta_blocks()
+{
+ unsigned max_pref_prog_hact = 0;
+ unsigned max_pref_prog_vact = 0;
+ unsigned max_pref_ilace_hact = 0;
+ unsigned max_pref_ilace_vact = 0;
+
+ data_block = "CTA-861";
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter) {
+ if (iter->t.interlaced &&
+ (iter->t.vact > max_pref_ilace_vact ||
+ (iter->t.vact == max_pref_ilace_vact && iter->t.hact >= max_pref_ilace_hact))) {
+ max_pref_ilace_hact = iter->t.hact;
+ max_pref_ilace_vact = iter->t.vact;
+ }
+ if (!iter->t.interlaced &&
+ (iter->t.vact > max_pref_prog_vact ||
+ (iter->t.vact == max_pref_prog_vact && iter->t.hact >= max_pref_prog_hact))) {
+ max_pref_prog_hact = iter->t.hact;
+ max_pref_prog_vact = iter->t.vact;
+ }
+ }
+
+ unsigned native_prog = 0;
+ unsigned native_prog_hact = 0;
+ unsigned native_prog_vact = 0;
+ bool native_prog_mixed_resolutions = false;
+ unsigned native_ilace = 0;
+ unsigned native_ilace_hact = 0;
+ unsigned native_ilace_vact = 0;
+ bool native_ilace_mixed_resolutions = false;
+
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter) {
+ if (iter->t.interlaced) {
+ native_ilace++;
+ if (!native_ilace_hact) {
+ native_ilace_hact = iter->t.hact;
+ native_ilace_vact = iter->t.vact;
+ } else if (native_ilace_hact != iter->t.hact ||
+ native_ilace_vact != iter->t.vact) {
+ native_ilace_mixed_resolutions = true;
+ }
+ } else {
+ native_prog++;
+ if (!native_prog_hact) {
+ native_prog_hact = iter->t.hact;
+ native_prog_vact = iter->t.vact;
+ } else if (native_prog_hact != iter->t.hact ||
+ native_prog_vact != iter->t.vact) {
+ native_prog_mixed_resolutions = true;
+ }
+ }
+ }
+
+ if (native_prog_mixed_resolutions)
+ fail("Native progressive timings are a mix of several resolutions.\n");
+ if (native_ilace_mixed_resolutions)
+ fail("Native interlaced timings are a mix of several resolutions.\n");
+ if (native_ilace && !native_prog)
+ fail("A native interlaced timing is present, but not a native progressive timing.\n");
+ if (!native_prog_mixed_resolutions && native_prog > 1)
+ warn("Multiple native progressive timings are defined.\n");
+ if (!native_ilace_mixed_resolutions && native_ilace > 1)
+ warn("Multiple native interlaced timings are defined.\n");
+
+ if (!native_prog_mixed_resolutions && native_prog_vact &&
+ (max_pref_prog_vact > native_prog_vact ||
+ (max_pref_prog_vact == native_prog_vact && max_pref_prog_hact > native_prog_hact)))
+ warn("Native progressive resolution of %ux%u is smaller than the max preferred progressive resolution %ux%u.\n",
+ native_prog_hact, native_prog_vact,
+ max_pref_prog_hact, max_pref_prog_vact);
+ if (!native_ilace_mixed_resolutions && native_ilace_vact &&
+ (max_pref_ilace_vact > native_ilace_vact ||
+ (max_pref_ilace_vact == native_ilace_vact && max_pref_ilace_hact > native_ilace_hact)))
+ warn("Native interlaced resolution of %ux%u is smaller than the max preferred interlaced resolution %ux%u.\n",
+ native_ilace_hact, native_ilace_vact,
+ max_pref_ilace_hact, max_pref_ilace_vact);
+
+ if (dispid.native_width && native_prog_hact &&
+ !native_prog_mixed_resolutions) {
+ if (dispid.native_width != native_prog_hact ||
+ dispid.native_height != native_prog_vact)
+ fail("Mismatch between CTA-861 and DisplayID native progressive resolution.\n");
+ }
+}
diff --git a/parse-di-ext-block.cpp b/parse-di-ext-block.cpp
new file mode 100644
index 0000000..9751363
--- /dev/null
+++ b/parse-di-ext-block.cpp
@@ -0,0 +1,499 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include "edid-decode.h"
+
+void edid_state::parse_digital_interface(const unsigned char *x)
+{
+ data_block = "Digital Interface";
+ printf(" %s:\n", data_block.c_str());
+
+ printf(" Supported Digital Interface: ");
+ unsigned short v = x[2];
+ switch (v) {
+ case 0x00:
+ printf("Analog Video Input\n");
+ if (!memchk(x + 2, 12))
+ fail("Bytes 0x02-0x0d should be 0.\n");
+ return;
+ case 0x01: printf("DVI\n"); break;
+ case 0x02: printf("DVI Single Link\n"); break;
+ case 0x03: printf("DVI Dual Link - High Resolution\n"); break;
+ case 0x04: printf("DVI Dual Link - High Color\n"); break;
+ case 0x05: printf("DVI - Consumer Electronics\n"); break;
+ case 0x06: printf("Plug & Display\n"); break;
+ case 0x07: printf("DFP\n"); break;
+ case 0x08: printf("Open LDI - Single Link\n"); break;
+ case 0x09: printf("Open LDI - Dual Link\n"); break;
+ case 0x0a: printf("Open LDI - Consumer Electronics\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Digital Interface 0x%02x.\n", v);
+ break;
+ }
+
+ switch ((x[3]) >> 6) {
+ case 0x00:
+ if (!memchk(x + 3, 4))
+ fail("Bytes 0x03-0x06 should be 0.\n");
+ break;
+ case 0x01:
+ printf(" Version: %u.%u\n Release: %u.%u\n", x[3] & 0x3f, x[4], x[5], x[6]);
+ if (x[4] > 99)
+ fail("Version number > 99.\n");
+ if (x[6] > 99)
+ fail("Release number > 99.\n");
+ break;
+ case 0x02:
+ if (x[3] & 0x3f)
+ fail("Bits 5-0 of byte 0x03 should be 0.\n");
+ if (x[5] || x[6])
+ fail("Bytes 0x05-0x06 should be 0.\n");
+ printf(" Letter Designation: %c\n", x[4]);
+ break;
+ case 0x03:
+ if (x[3] & 0x3f)
+ fail("Bits 5-0 of byte 0x03 should be 0.\n");
+ printf(" Date Code: Year %u Week %u Day %u\n", 1990 + x[4], x[5], x[6]);
+ if (!x[5] || x[5] > 12)
+ fail("Bad month number.\n");
+ if (!x[6] || x[6] > 31)
+ fail("Bad day number.\n");
+ break;
+ }
+
+ v = x[7];
+ printf(" Data Enable Signal Usage %sAvailable\n",
+ (v & 0x80) ? "" : "Not ");
+ if (v & 0x80)
+ printf(" Data Enable Signal %s\n",
+ (v & 0x40) ? "High" : "Low");
+ else if (v & 0x40)
+ fail("Bit 6 of byte 0x07 should be 0.\n");
+ printf(" Edge of Shift Clock: ");
+ switch ((v >> 4) & 0x03) {
+ case 0: printf("Not specified\n"); break;
+ case 1: printf("Use rising edge of shift clock\n"); break;
+ case 2: printf("Use falling edge of shift clock\n"); break;
+ case 3: printf("Use both edges of shift clock\n"); break;
+ }
+ printf(" HDCP is %ssupported\n", (v & 0x08) ? "" : "not ");
+ printf(" Digital Receivers %ssupport Double Clocking of Input Data\n",
+ (v & 0x04) ? "" : "do not ");
+ printf(" Packetized Digital Video is %ssupported\n", (v & 0x02) ? "" : "not ");
+ if (v & 0x01)
+ fail("Bit 0 of byte 0x07 should be 0.\n");
+
+ v = x[8];
+ printf(" Data Formats: ");
+ switch (v) {
+ case 0x15: printf("8-Bit Over 8-Bit RGB\n"); break;
+ case 0x19: printf("12-Bit Over 12-Bit RGB\n"); break;
+ case 0x24: printf("24-Bit MSB-Aligned RGB (Single Link)\n"); break;
+ case 0x48: printf("48-Bit MSB-Aligned RGB (Dual Link - High Resolution)\n"); break;
+ case 0x49: printf("48-Bit MSB-Aligned RGB (Dual Link - High Color)\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Data Format 0x%02x.\n", v);
+ break;
+ }
+ if (x[2] == 0x03 && v != 0x48)
+ fail("Data Format should be 0x48, not 0x%02x.\n", v);
+ if (x[2] == 0x04 && v != 0x49)
+ fail("Data Format should be 0x49, not 0x%02x.\n", v);
+
+ v = x[9];
+ if (v) {
+ printf(" Minimum Pixel Clock Frequency Per Link: %u MHz\n", v);
+ if (v == 0xff)
+ fail("Invalid Min-PCF 0x%02x.\n", v);
+ }
+
+ v = x[10] | (x[11] << 8);
+ if (v) {
+ printf(" Maximum Pixel Clock Frequency Per Link: %u MHz\n", v);
+ if (v == 0xffff)
+ fail("Invalid Max-PCF 0x%04x.\n", v);
+ }
+
+ v = x[12] | (x[13] << 8);
+ if (v == 0xffff)
+ printf(" Crossover Frequency: None - Single Link\n");
+ else if (v)
+ printf(" Crossover Frequency: %u MHz\n", v);
+}
+
+void edid_state::parse_display_device(const unsigned char *x)
+{
+ data_block = "Display Device";
+ printf(" %s:\n", data_block.c_str());
+
+ printf(" Sub-Pixel Layout: ");
+ unsigned char v = x[0x0e];
+ switch (v) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("RGB\n"); break;
+ case 0x02: printf("BGR\n"); break;
+ case 0x03: printf("Quad Pixel - G at bottom left & top right\n"); break;
+ case 0x04: printf("Quad Pixel - G at bottom right & top left\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Sub-Pixel Layout 0x%02x.\n", v);
+ break;
+ }
+ printf(" Sub-Pixel Configuration: ");
+ v = x[0x0f];
+ switch (v) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("Delta (Tri-ad)\n"); break;
+ case 0x02: printf("Stripe\n"); break;
+ case 0x03: printf("Stripe Offset\n"); break;
+ case 0x04: printf("Quad Pixel\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Sub-Pixel Configuration 0x%02x.\n", v);
+ break;
+ }
+ printf(" Sub-Pixel Shape: ");
+ v = x[0x10];
+ switch (v) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("Round\n"); break;
+ case 0x02: printf("Square\n"); break;
+ case 0x03: printf("Rectangular\n"); break;
+ case 0x04: printf("Oval\n"); break;
+ case 0x05: printf("Elliptical\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Sub-Pixel Shape 0x%02x.\n", v);
+ break;
+ }
+ if (x[0x11])
+ printf(" Horizontal Dot/Pixel Pitch: %.2f mm\n",
+ x[0x11] / 100.0);
+ if (x[0x12])
+ printf(" Vertical Dot/Pixel Pitch: %.2f mm\n",
+ x[0x12] / 100.0);
+ v = x[0x13];
+ printf(" Display Device %s a Fixed Pixel Format\n",
+ (v & 0x80) ? "has" : "does not have");
+ printf(" View Direction: ");
+ switch ((v & 0x60) >> 5) {
+ case 0x00: printf("Not specified\n"); break;
+ case 0x01: printf("Direct\n"); break;
+ case 0x02: printf("Reflected\n"); break;
+ case 0x03: printf("Direct & Reflected\n"); break;
+ }
+ printf(" Display Device uses %stransparent background\n",
+ (v & 0x10) ? "" : "non-");
+ printf(" Physical Implementation: ");
+ switch ((v & 0x0c) >> 2) {
+ case 0x00: printf("Not specified\n"); break;
+ case 0x01: printf("Large Image device for group viewing\n"); break;
+ case 0x02: printf("Desktop or personal display\n"); break;
+ case 0x03: printf("Eyepiece type personal display\n"); break;
+ }
+ printf(" Monitor/display does %ssupport DDC/CI\n",
+ (v & 0x02) ? "" : "not ");
+ if (v & 0x01)
+ fail("Bit 0 of byte 0x13 should be 0.\n");
+}
+
+void edid_state::parse_display_caps(const unsigned char *x)
+{
+ data_block = "Display Capabities & Feature Support Set";
+ printf(" %s:\n", data_block.c_str());
+
+ unsigned short v = x[0x14];
+
+ printf(" Legacy Modes: %s VGA/DOS Legacy Timing Modes are supported\n",
+ (v & 0x80) ? "All" : "Not all");
+ printf(" Stereo Video: ");
+ switch ((v & 0x70) >> 4) {
+ case 0x00: printf("No direct stereo\n"); break;
+ case 0x01: printf("Field seq. stereo via stereo sync signal\n"); break;
+ case 0x02: printf("auto-stereoscopic, column interleave\n"); break;
+ case 0x03: printf("auto-stereoscopic, line interleave\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", (v & 0x70) >> 4);
+ fail("Unknown Stereo Video 0x%02x.\n", (v & 0x70) >> 4);
+ break;
+ }
+ printf(" Scaler On Board: %s\n", (v & 0x08) ? "Yes" : "No");
+ printf(" Image Centering: %s\n", (v & 0x04) ? "Yes" : "No");
+ printf(" Conditional Update: %s\n", (v & 0x02) ? "Yes" : "No");
+ printf(" Interlaced Video: %s\n", (v & 0x01) ? "Yes" : "No");
+
+ v = x[0x15];
+ printf(" Frame Lock: %s\n", (v & 0x80) ? "Yes" : "No");
+ printf(" Frame Rate Conversion: ");
+ switch ((v & 0x60) >> 5) {
+ case 0x00: printf("Not supported\n"); break;
+ case 0x01: printf("Vertical is converted to a single frequency\n"); break;
+ case 0x02: printf("Horizontal is convertred to a single frequency\n"); break;
+ case 0x03: printf("Both Vertical & Horizontal are converted to single frequencies\n"); break;
+ }
+ if (v & 0x1f)
+ fail("Bits 4-0 of byte 0x15 should be 0.\n");
+ v = x[0x16] | (x[0x17] << 8);
+ printf(" Vertical Frequency: ");
+ if (!v) {
+ printf("Not available\n");
+ } else if (v == 0xffff) {
+ printf("Reserved\n");
+ fail("Vertical Frequency uses 0xffff (reserved value).\n");
+ } else {
+ printf("%.2f kHz\n", v / 100.0);
+ }
+ v = x[0x18] | (x[0x19] << 8);
+ printf(" Horizontal Frequency: ");
+ if (!v) {
+ printf("Not available\n");
+ } else if (v == 0xffff) {
+ printf("Reserved\n");
+ fail("Horizontal Frequency uses 0xffff (reserved value).\n");
+ } else {
+ printf("%.2f kHz\n", v / 100.0);
+ }
+
+ v = x[0x1a];
+ printf(" Display/Scan Orientation Definition Type: ");
+ switch ((v & 0xc0) >> 6) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("Fixed Orientation\n"); break;
+ case 0x02: printf("Pivots: Default Orientation\n"); break;
+ case 0x03: printf("Pivots: Current Orientation (requires multiple EDID Extension Tables)\n"); break;
+ }
+ printf(" Screen Orientation: %s\n",
+ (v & 0x20) ? "Portrait" : "Landscape");
+ printf(" Zero Pixel Location: ");
+ switch ((v & 0x18) >> 3) {
+ case 0x00: printf("Upper Left\n"); break;
+ case 0x01: printf("Upper Right\n"); break;
+ case 0x02: printf("Lower Left\n"); break;
+ case 0x03: printf("Lower Right\n"); break;
+ }
+ printf(" Scan Direction: ");
+ switch ((v & 0x06) >> 1) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("Fast Scan is on the Major (Long) Axis and Slow Scan is on the Minor Axis\n"); break;
+ case 0x02: printf("Fast Scan is on the Minor (Short) Axis and Slow Scan is on the Major Axis\n"); break;
+ case 0x03:
+ printf("Reserved\n");
+ fail("Scan Direction used the reserved value 0x03.\n");
+ break;
+ }
+ printf(" Standalone Projector: %s\n",
+ (v & 0x01) ? "Yes" : "No");
+
+ v = x[0x1b];
+ printf(" Default Color/Luminance Decoding: ");
+ switch (v) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("BGR\n"); break;
+ case 0x02: printf("Y/C (S-Video) NTSC\n"); break;
+ case 0x03: printf("Y/C (S-Video) PAL\n"); break;
+ case 0x04: printf("Y/C (S-Video) SECAM\n"); break;
+ case 0x05: printf("YCrCb 4:4:4 per SMPTE 293M & 294M\n"); break;
+ case 0x06: printf("YCrCb 4:2:2 per SMPTE 293M & 294M\n"); break;
+ case 0x07: printf("YCrCb 4:2:0 per SMPTE 293M & 294M\n"); break;
+ case 0x08: printf("YCrCb per SMPTE 260M (Legacy HDTV)\n"); break;
+ case 0x09: printf("YPbPr per SMPTE 240M (Legacy HDTV)\n"); break;
+ case 0x0a: printf("YCrCb per SMPTE 274M (Modern HDTV)\n"); break;
+ case 0x0b: printf("YPbPr per SMPTE 274M (Modern HDTV)\n"); break;
+ case 0x0c: printf("Y B-Y R-Y BetaCam (Sony)\n"); break;
+ case 0x0d: printf("Y B-Y R-Y M-2 (Matsushita)\n"); break;
+ case 0x0e: printf("Monochrome\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Default Color/Luminance Decoding 0x%02x.\n", v);
+ break;
+ }
+ v = x[0x1c];
+ printf(" Preferred Color/Luminance Decoder: ");
+ switch (v) {
+ case 0x00: printf("Uses Default Decoding\n"); break;
+ case 0x01: printf("BGR\n"); break;
+ case 0x02: printf("Y/C (S-Video)\n"); break;
+ case 0x03: printf("Yxx (SMPTE 2xxM)\n"); break;
+ case 0x04: printf("Monochrome\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", v);
+ fail("Unknown Preferred Color/Luminance Decoding 0x%02x.\n", v);
+ break;
+ }
+ v = x[0x1d];
+ if (v && (x[0x1e] & 0xfc)) {
+ printf(" Color/Luminance Decoding Capabilities:\n");
+ printf(" BGR: %s\n", (v & 0x80) ? "Yes" : "No");
+ printf(" Y/C (S-Video) NTSC: %s\n", (v & 0x40) ? "Yes" : "No");
+ printf(" Y/C (S-Video) PAL: %s\n", (v & 0x20) ? "Yes" : "No");
+ printf(" Y/C (S-Video) SECAM: %s\n", (v & 0x10) ? "Yes" : "No");
+ printf(" YCrCb 4:4:4 per SMPTE 293M & 294M: %s\n", (v & 0x08) ? "Yes" : "No");
+ printf(" YCrCb 4:2:2 per SMPTE 293M & 294M: %s\n", (v & 0x04) ? "Yes" : "No");
+ printf(" YCrCb 4:2:0 per SMPTE 293M & 294M: %s\n", (v & 0x02) ? "Yes" : "No");
+ printf(" YCrCb per SMPTE 260M (Legacy HDTV): %s\n", (v & 0x01) ? "Yes" : "No");
+ v = x[0x1e];
+ printf(" YPbPr per SMPTE 240M (Legacy HDTV): %s\n", (v & 0x80) ? "Yes" : "No");
+ printf(" YCrCb per SMPTE 274M (Modern HDTV): %s\n", (v & 0x40) ? "Yes" : "No");
+ printf(" YPbPr per SMPTE 274M (Modern HDTV): %s\n", (v & 0x20) ? "Yes" : "No");
+ printf(" Y B-Y R-Y BetaCam (Sony): %s\n", (v & 0x10) ? "Yes" : "No");
+ printf(" Y B-Y R-Y M-2 (Matsushita): %s\n", (v & 0x08) ? "Yes" : "No");
+ printf(" Monochrome: %s\n", (v & 0x04) ? "Yes" : "No");
+ } else {
+ printf(" Color/Luminance Decoding Capabilities: None\n");
+ }
+ if (v & 0x03)
+ fail("Bits 1-0 of byte 0x1e should be 0.\n");
+
+ v = x[0x1f];
+ printf(" Dithering: %s\n", (v & 0x80) ? "Yes" : "No");
+ if (v & 0x7f)
+ fail("Bits 6-0 of byte 0x1f should be 0.\n");
+ v = x[0x20];
+ printf(" Supported Color Bit-Depth of Sub-Channel 0 (Blue): ");
+ if (!v) {
+ printf("No Information\n");
+ } else if (v <= 16) {
+ printf("%u\n", v);
+ } else {
+ printf("Reserved (0x%02x)\n", v);
+ fail("Supported Color Bit-Depth of Sub-Channel Blue value is 0x%02x.\n", v);
+ }
+ v = x[0x21];
+ printf(" Supported Color Bit-Depth of Sub-Channel 1 (Green): ");
+ if (!v) {
+ printf("No Information\n");
+ } else if (v <= 16) {
+ printf("%u\n", v);
+ } else {
+ printf("Reserved (0x%02x)\n", v);
+ fail("Supported Color Bit-Depth of Sub-Channel Green value is 0x%02x.\n", v);
+ }
+ v = x[0x22];
+ printf(" Supported Color Bit-Depth of Sub-Channel 2 (Red): ");
+ if (!v) {
+ printf("No Information\n");
+ } else if (v <= 16) {
+ printf("%u\n", v);
+ } else {
+ printf("Reserved (0x%02x)\n", v);
+ fail("Supported Color Bit-Depth of Sub-Channel Red value is 0x%02x.\n", v);
+ }
+ v = x[0x23];
+ printf(" Supported Color Bit-Depth of Sub-Channel 0 (Cb/Pb): ");
+ if (!v) {
+ printf("No Information\n");
+ } else if (v <= 16) {
+ printf("%u\n", v);
+ } else {
+ printf("Reserved (0x%02x)\n", v);
+ fail("Supported Color Bit-Depth of Sub-Channel Cb/Pb value is 0x%02x.\n", v);
+ }
+ v = x[0x24];
+ printf(" Supported Color Bit-Depth of Sub-Channel 1 (Y): ");
+ if (!v) {
+ printf("No Information\n");
+ } else if (v <= 16) {
+ printf("%u\n", v);
+ } else {
+ printf("Reserved (0x%02x)\n", v);
+ fail("Supported Color Bit-Depth of Sub-Channel Y value is 0x%02x.\n", v);
+ }
+ v = x[0x25];
+ printf(" Supported Color Bit-Depth of Sub-Channel 2 (Cr/Pr): ");
+ if (!v) {
+ printf("No Information\n");
+ } else if (v <= 16) {
+ printf("%u\n", v);
+ } else {
+ printf("Reserved (0x%02x)\n", v);
+ fail("Supported Color Bit-Depth of Sub-Channel Cr/Pr value is 0x%02x.\n", v);
+ }
+
+ v = x[0x26];
+ printf(" Aspect Ratio Conversion Modes:");
+ if (!v) {
+ printf(" None\n");
+ } else {
+ printf("\n");
+ printf(" Full Mode: %s\n", (v & 0x80) ? "Yes" : "No");
+ printf(" Zoom Mode: %s\n", (v & 0x40) ? "Yes" : "No");
+ printf(" Squeeze (Side Bars/Letterbox) Mode: %s\n", (v & 0x20) ? "Yes" : "No");
+ printf(" Variable (Expand/Shrink) Mode: %s\n", (v & 0x10) ? "Yes" : "No");
+ }
+ if (v & 0x0f)
+ fail("Bits 3-0 of byte 0x26 should be 0.\n");
+}
+
+void edid_state::parse_display_xfer(const unsigned char *x)
+{
+ data_block = "Display Transfer Characteristics - Gamma";
+ printf(" %s:\n", data_block.c_str());
+
+ unsigned char v = x[0x51];
+ unsigned num_entries = v & 0x3f;
+
+ switch ((v & 0xc0) >> 6) {
+ case 0x00:
+ printf(" No Display Transfer Characteristics\n");
+ if (!memchk(x + 0x51, 46))
+ fail("Bytes 0x51-0x7e should be 0.\n");
+ return;
+ case 0x03:
+ fail("Bits 7-6 of byte 0x51 cannot be 0x03.\n");
+ return;
+ default:
+ break;
+ }
+
+ if (((v & 0xc0) >> 6) == 0x01) {
+ if (!num_entries || num_entries > 45)
+ fail("White Curve with %u entries.\n", num_entries);
+ if (num_entries > 45)
+ num_entries = 45;
+ if (!memchk(x + 0x52 + num_entries, 45 - num_entries))
+ fail("Bytes 0x%02x-0x7e should be 0.\n", 0x52 + num_entries);
+ printf(" White Curve (%u entries):\n", num_entries);
+ hex_block(" ", x + 0x52, num_entries, false, 15);
+ } else {
+ if (!num_entries || num_entries > 15)
+ fail("Sub-Channel Curve with %u entries.\n", num_entries);
+ if (num_entries > 15)
+ num_entries = 15;
+ printf(" Sub-Channel 0 (Blue) Curve with %u entries:\n", num_entries);
+ hex_block(" ", x + 0x52, num_entries, false);
+ if (!memchk(x + 0x52 + num_entries, 15 - num_entries))
+ fail("Bytes 0x%02x-0x7e should be 0.\n", 0x52 + num_entries);
+ printf(" Sub-Channel 1 (Green) Curve with %u entries:\n", num_entries);
+ hex_block(" ", x + 0x52 + 15, num_entries, false);
+ if (!memchk(x + 0x52 + 15 + num_entries, 15 - num_entries))
+ fail("Bytes 0x%02x-0x7e should be 0.\n", 0x52 + 15 + num_entries);
+ printf(" Sub-Channel 2 (Red) Curve with %u entries:\n", num_entries);
+ hex_block(" ", x + 0x52 + 30, num_entries, false);
+ if (!memchk(x + 0x52 + 30 + num_entries, 15 - num_entries))
+ fail("Bytes 0x%02x-0x7e should be 0.\n", 0x52 + 30 + num_entries);
+ }
+}
+
+void edid_state::parse_di_ext_block(const unsigned char *x)
+{
+ printf(" Version: %u\n", x[1]);
+ if (!x[1])
+ fail("Invalid version 0.\n");
+
+ parse_digital_interface(x);
+ parse_display_device(x);
+ parse_display_caps(x);
+ if (!memchk(x + 0x27, 16))
+ fail("Bytes 0x27-0x36 should be 0.\n");
+ if (!memchk(x + 0x37, 17))
+ fail("Bytes 0x37-0x47 should be 0.\n");
+ if (!memchk(x + 0x48, 9))
+ fail("Bytes 0x48-0x50 should be 0.\n");
+ parse_display_xfer(x);
+}
diff --git a/parse-displayid-block.cpp b/parse-displayid-block.cpp
new file mode 100644
index 0000000..5bd9734
--- /dev/null
+++ b/parse-displayid-block.cpp
@@ -0,0 +1,1879 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2006-2012 Red Hat, Inc.
+ * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Adam Jackson <ajax@nwnk.net>
+ * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include <math.h>
+
+#include "edid-decode.h"
+
+static const char *bpc444[] = {"6", "8", "10", "12", "14", "16", NULL, NULL};
+static const char *bpc4xx[] = {"8", "10", "12", "14", "16", NULL, NULL, NULL};
+static const char *audiorates[] = {"32", "44.1", "48", NULL, NULL, NULL, NULL, NULL};
+
+// misc functions
+
+static void print_flags(const char *label, unsigned char flag_byte,
+ const char **flags, bool reverse = false)
+{
+ if (!flag_byte)
+ return;
+
+ unsigned countflags = 0;
+
+ printf("%s: ", label);
+ for (unsigned i = 0; i < 8; i++) {
+ if (flag_byte & (1 << (reverse ? 7 - i : i))) {
+ if (countflags)
+ printf(", ");
+ if (flags[i])
+ printf("%s", flags[i]);
+ else
+ printf("Undefined (%u)", i);
+ countflags++;
+ }
+ }
+ printf("\n");
+}
+
+void edid_state::check_displayid_datablock_revision(unsigned char hdr,
+ unsigned char valid_flags,
+ unsigned char rev)
+{
+ unsigned char revision = hdr & 7;
+ unsigned char flags = hdr & ~7 & ~valid_flags;
+
+ if (revision != rev)
+ warn("Unexpected revision (%u != %u).\n", revision, rev);
+ if (flags)
+ warn("Unexpected flags (0x%02x).\n", flags);
+}
+
+static bool check_displayid_datablock_length(const unsigned char *x,
+ unsigned expectedlenmin = 0,
+ unsigned expectedlenmax = 128 - 2 - 5 - 3,
+ unsigned payloaddumpstart = 0)
+{
+ unsigned char len = x[2];
+
+ if (expectedlenmin == expectedlenmax && len != expectedlenmax)
+ fail("DisplayID payload length is different than expected (%d != %d).\n", len, expectedlenmax);
+ else if (len > expectedlenmax)
+ fail("DisplayID payload length is greater than expected (%d > %d).\n", len, expectedlenmax);
+ else if (len < expectedlenmin)
+ fail("DisplayID payload length is less than expected (%d < %d).\n", len, expectedlenmin);
+ else
+ return true;
+
+ if (len > payloaddumpstart)
+ hex_block(" ", x + 3 + payloaddumpstart, len - payloaddumpstart);
+ return false;
+}
+
+// tag 0x00 and 0x20
+
+void edid_state::parse_displayid_product_id(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ dispid.has_product_identification = true;
+ if (dispid.version >= 0x20) {
+ unsigned oui = (x[3] << 16) | (x[4] << 8) | x[5];
+ printf(" Vendor OUI %s\n", ouitohex(oui).c_str());
+ } else {
+ printf(" Vendor ID: %c%c%c\n", x[3], x[4], x[5]);
+ }
+ printf(" Product Code: %u\n", x[6] | (x[7] << 8));
+ unsigned sn = x[8] | (x[9] << 8) | (x[10] << 16) | (x[11] << 24);
+ if (sn) {
+ if (hide_serial_numbers)
+ printf(" Serial Number: ...\n");
+ else
+ printf(" Serial Number: %u\n", sn);
+ }
+ unsigned week = x[12];
+ unsigned year = 2000 + x[13];
+ printf(" %s: %u",
+ week == 0xff ? "Model Year" : "Year of Manufacture", year);
+ if (week && week <= 0x36)
+ printf(", Week %u", week);
+ printf("\n");
+ if (x[14]) {
+ char buf[256];
+
+ memcpy(buf, x + 15, x[14]);
+ buf[x[14]] = 0;
+ printf(" Product ID: %s\n", buf);
+ }
+}
+
+// tag 0x01
+
+static const char *feature_support_flags[] = {
+ "De-interlacing",
+ "Support ACP, ISRC1, or ISRC2packets",
+ "Fixed pixel format",
+ "Fixed timing",
+ "Power management (DPM)",
+ "Audio input override",
+ "Separate audio inputs provided",
+ "Audio support on video interface"
+};
+
+static void print_flag_lines(const char *indent, const char *label,
+ unsigned char flag_byte, const char **flags)
+{
+ if (flag_byte) {
+ printf("%s\n", label);
+
+ for (int i = 0; i < 8; i++)
+ if (flag_byte & (1 << i))
+ printf("%s%s\n", indent, flags[i]);
+ }
+}
+
+void edid_state::parse_displayid_parameters(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 12, 12))
+ return;
+
+ dispid.has_display_parameters = true;
+ printf(" Image size: %.1f mm x %.1f mm\n",
+ ((x[4] << 8) + x[3]) / 10.0,
+ ((x[6] << 8) + x[5]) / 10.0);
+ printf(" Pixels: %d x %d\n",
+ (x[8] << 8) + x[7], (x[10] << 8) + x[9]);
+ print_flag_lines(" ", " Feature support flags:",
+ x[11], feature_support_flags);
+
+ if (x[12] != 0xff)
+ printf(" Gamma: %.2f\n", ((x[12] + 100.0) / 100.0));
+ printf(" Aspect ratio: %.2f\n", ((x[13] + 100.0) / 100.0));
+ printf(" Dynamic bpc native: %d\n", (x[14] & 0xf) + 1);
+ printf(" Dynamic bpc overall: %d\n", ((x[14] >> 4) & 0xf) + 1);
+}
+
+// tag 0x02
+
+static const char *std_colorspace_ids[] = {
+ "sRGB",
+ "BT.601",
+ "BT.709",
+ "Adobe RGB",
+ "DCI-P3",
+ "NTSC",
+ "EBU",
+ "Adobe Wide Gamut RGB",
+ "DICOM"
+};
+
+static double fp2d(unsigned short fp)
+{
+ return fp / 4096.0;
+}
+
+void edid_state::parse_displayid_color_characteristics(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1], 0xf8, 1);
+
+ unsigned cie_year = (x[1] & 0x80) ? 1976 : 1931;
+ unsigned xfer_id = (x[1] >> 3) & 0x0f;
+ unsigned num_whitepoints = x[3] & 0x0f;
+ unsigned num_primaries = (x[3] >> 4) & 0x07;
+ bool temporal_color = x[3] & 0x80;
+ unsigned offset = 4;
+
+ printf(" Uses %s color\n", temporal_color ? "temporal" : "spatial");
+ printf(" Uses %u CIE (x, y) coordinates\n", cie_year);
+ if (xfer_id) {
+ printf(" Associated with Transfer Characteristics Data Block with Identifier %u\n", xfer_id);
+ if (!(dispid.preparsed_xfer_ids & (1 << xfer_id)))
+ fail("Missing Transfer Characteristics Data Block with Identifier %u.\n", xfer_id);
+ }
+ if (!num_primaries) {
+ printf(" Uses color space %s\n",
+ x[4] >= ARRAY_SIZE(std_colorspace_ids) ? "Reserved" :
+ std_colorspace_ids[x[4]]);
+ offset++;
+ }
+ for (unsigned i = 0; i < num_primaries; i++) {
+ unsigned idx = offset + 3 * i;
+
+ printf(" Primary #%u: (%.4f, %.4f)\n", i,
+ fp2d(x[idx] | ((x[idx + 1] & 0x0f) << 8)),
+ fp2d(((x[idx + 1] & 0xf0) >> 4) | (x[idx + 2] << 4)));
+ }
+ offset += 3 * num_primaries;
+ for (unsigned i = 0; i < num_whitepoints; i++) {
+ unsigned idx = offset + 3 * i;
+
+ printf(" White point #%u: (%.4f, %.4f)\n", i,
+ fp2d(x[idx] | ((x[idx + 1] & 0x0f) << 8)),
+ fp2d(((x[idx + 1] & 0xf0) >> 4) | (x[idx + 2] << 4)));
+ }
+}
+
+// tag 0x03 and 0x22
+
+void edid_state::parse_displayid_type_1_7_timing(const unsigned char *x,
+ bool type7, unsigned block_rev, bool is_cta)
+{
+ struct timings t = {};
+ unsigned hbl, vbl;
+ std::string s("aspect ");
+
+ dispid.has_type_1_7 = true;
+ t.pixclk_khz = (type7 ? 1 : 10) * (1 + (x[0] + (x[1] << 8) + (x[2] << 16)));
+ switch (x[3] & 0xf) {
+ case 0:
+ s += "1:1";
+ t.hratio = t.vratio = 1;
+ break;
+ case 1:
+ s += "5:4";
+ t.hratio = 5;
+ t.vratio = 4;
+ break;
+ case 2:
+ s += "4:3";
+ t.hratio = 4;
+ t.vratio = 3;
+ break;
+ case 3:
+ s += "15:9";
+ t.hratio = 15;
+ t.vratio = 9;
+ break;
+ case 4:
+ s += "16:9";
+ t.hratio = 16;
+ t.vratio = 9;
+ break;
+ case 5:
+ s += "16:10";
+ t.hratio = 16;
+ t.vratio = 10;
+ break;
+ case 6:
+ s += "64:27";
+ t.hratio = 64;
+ t.vratio = 27;
+ break;
+ case 7:
+ s += "256:135";
+ t.hratio = 256;
+ t.vratio = 135;
+ break;
+ default:
+ s += "undefined";
+ if ((x[3] & 0xf) > (dispid.version <= 0x12 ? 7 : 8))
+ fail("Unknown aspect 0x%02x.\n", x[3] & 0xf);
+ break;
+ }
+ switch ((x[3] >> 5) & 0x3) {
+ case 0:
+ s += ", no 3D stereo";
+ break;
+ case 1:
+ s += ", 3D stereo";
+ break;
+ case 2:
+ s += ", 3D stereo depends on user action";
+ break;
+ case 3:
+ s += ", reserved";
+ fail("Reserved stereo 0x03.\n");
+ break;
+ }
+ if (block_rev >= 2 && (x[3] & 0x80))
+ s += ", YCbCr 4:2:0";
+
+ t.hact = 1 + (x[4] | (x[5] << 8));
+ hbl = 1 + (x[6] | (x[7] << 8));
+ t.hfp = 1 + (x[8] | ((x[9] & 0x7f) << 8));
+ t.hsync = 1 + (x[10] | (x[11] << 8));
+ t.hbp = hbl - t.hfp - t.hsync;
+ if ((x[9] >> 7) & 0x1)
+ t.pos_pol_hsync = true;
+ t.vact = 1 + (x[12] | (x[13] << 8));
+ vbl = 1 + (x[14] | (x[15] << 8));
+ t.vfp = 1 + (x[16] | ((x[17] & 0x7f) << 8));
+ t.vsync = 1 + (x[18] | (x[19] << 8));
+ t.vbp = vbl - t.vfp - t.vsync;
+ if ((x[17] >> 7) & 0x1)
+ t.pos_pol_vsync = true;
+
+ if (x[3] & 0x10) {
+ t.interlaced = true;
+ t.vfp /= 2;
+ t.vsync /= 2;
+ t.vbp /= 2;
+ }
+ if (block_rev < 2 && (x[3] & 0x80)) {
+ s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "DTD", s));
+ }
+
+ print_timings(" ", &t, "DTD", s.c_str(), true);
+ if (is_cta) {
+ timings_ext te(t, "DTD", s);
+ cta.vec_vtdbs.push_back(te);
+
+ // Only use a T7VTDB if is cannot be expressed by a
+ // DTD or a T10VTDB.
+ if (t.hact <= 4095 && t.vact <= 4095 &&
+ t.pixclk_khz <= 655360 && !(x[3] & 0xe0)) {
+ fail("This T7VTDB can be represented as an 18-byte DTD.\n");
+ return;
+ }
+ unsigned htot = t.hact + t.hfp + t.hsync + t.hbp;
+ unsigned vtot = t.vact + t.vfp + t.vsync + t.vbp;
+ unsigned refresh = (t.pixclk_khz * 1000ULL) / (htot * vtot);
+
+ for (unsigned rb = RB_NONE; rb <= RB_CVT_V3; rb++) {
+ timings cvt_t = calc_cvt_mode(t.hact, t.vact, refresh, rb);
+ if (match_timings(t, cvt_t)) {
+ fail("This T7VTDB can be represented as a T10VTDB.\n");
+ return;
+ }
+ }
+ timings cvt_t = calc_cvt_mode(t.hact, t.vact, refresh, RB_CVT_V3,
+ false, false, true);
+ if (match_timings(t, cvt_t))
+ fail("This T7VTDB can be represented as a T10VTDB.\n");
+ }
+}
+
+// tag 0x04
+
+void edid_state::parse_displayid_type_2_timing(const unsigned char *x)
+{
+ struct timings t = {};
+ unsigned hbl, vbl;
+ std::string s("aspect ");
+
+ t.pixclk_khz = 10 * (1 + (x[0] + (x[1] << 8) + (x[2] << 16)));
+ t.hact = 8 + 8 * (x[4] | ((x[5] & 0x01) << 8));
+ hbl = 8 + 8 * ((x[5] & 0xfe) >> 1);
+ t.hfp = 8 + 8 * ((x[6] & 0xf0) >> 4);
+ t.hsync = 8 + 8 * (x[6] & 0xf);
+ t.hbp = hbl - t.hfp - t.hsync;
+ if ((x[3] >> 3) & 0x1)
+ t.pos_pol_hsync = true;
+ t.vact = 1 + (x[7] | ((x[8] & 0xf) << 8));
+ vbl = 1 + x[9];
+ t.vfp = 1 + (x[10] >> 4);
+ t.vsync = 1 + (x[10] & 0xf);
+ t.vbp = vbl - t.vfp - t.vsync;
+ if ((x[17] >> 2) & 0x1)
+ t.pos_pol_vsync = true;
+
+ if (x[3] & 0x10) {
+ t.interlaced = true;
+ t.vfp /= 2;
+ t.vsync /= 2;
+ t.vbp /= 2;
+ }
+
+ calc_ratio(&t);
+
+ s += std::to_string(t.hratio) + ":" + std::to_string(t.vratio);
+
+ switch ((x[3] >> 5) & 0x3) {
+ case 0:
+ s += ", no 3D stereo";
+ break;
+ case 1:
+ s += ", 3D stereo";
+ break;
+ case 2:
+ s += ", 3D stereo depends on user action";
+ break;
+ case 3:
+ s += ", reserved";
+ fail("Reserved stereo 0x03.\n");
+ break;
+ }
+ if (x[3] & 0x80) {
+ s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "DTD", s));
+ }
+
+ print_timings(" ", &t, "DTD", s.c_str(), true);
+}
+
+// tag 0x05
+
+void edid_state::parse_displayid_type_3_timing(const unsigned char *x)
+{
+ struct timings t = {};
+ std::string s("aspect ");
+
+ switch (x[0] & 0xf) {
+ case 0:
+ s += "1:1";
+ t.hratio = t.vratio = 1;
+ break;
+ case 1:
+ s += "5:4";
+ t.hratio = 5;
+ t.vratio = 4;
+ break;
+ case 2:
+ s += "4:3";
+ t.hratio = 4;
+ t.vratio = 3;
+ break;
+ case 3:
+ s += "15:9";
+ t.hratio = 15;
+ t.vratio = 9;
+ break;
+ case 4:
+ s += "16:9";
+ t.hratio = 16;
+ t.vratio = 9;
+ break;
+ case 5:
+ s += "16:10";
+ t.hratio = 16;
+ t.vratio = 10;
+ break;
+ case 6:
+ s += "64:27";
+ t.hratio = 64;
+ t.vratio = 27;
+ break;
+ case 7:
+ s += "256:135";
+ t.hratio = 256;
+ t.vratio = 135;
+ break;
+ default:
+ s += "undefined";
+ if ((x[3] & 0xf) > (dispid.version <= 0x12 ? 7 : 8))
+ fail("Unknown aspect 0x%02x.\n", x[3] & 0xf);
+ break;
+ }
+
+ t.rb = ((x[0] & 0x70) >> 4) == 1 ? RB_CVT_V1 : RB_NONE;
+ t.hact = 8 + 8 * x[1];
+ t.vact = t.hact * t.vratio / t.hratio;
+
+ edid_cvt_mode(1 + (x[2] & 0x7f), t);
+
+ if (x[0] & 0x80) {
+ s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "CVT", s));
+ }
+
+ print_timings(" ", &t, "CVT", s.c_str());
+}
+
+// tag 0x06 and 0x23
+
+void edid_state::parse_displayid_type_4_8_timing(unsigned char type, unsigned short id, bool is_cta)
+{
+ const struct timings *t = NULL;
+ char type_name[16];
+
+ switch (type) {
+ case 0: t = find_dmt_id(id); sprintf(type_name, "DMT 0x%02x", id); break;
+ case 1: t = find_vic_id(id); sprintf(type_name, "VIC %3u", id); break;
+ case 2: t = find_hdmi_vic_id(id); sprintf(type_name, "HDMI VIC %u", id); break;
+ default: break;
+ }
+ if (t)
+ print_timings(" ", t, type_name);
+ if (t && is_cta && !cta.t8vtdb.is_valid()) {
+ timings_ext te(*t, type_name, "");
+ cta.t8vtdb = te;
+ }
+}
+
+// tag 0x09
+
+void edid_state::parse_displayid_video_timing_range_limits(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 15, 15))
+ return;
+ printf(" Pixel Clock: %.3f-%.3f MHz\n",
+ (double)((x[3] | (x[4] << 8) | (x[5] << 16)) + 1) / 100.0,
+ (double)((x[6] | (x[7] << 8) | (x[8] << 16)) + 1) / 100.0);
+ printf(" Horizontal Frequency: %u-%u kHz\n", x[9], x[10]);
+ printf(" Minimum Horizontal Blanking: %u pixels\n", x[11] | (x[12] << 8));
+ printf(" Vertical Refresh: %u-%u Hz\n", x[13], x[14]);
+ printf(" Minimum Vertical Blanking: %u lines\n", x[15] | (x[16] << 8));
+ if (x[17] & 0x80)
+ printf(" Supports Interlaced\n");
+ if (x[17] & 0x40)
+ printf(" Supports CVT\n");
+ if (x[17] & 0x20)
+ printf(" Supports CVT Reduced Blanking\n");
+ if (x[17] & 0x10)
+ printf(" Discrete frequency display device\n");
+}
+
+// tag 0x0a and 0x0b
+
+void edid_state::parse_displayid_string(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+ if (check_displayid_datablock_length(x))
+ printf(" Text: '%s'\n", extract_string(x + 3, x[2]));
+}
+
+// tag 0x0c
+
+void edid_state::parse_displayid_display_device(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 13, 13))
+ return;
+
+ printf(" Display Device Technology: ");
+ switch (x[3]) {
+ case 0x00: printf("Monochrome CRT\n"); break;
+ case 0x01: printf("Standard tricolor CRT\n"); break;
+ case 0x02: printf("Other/undefined CRT\n"); break;
+ case 0x10: printf("Passive matrix TN\n"); break;
+ case 0x11: printf("Passive matrix cholesteric LC\n"); break;
+ case 0x12: printf("Passive matrix ferroelectric LC\n"); break;
+ case 0x13: printf("Other passive matrix LC type\n"); break;
+ case 0x14: printf("Active-matrix TN\n"); break;
+ case 0x15: printf("Active-matrix IPS (all types)\n"); break;
+ case 0x16: printf("Active-matrix VA (all types)\n"); break;
+ case 0x17: printf("Active-matrix OCB\n"); break;
+ case 0x18: printf("Active-matrix ferroelectric\n"); break;
+ case 0x1f: printf("Other LC type\n"); break;
+ case 0x20: printf("DC plasma\n"); break;
+ case 0x21: printf("AC plasma\n"); break;
+ }
+ switch (x[3] & 0xf0) {
+ case 0x30: printf("Electroluminescent, except OEL/OLED\n"); break;
+ case 0x40: printf("Inorganic LED\n"); break;
+ case 0x50: printf("Organic LED/OEL\n"); break;
+ case 0x60: printf("FED or sim. \"cold-cathode,\" phosphor-based types\n"); break;
+ case 0x70: printf("Electrophoretic\n"); break;
+ case 0x80: printf("Electrochromic\n"); break;
+ case 0x90: printf("Electromechanical\n"); break;
+ case 0xa0: printf("Electrowetting\n"); break;
+ case 0xf0: printf("Other type not defined here\n"); break;
+ }
+ printf(" Display operating mode: ");
+ switch (x[4] >> 4) {
+ case 0x00: printf("Direct-view reflective, ambient light\n"); break;
+ case 0x01: printf("Direct-view reflective, ambient light, also has light source\n"); break;
+ case 0x02: printf("Direct-view reflective, uses light source\n"); break;
+ case 0x03: printf("Direct-view transmissive, ambient light\n"); break;
+ case 0x04: printf("Direct-view transmissive, ambient light, also has light source\n"); break;
+ case 0x05: printf("Direct-view transmissive, uses light source\n"); break;
+ case 0x06: printf("Direct-view emissive\n"); break;
+ case 0x07: printf("Direct-view transflective, backlight off by default\n"); break;
+ case 0x08: printf("Direct-view transflective, backlight on by default\n"); break;
+ case 0x09: printf("Transparent display, ambient light\n"); break;
+ case 0x0a: printf("Transparent emissive display\n"); break;
+ case 0x0b: printf("Projection device using reflective light modulator\n"); break;
+ case 0x0c: printf("Projection device using transmissive light modulator\n"); break;
+ case 0x0d: printf("Projection device using emissive image transducer\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ if (x[4] & 0x08)
+ printf(" The backlight may be switched on and off\n");
+ if (x[4] & 0x04)
+ printf(" The backlight's intensity can be controlled\n");
+ unsigned w = x[5] | (x[6] << 8);
+ unsigned h = x[7] | (x[8] << 8);
+ if (w && h) {
+ printf(" Display native pixel format: %ux%u\n", w + 1, h + 1);
+ dispid.native_width = w + 1;
+ dispid.native_height = h + 1;
+ } else if (w || h) {
+ fail("Invalid Native Pixel Format %ux%u.\n", w, h);
+ }
+ printf(" Aspect ratio and orientation:\n");
+ printf(" Aspect Ratio: %.2f\n", (100 + x[9]) / 100.0);
+ unsigned char v = x[0x0a];
+ printf(" Default Orientation: ");
+ switch ((v & 0xc0) >> 6) {
+ case 0x00: printf("Landscape\n"); break;
+ case 0x01: printf("Portrait\n"); break;
+ case 0x02: printf("Not Fixed\n"); break;
+ case 0x03: printf("Undefined\n"); break;
+ }
+ printf(" Rotation Capability: ");
+ switch ((v & 0x30) >> 4) {
+ case 0x00: printf("None\n"); break;
+ case 0x01: printf("Can rotate 90 degrees clockwise\n"); break;
+ case 0x02: printf("Can rotate 90 degrees counterclockwise\n"); break;
+ case 0x03: printf("Can rotate 90 degrees in either direction)\n"); break;
+ }
+ printf(" Zero Pixel Location: ");
+ switch ((v & 0x0c) >> 2) {
+ case 0x00: printf("Upper Left\n"); break;
+ case 0x01: printf("Upper Right\n"); break;
+ case 0x02: printf("Lower Left\n"); break;
+ case 0x03: printf("Lower Right\n"); break;
+ }
+ printf(" Scan Direction: ");
+ switch (v & 0x03) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("Fast Scan is on the Major (Long) Axis and Slow Scan is on the Minor Axis\n"); break;
+ case 0x02: printf("Fast Scan is on the Minor (Short) Axis and Slow Scan is on the Major Axis\n"); break;
+ case 0x03: printf("Reserved\n");
+ fail("Scan Direction used the reserved value 0x03.\n");
+ break;
+ }
+ printf(" Sub-pixel layout/configuration/shape: ");
+ switch (x[0x0b]) {
+ case 0x00: printf("Not defined\n"); break;
+ case 0x01: printf("RGB vertical stripes\n"); break;
+ case 0x02: printf("RGB horizontal stripes\n"); break;
+ case 0x03: printf("Vertical stripes using primary order\n"); break;
+ case 0x04: printf("Horizontal stripes using primary order\n"); break;
+ case 0x05: printf("Quad sub-pixels, red at top left\n"); break;
+ case 0x06: printf("Quad sub-pixels, red at bottom left\n"); break;
+ case 0x07: printf("Delta (triad) RGB sub-pixels\n"); break;
+ case 0x08: printf("Mosaic\n"); break;
+ case 0x09: printf("Quad sub-pixels, RGB + 1 additional color\n"); break;
+ case 0x0a: printf("Five sub-pixels, RGB + 2 additional colors\n"); break;
+ case 0x0b: printf("Six sub-pixels, RGB + 3 additional colors\n"); break;
+ case 0x0c: printf("Clairvoyante, Inc. PenTile Matrix (tm) layout\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ printf(" Horizontal and vertical dot/pixel pitch: %.2fx%.2f mm\n",
+ (double)(x[0x0c]) / 100.0, (double)(x[0x0d]) / 100.0);
+ printf(" Color bit depth: %u\n", x[0x0e] & 0x0f);
+ v = x[0x0f];
+ printf(" Response time for %s transition: %u ms\n",
+ (v & 0x80) ? "white-to-black" : "black-to-white", v & 0x7f);
+}
+
+// tag 0x0d
+
+void edid_state::parse_displayid_intf_power_sequencing(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 6, 6))
+ return;
+
+ printf(" Power Sequence T1 Range: %.1f-%u.0 ms\n", (x[3] >> 4) / 10.0, (x[3] & 0xf) * 2);
+ printf(" Power Sequence T2 Range: 0.0-%u.0 ms\n", (x[4] & 0x3f) * 2);
+ printf(" Power Sequence T3 Range: 0.0-%u.0 ms\n", (x[5] & 0x3f) * 2);
+ printf(" Power Sequence T4 Min: %u.0 ms\n", (x[6] & 0x7f) * 10);
+ printf(" Power Sequence T5 Min: %u.0 ms\n", (x[7] & 0x3f) * 10);
+ printf(" Power Sequence T6 Min: %u.0 ms\n", (x[8] & 0x3f) * 10);
+}
+
+// tag 0x0e
+
+void edid_state::parse_displayid_transfer_characteristics(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1], 0xf0, 1);
+
+ unsigned xfer_id = x[1] >> 4;
+ bool first_is_white = x[3] & 0x80;
+ bool four_param = x[3] & 0x20;
+
+ if (xfer_id) {
+ printf(" Transfer Characteristics Data Block Identifier: %u\n", xfer_id);
+ if (!(dispid.preparsed_color_ids & (1 << xfer_id)))
+ fail("Missing Color Characteristics Data Block using Identifier %u.\n", xfer_id);
+ }
+ if (first_is_white)
+ printf(" The first curve is the 'white' transfer characteristic\n");
+ if (x[3] & 0x40)
+ printf(" Individual response curves\n");
+
+ unsigned offset = 4;
+ unsigned len = x[2] - 1;
+
+ for (unsigned i = 0; len; i++) {
+ if ((x[3] & 0x80) && !i)
+ printf(" White curve: ");
+ else
+ printf(" Response curve #%u:",
+ i - first_is_white);
+ unsigned samples = x[offset];
+ if (four_param) {
+ if (samples != 5)
+ fail("Expected 5 samples.\n");
+ printf(" A0=%u A1=%u A2=%u A3=%u Gamma=%.2f\n",
+ x[offset + 1], x[offset + 2], x[offset + 3], x[offset + 4],
+ (double)(x[offset + 5] + 100.0) / 100.0);
+ samples++;
+ } else {
+ double sum = 0;
+
+ // The spec is not very clear about the number of samples:
+ // should this be interpreted as the actual number of
+ // samples stored in this Data Block, or as the number of
+ // samples in the curve, but where the last sample is not
+ // actually stored since it is always 0x3ff.
+ //
+ // The ATP Manager interprets this as the latter, so that's
+ // what we implement here.
+ for (unsigned j = offset + 1; j < offset + samples; j++) {
+ sum += x[j];
+ printf(" %.2f", sum * 100.0 / 1023.0);
+ }
+ printf(" 100.00\n");
+ }
+ offset += samples;
+ len -= samples;
+ }
+}
+
+// tag 0x0f
+
+void edid_state::parse_displayid_display_intf(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 10, 10))
+ return;
+
+ dispid.has_display_interface_features = true;
+ printf(" Interface Type: ");
+ switch (x[3] >> 4) {
+ case 0x00:
+ switch (x[3] & 0xf) {
+ case 0x00: printf("Analog 15HD/VGA\n"); break;
+ case 0x01: printf("Analog VESA NAVI-V (15HD)\n"); break;
+ case 0x02: printf("Analog VESA NAVI-D\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ break;
+ case 0x01: printf("LVDS\n"); break;
+ case 0x02: printf("TMDS\n"); break;
+ case 0x03: printf("RSDS\n"); break;
+ case 0x04: printf("DVI-D\n"); break;
+ case 0x05: printf("DVI-I, analog\n"); break;
+ case 0x06: printf("DVI-I, digital\n"); break;
+ case 0x07: printf("HDMI-A\n"); break;
+ case 0x08: printf("HDMI-B\n"); break;
+ case 0x09: printf("MDDI\n"); break;
+ case 0x0a: printf("DisplayPort\n"); break;
+ case 0x0b: printf("Proprietary Digital Interface\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ if (x[3] >> 4)
+ printf(" Number of Links: %u\n", x[3] & 0xf);
+ printf(" Interface Standard Version: %u.%u\n",
+ x[4] >> 4, x[4] & 0xf);
+ print_flags(" Supported bpc for RGB encoding", x[5], bpc444);
+ print_flags(" Supported bpc for YCbCr 4:4:4 encoding", x[6], bpc444);
+ print_flags(" Supported bpc for YCbCr 4:2:2 encoding", x[7], bpc4xx);
+ printf(" Supported Content Protection: ");
+ switch (x[8] & 0xf) {
+ case 0x00: printf("None\n"); break;
+ case 0x01: printf("HDCP "); break;
+ case 0x02: printf("DTCP "); break;
+ case 0x03: printf("DPCP "); break;
+ default: printf("Reserved "); break;
+ }
+ if (x[8] & 0xf)
+ printf("%u.%u\n", x[9] >> 4, x[9] & 0xf);
+ unsigned char v = x[0x0a] & 0xf;
+ printf(" Spread Spectrum: ");
+ switch (x[0x0a] >> 6) {
+ case 0x00: printf("None\n"); break;
+ case 0x01: printf("Down Spread %.1f%%\n", v / 10.0); break;
+ case 0x02: printf("Center Spread %.1f%%\n", v / 10.0); break;
+ case 0x03: printf("Reserved\n"); break;
+ }
+ switch (x[3] >> 4) {
+ case 0x01:
+ printf(" LVDS Color Mapping: %s mode\n",
+ (x[0x0b] & 0x10) ? "6 bit compatible" : "normal");
+ if (x[0x0b] & 0x08) printf(" LVDS supports 2.8V\n");
+ if (x[0x0b] & 0x04) printf(" LVDS supports 12V\n");
+ if (x[0x0b] & 0x02) printf(" LVDS supports 5V\n");
+ if (x[0x0b] & 0x01) printf(" LVDS supports 3.3V\n");
+ printf(" LVDS %s Mode\n", (x[0x0c] & 0x04) ? "Fixed" : "DE");
+ if (x[0x0c] & 0x04)
+ printf(" LVDS %s Signal Level\n", (x[0x0c] & 0x02) ? "Low" : "High");
+ else
+ printf(" LVDS DE Polarity Active %s\n", (x[0x0c] & 0x02) ? "Low" : "High");
+ printf(" LVDS Shift Clock Data Strobe at %s Edge\n", (x[0x0c] & 0x01) ? "Rising" : "Falling");
+ break;
+ case 0x0b:
+ printf(" PDI %s Mode\n", (x[0x0b] & 0x04) ? "Fixed" : "DE");
+ if (x[0x0b] & 0x04)
+ printf(" PDI %s Signal Level\n", (x[0x0b] & 0x02) ? "Low" : "High");
+ else
+ printf(" PDI DE Polarity Active %s\n", (x[0x0b] & 0x02) ? "Low" : "High");
+ printf(" PDI Shift Clock Data Strobe at %s Edge\n", (x[0x0b] & 0x01) ? "Rising" : "Falling");
+ break;
+ }
+}
+
+// tag 0x10 and 0x27
+
+void edid_state::parse_displayid_stereo_display_intf(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1], 0xc0, 1);
+
+ switch (x[1] >> 6) {
+ case 0x00: printf(" Timings that explicitly report 3D capability\n"); break;
+ case 0x01: printf(" Timings that explicitly report 3D capability & Timing Codes listed here\n"); break;
+ case 0x02: printf(" All listed timings\n"); break;
+ case 0x03: printf(" Only Timings Codes listed here\n"); break;
+ }
+
+ unsigned len = x[2];
+
+ switch (x[4]) {
+ case 0x00:
+ printf(" Field Sequential Stereo (L/R Polarity: %s)\n",
+ (x[5] & 1) ? "0/1" : "1/0");
+ break;
+ case 0x01:
+ printf(" Side-by-side Stereo (Left Half = %s Eye View)\n",
+ (x[5] & 1) ? "Right" : "Left");
+ break;
+ case 0x02:
+ printf(" Pixel Interleaved Stereo:\n");
+ for (unsigned y = 0; y < 8; y++) {
+ unsigned char v = x[5 + y];
+
+ printf(" ");
+ for (int x = 7; x >= 0; x--)
+ printf("%c", (v & (1 << x)) ? 'L' : 'R');
+ printf("\n");
+ }
+ break;
+ case 0x03:
+ printf(" Dual Interface, Left and Right Separate\n");
+ printf(" Carries the %s-eye view\n",
+ (x[5] & 1) ? "Right" : "Left");
+ printf(" ");
+ switch ((x[5] >> 1) & 3) {
+ case 0x00: printf("No mirroring\n"); break;
+ case 0x01: printf("Left/Right mirroring\n"); break;
+ case 0x02: printf("Top/Bottom mirroring\n"); break;
+ case 0x03: printf("Reserved\n"); break;
+ }
+ break;
+ case 0x04:
+ printf(" Multi-View: %u views, Interleaving Method Code: %u\n",
+ x[5], x[6]);
+ break;
+ case 0x05:
+ printf(" Stacked Frame Stereo (Top Half = %s Eye View)\n",
+ (x[5] & 1) ? "Right" : "Left");
+ break;
+ case 0xff:
+ printf(" Proprietary\n");
+ break;
+ default:
+ printf(" Reserved\n");
+ break;
+ }
+ if (!(x[1] & 0x40)) // Has No Timing Codes
+ return;
+ len -= 1 + x[3];
+ x += 4 + x[3];
+ while (1U + (x[0] & 0x1f) <= len) {
+ unsigned num_codes = x[0] & 0x1f;
+ unsigned type = x[0] >> 6;
+ char type_name[16];
+
+ for (unsigned i = 1; i <= num_codes; i++) {
+ switch (type) {
+ case 0x00:
+ sprintf(type_name, "DMT 0x%02x", x[i]);
+ print_timings(" ", find_dmt_id(x[i]), type_name);
+ break;
+ case 0x01:
+ sprintf(type_name, "VIC %3u", x[i]);
+ print_timings(" ", find_vic_id(x[i]), type_name);
+ break;
+ case 0x02:
+ sprintf(type_name, "HDMI VIC %u", x[i]);
+ print_timings(" ", find_hdmi_vic_id(x[i]), type_name);
+ break;
+ }
+ }
+
+ len -= 1 + num_codes;
+ x += 1 + num_codes;
+ }
+}
+
+// tag 0x11
+
+void edid_state::parse_displayid_type_5_timing(const unsigned char *x)
+{
+ struct timings t = {};
+ std::string s("aspect ");
+
+ t.hact = 1 + (x[2] | (x[3] << 8));
+ t.vact = 1 + (x[4] | (x[5] << 8));
+ calc_ratio(&t);
+ s += std::to_string(t.hratio) + ":" + std::to_string(t.vratio);
+ switch ((x[0] >> 5) & 0x3) {
+ case 0:
+ s += ", no 3D stereo";
+ break;
+ case 1:
+ s += ", 3D stereo";
+ break;
+ case 2:
+ s += ", 3D stereo depends on user action";
+ break;
+ case 3:
+ s += ", reserved";
+ fail("Reserved stereo 0x03.\n");
+ break;
+ }
+ if (x[0] & 0x10)
+ s += ", refresh rate * (1000/1001) supported";
+
+ t.rb = RB_CVT_V2;
+ if ((x[0] & 0x03) == 1)
+ warn("Unexpected use of 'custom reduced blanking'.\n");
+ else if ((x[0] & 0x03) > 1)
+ fail("Invalid Timing Formula.\n");
+
+ edid_cvt_mode(1 + x[6], t);
+
+ if (x[0] & 0x80) {
+ s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "CVT", s));
+ }
+
+ print_timings(" ", &t, "CVT", s.c_str());
+}
+
+// tag 0x12 and 0x28
+
+void edid_state::parse_displayid_tiled_display_topology(const unsigned char *x, bool is_v2)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 22, 22))
+ return;
+
+ unsigned caps = x[3];
+ unsigned num_v_tile = (x[4] & 0xf) | (x[6] & 0x30);
+ unsigned num_h_tile = (x[4] >> 4) | ((x[6] >> 2) & 0x30);
+ unsigned tile_v_location = (x[5] & 0xf) | ((x[6] & 0x3) << 4);
+ unsigned tile_h_location = (x[5] >> 4) | (((x[6] >> 2) & 0x3) << 4);
+ unsigned tile_width = x[7] | (x[8] << 8);
+ unsigned tile_height = x[9] | (x[10] << 8);
+ unsigned pix_mult = x[11];
+
+ printf(" Capabilities:\n");
+ printf(" Behavior if it is the only tile: ");
+ switch (caps & 0x07) {
+ case 0x00: printf("Undefined\n"); break;
+ case 0x01: printf("Image is displayed at the Tile Location\n"); break;
+ case 0x02: printf("Image is scaled to fit the entire tiled display\n"); break;
+ case 0x03: printf("Image is cloned to all other tiles\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ printf(" Behavior if more than one tile and fewer than total number of tiles: ");
+ switch ((caps >> 3) & 0x03) {
+ case 0x00: printf("Undefined\n"); break;
+ case 0x01: printf("Image is displayed at the Tile Location\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ if (caps & 0x80)
+ printf(" Tiled display consists of a single physical display enclosure\n");
+ else
+ printf(" Tiled display consists of multiple physical display enclosures\n");
+ printf(" Num horizontal tiles: %u Num vertical tiles: %u\n",
+ num_h_tile + 1, num_v_tile + 1);
+ printf(" Tile location: %u, %u\n", tile_h_location, tile_v_location);
+ printf(" Tile resolution: %ux%u\n", tile_width + 1, tile_height + 1);
+ if (caps & 0x40) {
+ if (pix_mult) {
+ printf(" Top bevel size: %.1f pixels\n",
+ pix_mult * x[12] / 10.0);
+ printf(" Bottom bevel size: %.1f pixels\n",
+ pix_mult * x[13] / 10.0);
+ printf(" Right bevel size: %.1f pixels\n",
+ pix_mult * x[14] / 10.0);
+ printf(" Left bevel size: %.1f pixels\n",
+ pix_mult * x[15] / 10.0);
+ } else {
+ fail("No bevel information, but the pixel multiplier is non-zero.\n");
+ }
+ printf(" Tile resolution: %ux%u\n", tile_width + 1, tile_height + 1);
+ } else if (pix_mult) {
+ fail("No bevel information, but the pixel multiplier is non-zero.\n");
+ }
+ if (is_v2)
+ printf(" Tiled Display Manufacturer/Vendor ID: %02X-%02X-%02X\n",
+ x[0x10], x[0x11], x[0x12]);
+ else
+ printf(" Tiled Display Manufacturer/Vendor ID: %c%c%c\n",
+ x[0x10], x[0x11], x[0x12]);
+ printf(" Tiled Display Product ID Code: %u\n",
+ x[0x13] | (x[0x14] << 8));
+ if (hide_serial_numbers)
+ printf(" Tiled Display Serial Number: ...\n");
+ else
+ printf(" Tiled Display Serial Number: %u\n",
+ x[0x15] | (x[0x16] << 8) | (x[0x17] << 16)| (x[0x18] << 24));
+}
+
+// tag 0x13
+
+void edid_state::parse_displayid_type_6_timing(const unsigned char *x)
+{
+ struct timings t = {};
+ std::string s("aspect ");
+
+ t.pixclk_khz = 1 + (x[0] + (x[1] << 8) + ((x[2] & 0x3f) << 16));
+ t.hact = 1 + (x[3] | ((x[4] & 0x3f) << 8));
+ if ((x[4] >> 7) & 0x1)
+ t.pos_pol_hsync = true;
+ unsigned hbl = 1 + (x[7] | ((x[9] & 0xf) << 8));
+ t.hfp = 1 + (x[8] | ((x[9] & 0xf0) << 4));
+ t.hsync = 1 + x[10];
+ t.hbp = hbl - t.hfp - t.hsync;
+ t.vact = 1 + (x[5] | ((x[6] & 0x3f) << 8));
+ if ((x[6] >> 7) & 0x1)
+ t.pos_pol_vsync = true;
+ unsigned vbl = 1 + x[11];
+ t.vfp = 1 + x[12];
+ t.vsync = 1 + (x[13] & 0x0f);
+ t.vbp = vbl - t.vfp - t.vsync;
+
+ if (x[13] & 0x80) {
+ t.interlaced = true;
+ t.vfp /= 2;
+ t.vsync /= 2;
+ t.vbp /= 2;
+ }
+ calc_ratio(&t);
+ s += std::to_string(t.hratio) + ":" + std::to_string(t.vratio);
+ if (x[2] & 0x40) {
+ double aspect_mult = x[14] * 3.0 / 256.0;
+ unsigned size_mult = 1 + (x[16] >> 4);
+
+ t.vsize_mm = size_mult * (1 + (x[15] | ((x[16] & 0xf) << 8)));
+ t.hsize_mm = t.vsize_mm * aspect_mult;
+ }
+
+ switch ((x[13] >> 5) & 0x3) {
+ case 0:
+ s += ", no 3D stereo";
+ break;
+ case 1:
+ s += ", 3D stereo";
+ break;
+ case 2:
+ s += ", 3D stereo depends on user action";
+ break;
+ case 3:
+ s += ", reserved";
+ fail("Reserved stereo 0x03.\n");
+ break;
+ }
+
+ if (x[2] & 0x80) {
+ s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "DTD", s));
+ }
+
+ print_timings(" ", &t, "DTD", s.c_str(), true);
+}
+
+static std::string ieee7542d(unsigned short fp)
+{
+ int exp = ((fp & 0x7c00) >> 10) - 15;
+ unsigned fract = (fp & 0x3ff) | 0x400;
+
+ if (fp == 0x8000)
+ return "do not use";
+ if (fp & 0x8000)
+ return "reserved";
+ return std::to_string(pow(2, exp) * fract / 1024.0) + " cd/m^2";
+}
+
+// tag 0x21
+
+void edid_state::parse_displayid_parameters_v2(const unsigned char *x,
+ unsigned block_rev)
+{
+ if (!check_displayid_datablock_length(x, 29, 29))
+ return;
+
+ unsigned hor_size = (x[4] << 8) + x[3];
+ unsigned vert_size = (x[6] << 8) + x[5];
+
+ dispid.has_display_parameters = true;
+ if (x[1] & 0x80)
+ printf(" Image size: %u mm x %u mm\n",
+ hor_size, vert_size);
+ else
+ printf(" Image size: %.1f mm x %.1f mm\n",
+ hor_size / 10.0, vert_size / 10.0);
+ unsigned w = (x[8] << 8) + x[7];
+ unsigned h = (x[10] << 8) + x[9];
+ if (w && h) {
+ printf(" Native Format: %ux%u\n", w, h);
+ dispid.native_width = w;
+ dispid.native_height = h;
+ } else if (w || h) {
+ fail("Invalid Native Format %ux%u.\n", w, h);
+ }
+ unsigned char v = x[11];
+ printf(" Scan Orientation: ");
+ switch (v & 0x07) {
+ case 0x00: printf("Left to Right, Top to Bottom\n"); break;
+ case 0x01: printf("Right to Left, Top to Bottom\n"); break;
+ case 0x02: printf("Top to Bottom, Right to Left\n"); break;
+ case 0x03: printf("Bottom to Top, Right to Left\n"); break;
+ case 0x04: printf("Right to Left, Bottom to Top\n"); break;
+ case 0x05: printf("Left to Right, Bottom to Top\n"); break;
+ case 0x06: printf("Bottom to Top, Left to Right\n"); break;
+ case 0x07: printf("Top to Bottom, Left to Right\n"); break;
+ }
+ printf(" Luminance Information: ");
+ switch ((v >> 3) & 0x03) {
+ case 0x00: printf("Minimum guaranteed value\n"); break;
+ case 0x01: printf("Guidance for the Source device\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ printf(" Color Information: CIE %u\n",
+ (v & 0x40) ? 1976 : 1931);
+ printf(" Audio Speaker Information: %sintegrated\n",
+ (v & 0x80) ? "not " : "");
+ printf(" Native Color Chromaticity:\n");
+ printf(" Primary #1: (%.6f, %.6f)\n",
+ fp2d(x[0x0c] | ((x[0x0d] & 0x0f) << 8)),
+ fp2d(((x[0x0d] & 0xf0) >> 4) | (x[0x0e] << 4)));
+ printf(" Primary #2: (%.6f, %.6f)\n",
+ fp2d(x[0x0f] | ((x[0x10] & 0x0f) << 8)),
+ fp2d(((x[0x10] & 0xf0) >> 4) | (x[0x11] << 4)));
+ printf(" Primary #3: (%.6f, %.6f)\n",
+ fp2d(x[0x12] | ((x[0x13] & 0x0f) << 8)),
+ fp2d(((x[0x13] & 0xf0) >> 4) | (x[0x14] << 4)));
+ printf(" White Point: (%.6f, %.6f)\n",
+ fp2d(x[0x15] | ((x[0x16] & 0x0f) << 8)),
+ fp2d(((x[0x16] & 0xf0) >> 4) | (x[0x17] << 4)));
+ printf(" Native Maximum Luminance (Full Coverage): %s\n",
+ ieee7542d(x[0x18] | (x[0x19] << 8)).c_str());
+ printf(" Native Maximum Luminance (10%% Rectangular Coverage): %s\n",
+ ieee7542d(x[0x1a] | (x[0x1b] << 8)).c_str());
+ printf(" Native Minimum Luminance: %s\n",
+ ieee7542d(x[0x1c] | (x[0x1d] << 8)).c_str());
+ printf(" Native Color Depth: ");
+ if (!(x[0x1e] & 0x07))
+ printf("Not defined\n");
+ else if (bpc444[x[0x1e] & 0x07])
+ printf("%s bpc\n", bpc444[x[0x1e] & 0x07]);
+ else
+ printf("Reserved\n");
+ printf(" Display Device Technology: ");
+ switch ((x[0x1e] >> 4) & 0x07) {
+ case 0x00: printf("Not Specified\n"); break;
+ case 0x01: printf("Active Matrix LCD\n"); break;
+ case 0x02: printf("Organic LED\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ if (block_rev)
+ printf(" Display Device Theme Preference: %s\n",
+ (x[0x1e] & 0x80) ? "Dark Theme Preferred" : "No Preference");
+ if (x[0x1f] != 0xff)
+ printf(" Native Gamma EOTF: %.2f\n",
+ (100 + x[0x1f]) / 100.0);
+}
+
+// tag 0x24
+
+void edid_state::parse_displayid_type_9_timing(const unsigned char *x)
+{
+ struct timings t = {};
+ std::string s("aspect ");
+
+ t.hact = 1 + (x[1] | (x[2] << 8));
+ t.vact = 1 + (x[3] | (x[4] << 8));
+ calc_ratio(&t);
+ s += std::to_string(t.hratio) + ":" + std::to_string(t.vratio);
+ switch ((x[0] >> 5) & 0x3) {
+ case 0:
+ s += ", no 3D stereo";
+ break;
+ case 1:
+ s += ", 3D stereo";
+ break;
+ case 2:
+ s += ", 3D stereo depends on user action";
+ break;
+ case 3:
+ s += ", reserved";
+ fail("Reserved stereo 0x03.\n");
+ break;
+ }
+ if (x[0] & 0x10)
+ s += ", refresh rate * (1000/1001) supported";
+
+ switch (x[0] & 0x07) {
+ case 1: t.rb = RB_CVT_V1; break;
+ case 2: t.rb = RB_CVT_V2; break;
+ default: break;
+ }
+
+ edid_cvt_mode(1 + x[5], t);
+
+ print_timings(" ", &t, "CVT", s.c_str());
+}
+
+// tag 0x25
+
+void edid_state::parse_displayid_dynamic_video_timings_range_limits(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1], 0, (x[1] & 7) == 1);
+
+ if (!check_displayid_datablock_length(x, 9, 9))
+ return;
+
+ printf(" Minimum Pixel Clock: %u kHz\n",
+ 1 + (x[3] | (x[4] << 8) | (x[5] << 16)));
+ printf(" Maximum Pixel Clock: %u kHz\n",
+ 1 + (x[6] | (x[7] << 8) | (x[8] << 16)));
+ printf(" Minimum Vertical Refresh Rate: %u Hz\n", x[9]);
+ if (x[1] & 7)
+ printf(" Maximum Vertical Refresh Rate: %u Hz\n", x[10] + ((x[11] & 3) << 8));
+ else
+ printf(" Maximum Vertical Refresh Rate: %u Hz\n", x[10]);
+ printf(" Seamless Dynamic Video Timing Support: %s\n",
+ (x[11] & 0x80) ? "Yes" : "No");
+}
+
+// tag 0x26
+
+static const char *colorspace_eotf_combinations[] = {
+ "sRGB",
+ "BT.601",
+ "BT.709/BT.1886",
+ "Adobe RGB",
+ "DCI-P3",
+ "BT.2020",
+ "BT.2020/SMPTE ST 2084"
+};
+
+static const char *colorspace_eotf_reserved[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+static const char *colorspaces[] = {
+ "Undefined",
+ "sRGB",
+ "BT.601",
+ "BT.709",
+ "Adobe RGB",
+ "DCI-P3",
+ "BT.2020",
+ "Custom"
+};
+
+static const char *eotfs[] = {
+ "Undefined",
+ "sRGB",
+ "BT.601",
+ "BT.1886",
+ "Adobe RGB",
+ "DCI-P3",
+ "BT.2020",
+ "Gamma function",
+ "SMPTE ST 2084",
+ "Hybrid Log",
+ "Custom"
+};
+
+void edid_state::parse_displayid_interface_features(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 9))
+ return;
+
+ dispid.has_display_interface_features = true;
+ unsigned len = x[2];
+ if (len > 0) print_flags(" Supported bpc for RGB encoding", x[3], bpc444);
+ if (len > 1) print_flags(" Supported bpc for YCbCr 4:4:4 encoding", x[4], bpc444);
+ if (len > 2) print_flags(" Supported bpc for YCbCr 4:2:2 encoding", x[5], bpc4xx);
+ if (len > 3) print_flags(" Supported bpc for YCbCr 4:2:0 encoding", x[6], bpc4xx);
+ if (len > 4 && x[7])
+ printf(" Minimum pixel rate at which YCbCr 4:2:0 encoding is supported: %.3f MHz\n",
+ 74.25 * x[7]);
+ if (len > 5) print_flags(" Supported audio capability and features (kHz)",
+ x[8], audiorates, true);
+ if (len > 6) print_flags(" Supported color space and EOTF standard combination 1",
+ x[9], colorspace_eotf_combinations);
+ if (len > 7) print_flags(" Supported color space and EOTF standard combination 2",x[10], colorspace_eotf_reserved);
+
+ unsigned i = 0;
+
+ if (len > 8 && x[11]) {
+ printf(" Supported color space and EOTF additional combinations:");
+ for (i = 0; i < x[11]; i++) {
+ if (i > 6) {
+ printf("\n Number of additional color space and EOTF combinations (%d) is greater than allowed (7).", x[11]);
+ break;
+ } else if (i + 10 > len) {
+ printf("\n Number of additional color space and EOTF combinations (%d) is too many to fit in block (%d).", x[11], len - 9);
+ break;
+ }
+
+ const char *colorspace = "Out of range";
+ const char *eotf = "Out of range";
+ unsigned colorspace_index = (x[12 + i] >> 4) & 0xf;
+ unsigned eotf_index = x[12 + i] & 0xf;
+
+ if (colorspace_index < sizeof(colorspaces) / sizeof(colorspaces[0]))
+ colorspace = colorspaces[colorspace_index];
+ if (eotf_index < sizeof(eotfs) / sizeof(eotfs[0]))
+ eotf = eotfs[eotf_index];
+
+ if (i > 0)
+ printf(", ");
+ if (!strcmp(colorspace, eotf))
+ printf("%s", colorspace);
+ else
+ printf("%s/%s", colorspace, eotf);
+ }
+ printf("\n");
+ }
+ check_displayid_datablock_length(x, 9 + i, 9 + i, 9 + i);
+}
+
+// tag 0x29
+
+void edid_state::parse_displayid_ContainerID(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (check_displayid_datablock_length(x, 16, 16)) {
+ x += 3;
+ printf(" Container ID: %s\n", containerid2s(x).c_str());
+ }
+}
+
+// tag 0x32
+
+void edid_state::parse_displayid_type_10_timing(const unsigned char *x, bool is_cta)
+{
+ struct timings t = {};
+ std::string s("aspect ");
+
+ t.hact = 1 + (x[1] | (x[2] << 8));
+ t.vact = 1 + (x[3] | (x[4] << 8));
+ calc_ratio(&t);
+ s += std::to_string(t.hratio) + ":" + std::to_string(t.vratio);
+
+ switch ((x[0] >> 5) & 0x3) {
+ case 0:
+ s += ", no 3D stereo";
+ break;
+ case 1:
+ s += ", 3D stereo";
+ break;
+ case 2:
+ s += ", 3D stereo depends on user action";
+ break;
+ case 3:
+ s += ", reserved";
+ fail("Reserved stereo 0x03.\n");
+ break;
+ }
+
+ switch (x[0] & 0x07) {
+ case 1: t.rb = RB_CVT_V1; break;
+ case 2: t.rb = RB_CVT_V2; break;
+ case 3: t.rb = RB_CVT_V3; break;
+ default: break;
+ }
+
+ if (x[0] & 0x10) {
+ if (t.rb == RB_CVT_V2) {
+ s += ", refresh rate * (1000/1001) supported";
+ t.rb |= RB_ALT;
+ } else if (t.rb == RB_CVT_V3) {
+ s += ", hblank is 160 pixels";
+ t.rb |= RB_ALT;
+ } else {
+ fail("VR_HB must be 0.\n");
+ }
+ }
+ if (x[0] & 0x80)
+ s += ", YCbCr 4:2:0";
+
+ edid_cvt_mode(1 + x[5], t);
+
+ print_timings(" ", &t, "CVT", s.c_str());
+ if (is_cta) {
+ timings_ext te(t, "CVT", s);
+ cta.vec_vtdbs.push_back(te);
+ }
+}
+
+// tag 0x7e, OUI 3A-02-92 (VESA)
+
+void edid_state::parse_displayid_vesa(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ if (!check_displayid_datablock_length(x, 5, 7))
+ return;
+
+ unsigned len = x[2];
+ x += 6;
+ printf(" Data Structure Type: ");
+ switch (x[0] & 0x07) {
+ case 0x00: printf("eDP\n"); break;
+ case 0x01: printf("DP\n"); break;
+ default: printf("Reserved\n"); break;
+ }
+ printf(" Default Colorspace and EOTF Handling: %s\n",
+ (x[0] & 0x80) ? "Native as specified in the Display Parameters DB" : "sRGB");
+ printf(" Number of Pixels in Hor Pix Cnt Overlapping an Adjacent Panel: %u\n",
+ x[1] & 0xf);
+ printf(" Multi-SST Operation: ");
+ switch ((x[1] >> 5) & 0x03) {
+ case 0x00: printf("Not Supported\n"); break;
+ case 0x01: printf("Two Streams (number of links shall be 2 or 4)\n"); break;
+ case 0x02: printf("Four Streams (number of links shall be 4)\n"); break;
+ case 0x03: printf("Reserved\n"); break;
+ }
+ if (len >= 7) {
+ double bpp = (x[2] & 0x3f) + (x[3] & 0x0f) / 16.0;
+ printf(" Pass through timing's target DSC bits per pixel: %.4f\n", bpp);
+ }
+}
+
+// tag 0x81
+
+void edid_state::parse_displayid_cta_data_block(const unsigned char *x)
+{
+ check_displayid_datablock_revision(x[1]);
+
+ unsigned len = x[2];
+ unsigned i;
+
+ if (len > 248) {
+ fail("Length is > 248.\n");
+ len = 248;
+ }
+ x += 3;
+
+ for (i = 0; i < len; i += (x[i] & 0x1f) + 1) {
+ unsigned tag = (x[i] & 0xe0) << 3;
+
+ if (tag == 0x700)
+ tag |= x[i + 1];
+ bool duplicate = dispid.found_tags.find(tag) != dispid.found_tags.end();
+
+ cta_block(x + i, duplicate);
+ if (!duplicate)
+ dispid.found_tags.insert(tag);
+ }
+
+ if (i != len)
+ fail("Length is %u instead of %u.\n", len, i);
+}
+
+// DisplayID main
+
+std::string edid_state::product_type(unsigned char x, bool heading)
+{
+ std::string headingstr;
+
+ if (dispid.version < 0x20) {
+ headingstr = "Display Product Type";
+ if (heading) return headingstr;
+ dispid.is_display = x == 2 || x == 3 || x == 4 || x == 6;
+ switch (x) {
+ case 0: return "Extension Section";
+ case 1: return "Test Structure; test equipment only";
+ case 2: return "Display panel or other transducer, LCD or PDP module, etc.";
+ case 3: return "Standalone display device";
+ case 4: return "Television receiver";
+ case 5: return "Repeater/translator";
+ case 6: return "DIRECT DRIVE monitor";
+ default: break;
+ }
+ } else {
+ headingstr = "Display Product Primary Use Case";
+ if (heading) return headingstr;
+ dispid.is_display = x >= 2 && x <= 8;
+ switch (x) {
+ case 0: return "Same primary use case as the base section";
+ case 1: return "Test Structure; test equipment only";
+ case 2: return "None of the listed primary use cases; generic display";
+ case 3: return "Television (TV) display";
+ case 4: return "Desktop productivity display";
+ case 5: return "Desktop gaming display";
+ case 6: return "Presentation display";
+ case 7: return "Head-mounted Virtual Reality (VR) display";
+ case 8: return "Head-mounted Augmented Reality (AR) display";
+ default: break;
+ }
+ }
+ fail("Unknown %s 0x%02x.\n", headingstr.c_str(), x);
+ return std::string("Unknown " + headingstr + " (") + utohex(x) + ")";
+}
+
+void edid_state::preparse_displayid_block(const unsigned char *x)
+{
+ unsigned length = x[2];
+
+ if (length > 121)
+ length = 121;
+
+ unsigned offset = 5;
+
+ dispid.preparsed_displayid_blocks++;
+ while (length > 0) {
+ unsigned tag = x[offset];
+ unsigned len = x[offset + 2];
+
+ switch (tag) {
+ case 0x02:
+ dispid.preparsed_color_ids |= 1 << ((x[offset + 1] >> 3) & 0x0f);
+ break;
+ case 0x0e:
+ dispid.preparsed_xfer_ids |= 1 << ((x[offset + 1] >> 4) & 0x0f);
+ break;
+ default:
+ break;
+ }
+
+ if (length < 3)
+ break;
+
+ if (length < len + 3)
+ break;
+
+ if (!tag && !len)
+ break;
+
+ length -= len + 3;
+ offset += len + 3;
+ }
+}
+
+void edid_state::parse_displayid_block(const unsigned char *x)
+{
+ unsigned version = x[1];
+ unsigned length = x[2];
+ unsigned prod_type = x[3]; // future check: based on type, check for required data blocks
+ unsigned ext_count = x[4];
+ unsigned i;
+
+ printf(" Version: %u.%u\n Extension Count: %u\n",
+ version >> 4, version & 0xf, ext_count);
+
+ if (dispid.is_base_block) {
+ dispid.version = version;
+ printf(" %s: %s\n", product_type(prod_type, true).c_str(),
+ product_type(prod_type, false).c_str());
+ if (!prod_type)
+ fail("DisplayID Base Block has no product type.\n");
+ if (ext_count != dispid.preparsed_displayid_blocks - 1)
+ fail("Expected %u DisplayID Extension Block%s, but got %u.\n",
+ ext_count,
+ ext_count > 1 ? "s" : "",
+ dispid.preparsed_displayid_blocks - 1);
+ } else {
+ if (prod_type)
+ fail("Product Type should be 0 in extension block.\n");
+ if (ext_count)
+ fail("Extension Count should be 0 in extension block.\n");
+ if (version != dispid.version)
+ fail("Got version %u.%u, expected %u.%u.\n",
+ version >> 4, version & 0xf,
+ dispid.version >> 4, dispid.version & 0xf);
+ }
+
+ if (length > 121) {
+ fail("DisplayID length %d is greater than 121.\n", length);
+ length = 121;
+ }
+
+ unsigned offset = 5;
+ bool first_data_block = true;
+ while (length > 0) {
+ unsigned tag = x[offset];
+ unsigned oui = 0;
+
+ switch (tag) {
+ // DisplayID 1.3:
+ case 0x00: data_block = "Product Identification Data Block (" + utohex(tag) + ")"; break;
+ case 0x01: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
+ case 0x02: data_block = "Color Characteristics Data Block"; break;
+ case 0x03: data_block = "Video Timing Modes Type 1 - Detailed Timings Data Block"; break;
+ case 0x04: data_block = "Video Timing Modes Type 2 - Detailed Timings Data Block"; break;
+ case 0x05: data_block = "Video Timing Modes Type 3 - Short Timings Data Block"; break;
+ case 0x06: data_block = "Video Timing Modes Type 4 - DMT Timings Data Block"; break;
+ case 0x07: data_block = "Supported Timing Modes Type 1 - VESA DMT Timings Data Block"; break;
+ case 0x08: data_block = "Supported Timing Modes Type 2 - CTA-861 Timings Data Block"; break;
+ case 0x09: data_block = "Video Timing Range Data Block"; break;
+ case 0x0a: data_block = "Product Serial Number Data Block"; break;
+ case 0x0b: data_block = "GP ASCII String Data Block"; break;
+ case 0x0c: data_block = "Display Device Data Data Block"; break;
+ case 0x0d: data_block = "Interface Power Sequencing Data Block"; break;
+ case 0x0e: data_block = "Transfer Characteristics Data Block"; break;
+ case 0x0f: data_block = "Display Interface Data Block"; break;
+ case 0x10: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break;
+ case 0x11: data_block = "Video Timing Modes Type 5 - Short Timings Data Block"; break;
+ case 0x12: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
+ case 0x13: data_block = "Video Timing Modes Type 6 - Detailed Timings Data Block"; break;
+ // 0x14 .. 0x7e RESERVED for Additional VESA-defined Data Blocks
+ // DisplayID 2.0
+ case 0x20: data_block = "Product Identification Data Block (" + utohex(tag) + ")"; break;
+ case 0x21: data_block = "Display Parameters Data Block (" + utohex(tag) + ")"; break;
+ case 0x22: data_block = "Video Timing Modes Type 7 - Detailed Timings Data Block"; break;
+ case 0x23: data_block = "Video Timing Modes Type 8 - Enumerated Timing Codes Data Block"; break;
+ case 0x24: data_block = "Video Timing Modes Type 9 - Formula-based Timings Data Block"; break;
+ case 0x25: data_block = "Dynamic Video Timing Range Limits Data Block"; break;
+ case 0x26: data_block = "Display Interface Features Data Block"; break;
+ case 0x27: data_block = "Stereo Display Interface Data Block (" + utohex(tag) + ")"; break;
+ case 0x28: data_block = "Tiled Display Topology Data Block (" + utohex(tag) + ")"; break;
+ case 0x29: data_block = "ContainerID Data Block"; break;
+ case 0x32: data_block = "Video Timing Modes Type 10 - Formula-based Timings Data Block"; break;
+ // 0x2a .. 0x7d RESERVED for Additional VESA-defined Data Blocks
+ case 0x7e: // DisplayID 2.0
+ case 0x7f: // DisplayID 1.3
+ if ((tag == 0x7e && version >= 0x20) ||
+ (tag == 0x7f && version < 0x20)) {
+ oui = (x[offset + 3] << 16) + (x[offset + 4] << 8) + x[offset + 5];
+ const char *name = oui_name(oui);
+ bool reversed = false;
+
+ if (!name) {
+ name = oui_name(oui, true);
+ if (name)
+ reversed = true;
+ }
+ if (name)
+ data_block = std::string("Vendor-Specific Data Block (") + name + ")";
+ else
+ data_block = "Vendor-Specific Data Block, OUI " + ouitohex(oui);
+ if (reversed)
+ fail((std::string("OUI ") + ouitohex(oui) + " is in the wrong byte order.\n").c_str());
+ } else {
+ data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")";
+ }
+ break;
+ // 0x80 RESERVED
+ case 0x81: data_block = "CTA-861 DisplayID Data Block (" + utohex(tag) + ")"; break;
+ // 0x82 .. 0xff RESERVED
+ default: data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")"; break;
+ }
+
+ if (version >= 0x20 && (tag < 0x20 || tag == 0x7f))
+ fail("Use of DisplayID v1.x tag for DisplayID v%u.%u.\n",
+ version >> 4, version & 0xf);
+ if (version < 0x20 && tag >= 0x20 && tag <= 0x7e)
+ fail("Use of DisplayID v2.0 tag for DisplayID v%u.%u.\n",
+ version >> 4, version & 0xf);
+
+ if (length < 3) {
+ // report a problem when the remaining bytes are not 0.
+ if (tag || x[offset + 1]) {
+ fail("Not enough bytes remain (%d) for a DisplayID data block or the DisplayID filler is non-0.\n", length);
+ }
+ break;
+ }
+
+ unsigned block_rev = x[offset + 1] & 0x07;
+ unsigned len = x[offset + 2];
+
+ if (length < len + 3) {
+ fail("The length of this DisplayID data block (%d) exceeds the number of bytes remaining (%d).\n", len + 3, length);
+ break;
+ }
+
+ if (!tag && !len) {
+ // A Product Identification Data Block with no payload bytes is not valid - assume this is the end.
+ if (!memchk(x + offset, length)) {
+ fail("Non-0 filler bytes in the DisplayID block.\n");
+ }
+ break;
+ }
+
+ printf(" %s:\n", data_block.c_str());
+
+ switch (tag) {
+ case 0x00: parse_displayid_product_id(x + offset); break;
+ case 0x01: parse_displayid_parameters(x + offset); break;
+ case 0x02: parse_displayid_color_characteristics(x + offset); break;
+ case 0x03:
+ check_displayid_datablock_revision(x[offset + 1], 0, block_rev & 1);
+ for (i = 0; i < len / 20; i++)
+ parse_displayid_type_1_7_timing(&x[offset + 3 + (i * 20)], false, block_rev);
+ break;
+ case 0x04:
+ check_displayid_datablock_revision(x[offset + 1]);
+ for (i = 0; i < len / 11; i++)
+ parse_displayid_type_2_timing(&x[offset + 3 + (i * 11)]);
+ break;
+ case 0x05:
+ check_displayid_datablock_revision(x[offset + 1], 0, block_rev & 1);
+ for (i = 0; i < len / 3; i++)
+ parse_displayid_type_3_timing(&x[offset + 3 + (i * 3)]);
+ break;
+ case 0x06:
+ check_displayid_datablock_revision(x[offset + 1], 0xc0, 1);
+ for (i = 0; i < len; i++)
+ parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6, x[offset + 3 + i]);
+ break;
+ case 0x07:
+ check_displayid_datablock_revision(x[offset + 1]);
+ for (i = 0; i < min(len, 10) * 8; i++)
+ if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
+ char type[16];
+ sprintf(type, "DMT 0x%02x", i + 1);
+ print_timings(" ", find_dmt_id(i + 1), type);
+ }
+ break;
+ case 0x08:
+ check_displayid_datablock_revision(x[offset + 1]);
+ for (i = 0; i < min(len, 8) * 8; i++)
+ if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
+ char type[16];
+ sprintf(type, "VIC %3u", i + 1);
+ print_timings(" ", find_vic_id(i + 1), type);
+ }
+ break;
+ case 0x09: parse_displayid_video_timing_range_limits(x + offset); break;
+ case 0x0a:
+ case 0x0b: parse_displayid_string(x + offset); break;
+ case 0x0c: parse_displayid_display_device(x + offset); break;
+ case 0x0d: parse_displayid_intf_power_sequencing(x + offset); break;
+ case 0x0e: parse_displayid_transfer_characteristics(x + offset); break;
+ case 0x0f: parse_displayid_display_intf(x + offset); break;
+ case 0x10: parse_displayid_stereo_display_intf(x + offset); break;
+ case 0x11:
+ check_displayid_datablock_revision(x[offset + 1]);
+ for (i = 0; i < len / 7; i++)
+ parse_displayid_type_5_timing(&x[offset + 3 + (i * 7)]);
+ break;
+ case 0x12: parse_displayid_tiled_display_topology(x + offset, false); break;
+ case 0x13:
+ check_displayid_datablock_revision(x[offset + 1]);
+ for (i = 0; i < len; i += (x[offset + 3 + i + 2] & 0x40) ? 17 : 14)
+ parse_displayid_type_6_timing(&x[offset + 3 + i]);
+ break;
+ case 0x20: parse_displayid_product_id(x + offset); break;
+ case 0x21:
+ if (block_rev >= 1)
+ check_displayid_datablock_revision(x[offset + 1], 0x80, 1);
+ else
+ check_displayid_datablock_revision(x[offset + 1], 0x80, 0);
+ parse_displayid_parameters_v2(x + offset, block_rev);
+ break;
+ case 0x22: {
+ unsigned sz = 20;
+
+ if (block_rev >= 2)
+ check_displayid_datablock_revision(x[offset + 1], 0x08, 2);
+ else if (block_rev == 1)
+ check_displayid_datablock_revision(x[offset + 1], 0x08, 1);
+ else
+ check_displayid_datablock_revision(x[offset + 1]);
+ sz += (x[offset + 1] & 0x70) >> 4;
+ if (block_rev >= 1 && (x[offset + 1] & 0x08))
+ printf(" These timings support DSC pass-through\n");
+ for (i = 0; i < len / sz; i++)
+ parse_displayid_type_1_7_timing(&x[offset + 3 + i * sz], true, block_rev);
+ break;
+ }
+ case 0x23:
+ if (block_rev)
+ check_displayid_datablock_revision(x[offset + 1], 0xe8, 1);
+ else
+ check_displayid_datablock_revision(x[offset + 1], 0xc8);
+ if (x[offset + 1] & 0x08) {
+ for (i = 0; i < len / 2; i++)
+ parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6,
+ x[offset + 3 + i * 2] |
+ (x[offset + 4 + i * 2] << 8));
+ } else {
+ for (i = 0; i < len; i++)
+ parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6,
+ x[offset + 3 + i]);
+ }
+ break;
+ case 0x24:
+ check_displayid_datablock_revision(x[offset + 1]);
+ for (i = 0; i < len / 6; i++)
+ parse_displayid_type_9_timing(&x[offset + 3 + i * 6]);
+ break;
+ case 0x25: parse_displayid_dynamic_video_timings_range_limits(x + offset); break;
+ case 0x26: parse_displayid_interface_features(x + offset); break;
+ case 0x27: parse_displayid_stereo_display_intf(x + offset); break;
+ case 0x28: parse_displayid_tiled_display_topology(x + offset, true); break;
+ case 0x29: parse_displayid_ContainerID(x + offset); break;
+ case 0x32: {
+ unsigned sz = 6 + ((x[offset + 1] & 0x70) >> 4);
+
+ check_displayid_datablock_revision(x[offset + 1], 0x10);
+ for (i = 0; i < len / sz; i++)
+ parse_displayid_type_10_timing(&x[offset + 3 + i * sz]);
+ break;
+ }
+ case 0x81: parse_displayid_cta_data_block(x + offset); break;
+ case 0x7e:
+ if (oui == 0x3a0292) {
+ parse_displayid_vesa(x + offset);
+ break;
+ }
+ // fall-through
+ default: hex_block(" ", x + offset + 3, len); break;
+ }
+
+ if ((tag == 0x00 || tag == 0x20) &&
+ (!dispid.is_base_block || !first_data_block))
+ fail("%s is required to be the first DisplayID Data Block.\n",
+ data_block.c_str());
+ length -= len + 3;
+ offset += len + 3;
+ first_data_block = false;
+ }
+
+ /*
+ * DisplayID length field is number of following bytes
+ * but checksum is calculated over the entire structure
+ * (excluding DisplayID-in-EDID magic byte)
+ */
+ data_block.clear();
+ do_checksum(" ", x + 1, x[2] + 5);
+
+ if (!memchk(x + 1 + x[2] + 5, 0x7f - (1 + x[2] + 5))) {
+ data_block = "Padding";
+ fail("DisplayID padding contains non-zero bytes.\n");
+ }
+ dispid.is_base_block = false;
+}
+
+void edid_state::check_displayid_blocks()
+{
+ data_block = "DisplayID";
+ if (!dispid.has_product_identification)
+ fail("Missing DisplayID Product Identification Data Block.\n");
+ if (dispid.is_display && !dispid.has_display_parameters)
+ fail("Missing DisplayID Display Parameters Data Block.\n");
+ if (dispid.is_display && !dispid.has_display_interface_features)
+ fail("Missing DisplayID Display Interface Features Data Block.\n");
+ if (dispid.is_display && !dispid.has_type_1_7)
+ fail("Missing DisplayID Type %s Detailed Timing Data Block.\n",
+ dispid.version >= 0x20 ? "VII" : "I");
+ if (dispid.preferred_timings.empty())
+ fail("DisplayID expects at least one preferred timing.\n");
+}
diff --git a/parse-ls-ext-block.cpp b/parse-ls-ext-block.cpp
new file mode 100644
index 0000000..1643f7d
--- /dev/null
+++ b/parse-ls-ext-block.cpp
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2019-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include "edid-decode.h"
+
+static void parse_string(const char *name, const unsigned char *x)
+{
+ if (!*x)
+ return;
+ printf(" %s: ", name);
+ hex_block("", x + 1, *x, true, *x);
+}
+
+void edid_state::parse_string_table(const unsigned char *x)
+{
+ printf(" UTF Type: ");
+ switch (x[0] & 7) {
+ case 0: printf("UTF 8\n"); break;
+ case 1: printf("UTF 16BE\n"); break;
+ case 2: printf("UTF 32BE\n"); break;
+ default:
+ printf("Unknown (0x%02x)\n", x[0] & 7);
+ fail("Unknown UTF Type (0x%02x).\n", x[0] & 7);
+ break;
+ }
+ printf(" Country Code ID (ISO 3166-3): %u\n", ((x[1] & 0x3f) << 8) | x[2]);
+
+ if (x[3] || x[4]) {
+ char name[4];
+
+ name[0] = ((x[3] & 0x7c) >> 2) + '@';
+ name[1] = ((x[3] & 0x03) << 3) + ((x[4] & 0xe0) >> 5) + '@';
+ name[2] = (x[4] & 0x1f) + '@';
+ name[3] = 0;
+ if (name[0] == '@') name[0] = ' ';
+ if (name[1] == '@') name[1] = ' ';
+ if (name[2] == '@') name[2] = ' ';
+ printf(" Language ID: '%s'\n", name);
+ }
+ x += 5;
+ parse_string("Manufacturer Name", x);
+ x += x[0] + 1;
+ parse_string("Model Name", x);
+ x += x[0] + 1;
+ if (hide_serial_numbers)
+ printf(" Serial Number: ...\n");
+ else
+ parse_string("Serial Number", x);
+}
+
+void edid_state::parse_ls_ext_block(const unsigned char *x)
+{
+ const unsigned char *orig = x;
+
+ printf(" Version: %u.%u\n Unicode Version: %u.%u.%u\n",
+ x[1], x[2], (x[3] >> 4), x[3] & 0x0f, x[4]);
+ x += 5;
+
+ while (x[0] && x + x[0] < orig + 127) {
+ parse_string_table(x + 1);
+ x += x[0];
+ }
+ if (!memchk(x, orig + 127 - x)) {
+ data_block.clear();
+ fail("Non-zero values in unused space.\n");
+ }
+}
diff --git a/parse-vtb-ext-block.cpp b/parse-vtb-ext-block.cpp
new file mode 100644
index 0000000..05d54f4
--- /dev/null
+++ b/parse-vtb-ext-block.cpp
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2019-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * Author: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+ */
+
+#include "edid-decode.h"
+
+void edid_state::parse_vtb_ext_block(const unsigned char *x)
+{
+ printf(" Version: %u\n", x[1]);
+ if (x[1] != 1)
+ fail("Invalid version %u.\n", x[1]);
+
+ unsigned num_dtd = x[2];
+ unsigned num_cvt = x[3];
+ unsigned num_st = x[4];
+
+ x += 5;
+ if (num_dtd) {
+ printf(" Detailed Timing Descriptors:\n");
+ for (unsigned i = 0; i < num_dtd; i++, x += 18)
+ detailed_timings(" ", x, false);
+ }
+ if (num_cvt) {
+ printf(" Coordinated Video Timings:\n");
+ for (unsigned i = 0; i < num_cvt; i++, x += 3)
+ detailed_cvt_descriptor(" ", x, false);
+ }
+ if (num_st) {
+ // Note: the VTB-EXT standard has a mistake in the example EDID
+ // that it provides: there the refresh rate (bits 5-0 of the
+ // second byte) is set to 60 for 60 Hz, but this should be 0
+ // since the actual refresh rate is the value + 60.
+ //
+ // The documentation itself is correct, though.
+ printf(" Standard Timings:\n");
+ for (unsigned i = 0; i < num_st; i++, x += 2)
+ print_standard_timing(" ", x[0], x[1], true);
+ }
+}
diff --git a/test/256-blocks.test b/test/256-blocks.test
new file mode 100644
index 0000000..110b34c
--- /dev/null
+++ b/test/256-blocks.test
@@ -0,0 +1,2305 @@
+edid-decode (hex):
+
+00 ff ff ff ff ff ff 00 31 d8 34 12 00 00 00 00
+22 1a 01 04 a2 60 36 78 1f ee 91 a3 54 4c 99 26
+0f 50 54 ff ff 80 31 59 45 59 81 80 81 40 90 40
+95 00 a9 40 b3 00 08 e8 00 30 f2 70 5a 80 b0 58
+8a 00 c0 1c 32 00 00 1e 00 00 00 fd 00 18 58 0f
+87 3c 04 12 01 e0 f8 38 f0 3c 00 00 00 fc 00 30
+31 32 33 34 35 36 37 38 39 30 31 32 00 00 00 f7
+00 0a ff ff ff ff ff f0 00 00 00 00 00 00 ff d7
+
+02 03 24 f0 51 61 60 5f 5e 5d 10 1f 04 13 22 21
+20 05 14 02 11 01 23 09 07 07 e2 00 ea e6 0d 61
+81 82 6a 84 4d d0 00 a0 f0 70 3e 80 30 20 35 00
+c0 1c 32 00 00 1e 1a 36 80 a0 70 38 1f 40 30 20
+35 00 c0 1c 32 00 00 1a 1a 1d 00 80 51 d0 1c 20
+40 80 35 00 c0 1c 32 00 00 1c 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b7
+
+70 20 5f 02 fd 20 00 10 00 00 00 01 00 01 00 00
+00 00 00 04 54 65 73 74 21 00 1d 80 3e 28 23 00
+0f 70 08 00 3c 8a 54 cc 84 99 68 42 0f 00 45 54
+00 00 00 00 00 00 00 ff 22 00 14 4f 10 09 80 ff
+0e 2f 02 27 81 57 00 6f 08 59 00 47 80 09 00 24
+00 06 12 7f 07 37 04 3b 26 00 09 02 02 01 01 00
+e0 07 00 00 3b 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..978491b
--- /dev/null
+++ b/test/README
@@ -0,0 +1,43 @@
+This test directory contains some special test EDIDs:
+
+Three test files to check the edid-decode file parser:
+
+quantumdata.test: an EDID as generated by the QuantumData (nowadays
+ Teledyne LeCroy) software.
+
+xorg.test: an EDID as logged by xorg in /var/log/Xorg.0.log.
+
+xrandr.test: an EDID as reported by 'xrandr --props'.
+
+
+Five hand-crafted EDIDs to test rarely seen EDID extensions or data blocks:
+
+cta-vesa.test: has VESA Display Transfer Characteristics Data Block and
+ VESA Video Display Device Data Block in the CTA-861 block.
+
+ls-ext.test: has a Localized String Extension Block.
+
+blockmap-128.test: has a single Block Map extension and a total of
+ 128 blocks.
+
+blockmap-255.test: has two Block Map extensions and a total of
+ 255 blocks (the maximum possible EDID size when the second
+ Block Map is used).
+
+256-blocks.test: has the maximum number of 256 blocks.
+
+
+Examples of old EDID versions:
+
+edid-1.X.test: EDIDs with version 1.0, 1.1 and 1.2.
+
+
+Test EDIDs from several standards:
+
+cta-annex-a.test
+cta-annex-d.test
+vesa-edid-1.1.test
+vesa-edid-1.3.test
+vesa-edid-1.4-1.test
+vesa-edid-1.4-2.test
+vesa-edid-1.4-3.test
diff --git a/test/blockmap-128.test b/test/blockmap-128.test
new file mode 100644
index 0000000..cd57d0e
--- /dev/null
+++ b/test/blockmap-128.test
@@ -0,0 +1,1153 @@
+edid-decode (hex):
+
+00 ff ff ff ff ff ff 00 31 d8 34 12 00 00 00 00
+22 1a 01 04 a2 60 36 78 1f ee 91 a3 54 4c 99 26
+0f 50 54 ff ff 80 31 59 45 59 81 80 81 40 90 40
+95 00 a9 40 b3 00 08 e8 00 30 f2 70 5a 80 b0 58
+8a 00 c0 1c 32 00 00 1e 00 00 00 fd 00 18 58 0f
+87 3c 04 12 01 e0 f8 38 f0 3c 00 00 00 fc 00 30
+31 32 33 34 35 36 37 38 39 30 31 32 00 00 00 f7
+00 0a ff ff ff ff ff f0 00 00 00 00 00 00 7f 57
+
+f0 02 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 5e
+
+02 03 24 f1 51 61 60 5f 5e 5d 10 1f 04 13 22 21
+20 05 14 02 11 01 23 09 07 07 e2 00 ca e6 0d 61
+81 82 6a 84 4d d0 00 a0 f0 70 3e 80 30 20 35 00
+c0 1c 32 00 00 1e 1a 36 80 a0 70 38 1f 40 30 20
+35 00 c0 1c 32 00 00 1a 1a 1d 00 80 51 d0 1c 20
+40 80 35 00 c0 1c 32 00 00 1c 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d6
+
+70 20 5f 02 7c 20 00 10 12 34 56 01 00 01 00 00
+00 00 00 04 54 65 73 74 21 00 1d 80 3e 28 23 00
+0f 70 08 00 3c 8a 54 cc 84 99 68 42 0f 00 45 54
+00 00 00 00 00 00 00 ff 22 00 14 4f 10 09 84 ff
+0e 2f 02 af 80 57 00 6f 08 59 00 07 80 09 00 24
+00 06 12 7f 07 37 04 3b 26 00 09 02 02 01 01 00
+e0 07 00 00 d5 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
diff --git a/test/blockmap-255.test b/test/blockmap-255.test
new file mode 100644
index 0000000..e42d3c3
--- /dev/null
+++ b/test/blockmap-255.test
@@ -0,0 +1,2296 @@
+edid-decode (hex):
+
+00 ff ff ff ff ff ff 00 31 d8 34 12 00 00 00 00
+22 1a 01 04 a2 60 36 78 1f ee 91 a3 54 4c 99 26
+0f 50 54 ff ff 80 31 59 45 59 81 80 81 40 90 40
+95 00 a9 40 b3 00 08 e8 00 30 f2 70 5a 80 b0 58
+8a 00 c0 1c 32 00 00 1e 00 00 00 fd 00 18 58 0f
+87 3c 04 12 01 e0 f8 38 f0 3c 00 00 00 fc 00 30
+31 32 33 34 35 36 37 38 39 30 31 32 00 00 00 f7
+00 0a ff ff ff ff ff f0 00 00 00 00 00 00 fe d8
+
+f0 02 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 5e
+
+02 03 24 f1 51 61 60 5f 5e 5d 10 1f 04 13 22 21
+20 05 14 02 11 01 23 09 07 07 e2 00 ca e6 0d 61
+81 82 6a 84 4d d0 00 a0 f0 70 3e 80 30 20 35 00
+c0 1c 32 00 00 1e 1a 36 80 a0 70 38 1f 40 30 20
+35 00 c0 1c 32 00 00 1a 1a 1d 00 80 51 d0 1c 20
+40 80 35 00 c0 1c 32 00 00 1c 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d6
+
+70 20 5f 02 fa 20 00 10 12 34 56 01 00 01 00 00
+00 00 00 04 54 65 73 74 21 00 1d 80 3e 28 23 00
+0f 70 08 00 3c 8a 54 cc 84 99 68 42 0f 00 45 54
+00 00 00 00 00 00 00 ff 22 00 14 4f 10 09 84 ff
+0e 2f 02 af 80 57 00 6f 08 59 00 07 80 09 00 24
+00 06 12 7f 07 37 04 3b 26 00 09 02 02 01 01 00
+e0 07 00 00 57 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+f0 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 70
+70 70 70 70 70 70 70 70 70 70 70 70 70 70 70 f0
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
+
+70 20 04 00 00 23 00 01 02 b6 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90
diff --git a/test/cta-annex-a.test b/test/cta-annex-a.test
new file mode 100644
index 0000000..4159c54
--- /dev/null
+++ b/test/cta-annex-a.test
@@ -0,0 +1,17 @@
+00 ff ff ff ff ff ff 00 0c a1 00 00 00 00 00 00
+00 0c 01 03 80 50 2d 78 0a 0d c9 a0 57 47 98 27
+12 48 4c 20 00 00 01 01 01 01 01 01 01 01 01 01
+01 01 01 01 01 01 01 1d 80 18 71 1c 16 20 58 2c
+25 00 20 c2 31 00 00 9e 8c 0a d0 8a 20 e0 2d 10
+10 3e 96 00 58 c2 21 00 00 18 00 00 00 fc 00 4d
+59 20 48 44 54 56 0a 20 20 20 20 20 00 00 00 fd
+00 3b 3d 0f 2e 08 00 0a 20 20 20 20 20 20 01 c3
+
+02 01 04 00 01 1d 00 72 51 d0 1e 20 6e 28 55 00
+20 c2 31 00 00 1e 8c 0a a0 14 51 f0 16 00 26 7c
+43 00 58 c2 21 00 00 98 00 00 00 01 00 52 45 56
+31 2e 30 30 0a 00 00 00 00 00 00 00 00 ff 00 39
+39 46 43 35 30 30 30 31 0a 20 20 20 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84
diff --git a/test/cta-annex-d.test b/test/cta-annex-d.test
new file mode 100644
index 0000000..e517bda
--- /dev/null
+++ b/test/cta-annex-d.test
@@ -0,0 +1,17 @@
+00 ff ff ff ff ff ff 00 0c a1 00 00 00 00 00 00
+00 0f 01 03 80 50 2d 78 0a 0d c9 a0 57 47 98 27
+12 48 4c 20 00 00 01 01 01 01 01 01 01 01 01 01
+01 01 01 01 01 01 01 1d 80 18 71 1c 16 20 58 2c
+25 00 20 c2 31 00 00 9e 8c 0a d0 8a 20 e0 2d 10
+10 3e 96 00 58 c2 21 00 00 18 00 00 00 fc 00 4d
+59 20 48 44 54 56 0a 20 20 20 20 20 00 00 00 fd
+00 3b 3d 0f 2e 08 00 0a 20 20 20 20 20 20 01 c0
+
+02 03 1a 71 47 85 02 03 04 06 07 01 23 09 07 07
+83 01 00 00 65 03 0c 00 10 00 01 1d 00 72 51 d0
+1e 20 6e 28 55 00 20 c2 31 00 00 1e 8c 0a a0 14
+51 f0 16 00 26 7c 43 00 58 c2 21 00 00 98 8c 0a
+d0 8a 20 e0 2d 10 10 3e 96 00 20 c2 31 00 00 18
+8c 0a a0 14 51 f0 16 00 26 7c 43 00 20 c2 31 00
+00 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 7a
diff --git a/test/cta-vesa.test b/test/cta-vesa.test
new file mode 100644
index 0000000..1526dc1
--- /dev/null
+++ b/test/cta-vesa.test
@@ -0,0 +1,17 @@
+00 ff ff ff ff ff ff 00 31 d8 34 12 00 00 00 00
+22 1a 01 03 80 60 36 78 1e ee 91 a3 54 4c 99 26
+0f 50 54 2f cf 00 31 59 45 59 81 80 81 40 90 40
+95 00 a9 40 b3 00 08 e8 00 30 f2 70 5a 80 b0 58
+8a 00 c0 1c 32 00 00 1e 00 00 00 fd 00 18 55 18
+87 3c 00 0a 20 20 20 20 20 20 00 00 00 fc 00 68
+64 6d 69 2d 34 6b 2d 36 30 30 0a 20 00 00 00 10
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 1d
+
+02 03 3d f0 41 61 23 09 07 07 e2 00 ea af 06 1b
+2c 34 3f 3c 60 43 4b 6c 44 82 4d 3b 42 ff 02 92
+11 03 00 00 00 0a 40 06 3c 11 0a 18 18 d0 80 83
+8f 3c 77 41 02 25 4a 7e 77 00 00 8c 57 4d d0 00
+a0 f0 70 3e 80 30 20 35 00 c0 8c 32 00 00 1e 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78
diff --git a/test/edid-1.0.test b/test/edid-1.0.test
new file mode 100644
index 0000000..ea49217
--- /dev/null
+++ b/test/edid-1.0.test
@@ -0,0 +1,8 @@
+00 ff ff ff ff ff ff 00 34 38 c2 0b f1 01 00 00
+0f 0a 01 00 28 20 18 32 e8 7e 4e 9e 57 45 98 24
+10 47 4f a4 42 01 31 59 45 59 61 59 71 4f 81 80
+01 01 01 01 01 01 f9 15 20 f8 30 58 1f 20 20 40
+13 00 40 f0 10 00 00 1e a4 1a 20 10 31 58 24 20
+2f 55 33 00 40 f0 10 00 00 1e 30 2a 00 98 51 00
+2a 40 30 70 13 00 40 f0 10 00 00 1e ea 24 00 60
+41 00 28 30 30 60 13 00 40 f0 10 00 00 1e 00 fb
diff --git a/test/edid-1.1.test b/test/edid-1.1.test
new file mode 100644
index 0000000..b469995
--- /dev/null
+++ b/test/edid-1.1.test
@@ -0,0 +1,8 @@
+00 ff ff ff ff ff ff 00 34 ac 81 43 f9 14 00 00
+16 0a 01 01 0c 1f 17 a3 e8 67 a8 a0 56 47 99 26
+11 48 4c ff ef 80 31 59 45 59 61 59 71 4f 81 59
+81 99 a9 45 a9 4a 1a 4f 40 30 62 b0 32 40 40 c0
+13 00 38 ea 10 00 00 1e 00 00 00 fd 00 32 82 1e
+60 16 00 0a 20 20 20 20 20 20 00 00 00 fc 00 4e
+46 4e 39 37 30 35 0a 20 20 20 20 20 00 00 00 ff
+00 30 30 35 31 30 35 33 36 39 0a 20 20 20 00 6f
diff --git a/test/edid-1.2.test b/test/edid-1.2.test
new file mode 100644
index 0000000..e0d39a9
--- /dev/null
+++ b/test/edid-1.2.test
@@ -0,0 +1,8 @@
+00 ff ff ff ff ff ff 00 4c a3 46 36 00 00 00 00
+00 12 01 02 80 21 15 78 0a 87 f5 94 57 4f 8c 27
+27 50 54 00 00 00 01 01 01 01 01 01 01 01 01 01
+01 01 01 01 01 01 e0 2e 90 c4 60 1a 0f 40 18 58
+13 00 4b cf 10 00 00 19 00 00 00 0f 00 00 00 00
+00 00 00 00 00 3c d2 02 64 00 00 00 00 fe 00 53
+41 4d 53 55 4e 47 0a 20 20 20 20 20 00 00 00 fe
+00 4c 54 4e 31 35 34 50 33 2d 4c 30 34 0a 00 e0
diff --git a/test/ls-ext.test b/test/ls-ext.test
new file mode 100644
index 0000000..ec09067
--- /dev/null
+++ b/test/ls-ext.test
@@ -0,0 +1,26 @@
+00 ff ff ff ff ff ff 00 31 d8 34 12 00 00 00 00
+22 1a 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26
+0f 50 54 2f cf 00 31 59 45 59 81 80 81 40 90 40
+95 00 a9 40 b3 00 08 e8 00 30 f2 70 5a 80 b0 58
+8a 00 c0 1c 32 00 00 1e 00 00 00 fd 00 18 55 18
+87 3c 00 0a 20 20 20 20 20 20 00 00 00 fc 00 68
+64 6d 69 2d 34 6b 2d 36 30 30 0a 20 00 00 00 10
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2b
+
+02 03 3f f0 51 61 60 5f 5e 5d 10 1f 04 13 22 21
+20 05 14 02 11 01 23 09 07 07 83 01 00 00 6d 03
+0c 00 10 00 00 3c 21 00 60 01 02 03 67 d8 5d c4
+01 78 00 00 e2 00 ea e3 05 00 00 e3 06 01 00 4d
+d0 00 a0 f0 70 3e 80 30 20 35 00 c0 1c 32 00 00
+1e 1a 36 80 a0 70 38 1f 40 30 20 35 00 c0 1c 32
+00 00 1a 1a 1d 00 80 51 d0 1c 20 40 80 35 00 c0
+1c 32 00 00 1c 00 00 00 00 00 00 00 00 00 00 63
+
+50 01 00 30 00 22 00 02 10 39 84 09 46 61 62 72
+69 6b 61 6e 74 05 4d 6f 64 65 6c 0b 53 65 72 69
+65 6e 75 6d 6d 65 72 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 66
diff --git a/test/quantumdata.test b/test/quantumdata.test
new file mode 100644
index 0000000..a2e158e
--- /dev/null
+++ b/test/quantumdata.test
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<DATAOBJ>
+ <HEADER TYPE="DID" VERSION="1.0"/>
+ <DATA>
+ <BLOCK0>00FFFFFFFFFFFF0031D8341200000000221A0103806036780FEE91A3544C99260F50542FCF00315945598180814090409500A940B30008E80030F2705A80B0588A00C01C3200001E000000FD00185518873C000A202020202020000000FC0068646D692D346B2D3630300A20000000100000000000000000000000000000012C</BLOCK0>
+ <BLOCK1>02033FF05161605F5E5D101F0413222120051402110123090707830100006D030C001000003C21006001020367D85DC401780000E200EAE3050000E30601004DD000A0F0703E8030203500C01C3200001E1A3680A070381F4030203500C01C3200001A1A1D008051D01C2040803500C01C3200001C0000000000000000000063</BLOCK1>
+ </DATA>
+</DATAOBJ>
diff --git a/test/vesa-edid-1.1.test b/test/vesa-edid-1.1.test
new file mode 100644
index 0000000..f90109d
--- /dev/null
+++ b/test/vesa-edid-1.1.test
@@ -0,0 +1,8 @@
+00 ff ff ff ff ff ff 00 24 4d 8e 19 00 00 00 00
+0a 05 01 01 08 28 1e b4 c8 00 b2 a0 57 49 9b 26
+10 48 4f a4 cf 7c 31 4a a9 40 a9 4a a9 4f 81 80
+01 01 01 01 01 01 10 0b d0 b4 20 5e 63 10 12 6c
+62 08 fa b8 00 00 00 1a 00 00 00 ff 00 33 30 39
+41 42 43 30 30 30 32 35 0a 20 00 00 00 fe 00 54
+48 49 53 20 49 53 20 41 0a 20 20 20 00 00 00 fe
+00 54 45 53 54 2c 20 54 48 45 20 45 4e 44 00 8f
diff --git a/test/vesa-edid-1.3.test b/test/vesa-edid-1.3.test
new file mode 100644
index 0000000..c114dbb
--- /dev/null
+++ b/test/vesa-edid-1.3.test
@@ -0,0 +1,9 @@
+00 ff ff ff ff ff ff 00 10 ac ab 50 00 00 00 00
+2a 09 01 03 0e 26 1d 96 ef ee 91 a3 54 4c 99 26
+0f 50 54 a5 43 00 a9 4f a9 59 71 59 61 59 45 59
+31 59 c2 8f 01 01 86 3d 00 c0 51 00 30 40 40 a0
+13 00 7c 22 11 00 00 1e 00 00 00 ff 00 35 35 33
+34 37 42 4f 4e 5a 48 34 37 0a 00 00 00 fc 00 44
+45 4c 4c 20 55 52 31 31 31 0a 20 20 00 00 00 fd
+00 30 a0 1e 79 1c 02 00 28 50 10 0e 80 46 00 8d
+
diff --git a/test/vesa-edid-1.4-1.test b/test/vesa-edid-1.4-1.test
new file mode 100644
index 0000000..e30915f
--- /dev/null
+++ b/test/vesa-edid-1.4-1.test
@@ -0,0 +1,8 @@
+00 ff ff ff ff ff ff 00 04 43 06 f2 01 00 00 00
+01 11 01 04 0f 2b 20 78 2b 9c 68 a0 57 4a 9b 26
+12 48 4c ff ff 80 a9 59 a9 4f a9 4a a9 45 81 99
+81 80 61 59 45 59 48 3f 40 30 62 b0 32 40 40 c0
+13 00 ab 40 11 00 00 1e 00 00 00 fd 00 32 5a 1e
+6e 17 04 11 00 c8 90 00 50 3c 00 00 00 f7 00 0a
+f7 0f 03 87 c0 00 00 00 00 00 00 00 00 00 00 fc
+00 41 42 43 20 4c 43 44 32 31 0a 20 20 20 00 9a
diff --git a/test/vesa-edid-1.4-2.test b/test/vesa-edid-1.4-2.test
new file mode 100644
index 0000000..b4b7d36
--- /dev/null
+++ b/test/vesa-edid-1.4-2.test
@@ -0,0 +1,17 @@
+00 ff ff ff ff ff ff 00 04 43 07 f2 01 00 00 00
+ff 11 01 04 a2 4f 00 78 1e ee 91 a3 54 4c 99 26
+0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01
+01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c
+04 05 0f 48 42 00 00 1e 01 1d 80 18 71 1c 16 20
+58 2c 25 00 0f 48 42 00 00 9e 01 1d 00 72 51 d0
+1e 20 6e 28 55 00 0f 48 42 00 00 1e 00 00 00 fc
+00 41 42 43 20 4c 43 44 34 37 77 0a 20 20 01 cb
+
+02 03 1a 72 47 90 85 04 03 02 07 06 23 09 07 07
+83 01 00 00 65 03 0c 00 10 00 8e 0a d0 8a 20 e0
+2d 10 10 3e 96 00 1f 09 00 00 00 18 8e 0a d0 8a
+20 e0 2d 10 10 3e 96 00 04 03 00 00 00 18 8e 0a
+a0 14 51 f0 16 00 26 7c 43 00 1f 09 00 00 00 98
+8e 0a a0 14 51 f0 16 00 26 7c 43 00 04 03 00 00
+00 98 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c7
diff --git a/test/vesa-edid-1.4-3.test b/test/vesa-edid-1.4-3.test
new file mode 100644
index 0000000..4c813ba
--- /dev/null
+++ b/test/vesa-edid-1.4-3.test
@@ -0,0 +1,17 @@
+00 ff ff ff ff ff ff 00 04 43 08 f2 01 00 00 00
+10 11 01 04 a2 79 44 78 1e ee 91 a3 54 4c 99 26
+0f 50 54 ff ef 80 81 99 81 80 81 59 81 40 61 59
+4b c0 45 59 31 59 66 21 50 b0 51 00 1b 30 40 70
+36 00 be ab 42 00 00 1e 01 1d 00 72 51 d0 1e 20
+6e 28 55 00 be ab 42 00 00 1e 00 00 00 f7 00 0a
+f7 0f 03 87 c0 00 00 00 00 00 00 00 00 00 00 fc
+00 41 42 43 20 50 4c 41 35 35 0a 20 20 20 01 0a
+
+02 03 19 f0 46 05 04 03 02 07 06 23 09 07 07 83
+01 00 00 65 03 0c 00 10 00 01 1d 80 18 71 1c 16
+20 58 2c 25 00 0f 48 42 00 00 9e 01 1d 00 72 51
+d0 1e 20 6e 28 55 00 0f 48 42 00 00 1e 8c 0a d0
+8a 20 e0 2d 10 10 3e 96 00 b9 88 21 00 00 18 8e
+0a a0 14 51 f0 16 00 26 7c 43 00 b9 88 21 00 00
+98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 58
diff --git a/test/xorg.test b/test/xorg.test
new file mode 100644
index 0000000..093f500
--- /dev/null
+++ b/test/xorg.test
@@ -0,0 +1,1574 @@
+[ 4.295] (--) Log file renamed from "/var/log/Xorg.pid-762.log" to "/var/log/Xorg.0.log"
+[ 4.296]
+X.Org X Server 1.20.4
+X Protocol Version 11, Revision 0
+[ 4.296] Build Operating System: Linux 4.9.0-8-amd64 x86_64 Debian
+[ 4.296] Current Operating System: Linux octomore 5.4.0-rc8-octomore #23 SMP PREEMPT Sat Nov 23 16:00:49 CET 2019 x86_64
+[ 4.296] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-5.4.0-rc8-octomore root=UUID=e227131b-ee41-479a-a25e-bce91b18a6ed ro
+[ 4.296] Build Date: 05 March 2019 08:11:12PM
+[ 4.296] xorg-server 2:1.20.4-1 (https://www.debian.org/support)
+[ 4.296] Current version of pixman: 0.36.0
+[ 4.296] Before reporting problems, check http://wiki.x.org
+ to make sure that you have the latest version.
+[ 4.296] Markers: (--) probed, (**) from config file, (==) default setting,
+ (++) from command line, (!!) notice, (II) informational,
+ (WW) warning, (EE) error, (NI) not implemented, (??) unknown.
+[ 4.296] (==) Log file: "/var/log/Xorg.0.log", Time: Tue Nov 26 16:25:38 2019
+[ 4.297] (==) Using system config directory "/usr/share/X11/xorg.conf.d"
+[ 4.298] (==) No Layout section. Using the first Screen section.
+[ 4.298] (==) No screen section available. Using defaults.
+[ 4.298] (**) |-->Screen "Default Screen Section" (0)
+[ 4.298] (**) | |-->Monitor "<default monitor>"
+[ 4.298] (==) No monitor specified for screen "Default Screen Section".
+ Using a default monitor configuration.
+[ 4.298] (==) Automatically adding devices
+[ 4.298] (==) Automatically enabling devices
+[ 4.298] (==) Automatically adding GPU devices
+[ 4.298] (==) Max clients allowed: 256, resource mask: 0x1fffff
+[ 4.299] (WW) The directory "/usr/share/fonts/X11/cyrillic" does not exist.
+[ 4.299] Entry deleted from font path.
+[ 4.301] (==) FontPath set to:
+ /usr/share/fonts/X11/misc,
+ /usr/share/fonts/X11/100dpi/:unscaled,
+ /usr/share/fonts/X11/75dpi/:unscaled,
+ /usr/share/fonts/X11/Type1,
+ /usr/share/fonts/X11/100dpi,
+ /usr/share/fonts/X11/75dpi,
+ built-ins
+[ 4.301] (==) ModulePath set to "/usr/lib/xorg/modules"
+[ 4.301] (II) The server relies on udev to provide the list of input devices.
+ If no devices become available, reconfigure udev or disable AutoAddDevices.
+[ 4.301] (II) Loader magic: 0x55a9db113e20
+[ 4.301] (II) Module ABI versions:
+[ 4.301] X.Org ANSI C Emulation: 0.4
+[ 4.301] X.Org Video Driver: 24.0
+[ 4.301] X.Org XInput driver : 24.1
+[ 4.301] X.Org Server Extension : 10.0
+[ 4.301] (++) using VT number 7
+
+[ 4.301] (II) systemd-logind: logind integration requires -keeptty and -keeptty was not provided, disabling logind integration
+[ 4.302] (II) xfree86: Adding drm device (/dev/dri/card1)
+[ 4.316] (II) xfree86: Adding drm device (/dev/dri/card0)
+[ 4.318] (--) PCI:*(0@0:2:0) 8086:5917:17aa:225c rev 7, Mem @ 0x2ffa000000/16777216, 0x2fa0000000/268435456, I/O @ 0x0000e000/64, BIOS @ 0x????????/131072
+[ 4.318] (II) LoadModule: "glx"
+[ 4.319] (II) Loading /usr/lib/xorg/modules/extensions/libglx.so
+[ 4.325] (II) Module glx: vendor="X.Org Foundation"
+[ 4.325] compiled for 1.20.4, module version = 1.0.0
+[ 4.325] ABI class: X.Org Server Extension, version 10.0
+[ 4.325] (==) Matched modesetting as autoconfigured driver 0
+[ 4.325] (==) Matched fbdev as autoconfigured driver 1
+[ 4.325] (==) Matched vesa as autoconfigured driver 2
+[ 4.325] (==) Assigned the driver to the xf86ConfigLayout
+[ 4.325] (II) LoadModule: "modesetting"
+[ 4.325] (II) Loading /usr/lib/xorg/modules/drivers/modesetting_drv.so
+[ 4.326] (II) Module modesetting: vendor="X.Org Foundation"
+[ 4.326] compiled for 1.20.4, module version = 1.20.4
+[ 4.326] Module class: X.Org Video Driver
+[ 4.326] ABI class: X.Org Video Driver, version 24.0
+[ 4.326] (II) LoadModule: "fbdev"
+[ 4.326] (II) Loading /usr/lib/xorg/modules/drivers/fbdev_drv.so
+[ 4.327] (II) Module fbdev: vendor="X.Org Foundation"
+[ 4.327] compiled for 1.20.0, module version = 0.5.0
+[ 4.327] Module class: X.Org Video Driver
+[ 4.327] ABI class: X.Org Video Driver, version 24.0
+[ 4.327] (II) LoadModule: "vesa"
+[ 4.327] (II) Loading /usr/lib/xorg/modules/drivers/vesa_drv.so
+[ 4.327] (II) Module vesa: vendor="X.Org Foundation"
+[ 4.327] compiled for 1.20.4, module version = 2.4.0
+[ 4.327] Module class: X.Org Video Driver
+[ 4.327] ABI class: X.Org Video Driver, version 24.0
+[ 4.327] (II) modesetting: Driver for Modesetting Kernel Drivers: kms
+[ 4.327] (II) FBDEV: driver for framebuffer: fbdev
+[ 4.327] (II) VESA: driver for VESA chipsets: vesa
+[ 4.350] (II) modeset(0): using drv /dev/dri/card1
+[ 4.350] (WW) Falling back to old probe method for fbdev
+[ 4.350] (II) Loading sub module "fbdevhw"
+[ 4.350] (II) LoadModule: "fbdevhw"
+[ 4.350] (II) Loading /usr/lib/xorg/modules/libfbdevhw.so
+[ 4.350] (II) Module fbdevhw: vendor="X.Org Foundation"
+[ 4.350] compiled for 1.20.4, module version = 0.0.2
+[ 4.350] ABI class: X.Org Video Driver, version 24.0
+[ 4.350] (II) modeset(0): Creating default Display subsection in Screen section
+ "Default Screen Section" for depth/fbbpp 24/32
+[ 4.350] (==) modeset(0): Depth 24, (==) framebuffer bpp 32
+[ 4.350] (==) modeset(0): RGB weight 888
+[ 4.350] (==) modeset(0): Default visual is TrueColor
+[ 4.350] (II) Loading sub module "glamoregl"
+[ 4.350] (II) LoadModule: "glamoregl"
+[ 4.350] (II) Loading /usr/lib/xorg/modules/libglamoregl.so
+[ 4.356] (II) Module glamoregl: vendor="X.Org Foundation"
+[ 4.356] compiled for 1.20.4, module version = 1.0.1
+[ 4.356] ABI class: X.Org ANSI C Emulation, version 0.4
+[ 4.395] (II) modeset(0): glamor X acceleration enabled on Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2)
+[ 4.395] (II) modeset(0): glamor initialized
+[ 4.396] (II) modeset(0): Output eDP-1 has no monitor section
+[ 4.396] (II) modeset(0): Output DP-1 has no monitor section
+[ 4.404] (II) modeset(0): Output HDMI-1 has no monitor section
+[ 4.404] (II) modeset(0): Output DP-2 has no monitor section
+[ 4.513] (II) modeset(0): Output HDMI-2 has no monitor section
+[ 4.514] (II) modeset(0): EDID for output eDP-1
+[ 4.514] (II) modeset(0): Manufacturer: AUO Model: 313d Serial#: 0
+[ 4.514] (II) modeset(0): Year: 2016 Week: 0
+[ 4.514] (II) modeset(0): EDID Version: 1.4
+[ 4.514] (II) modeset(0): Digital Display Input
+[ 4.514] (II) modeset(0): 8 bits per channel
+[ 4.514] (II) modeset(0): Digital interface is DisplayPort
+[ 4.514] (II) modeset(0): Max Image Size [cm]: horiz.: 31 vert.: 17
+[ 4.514] (II) modeset(0): Gamma: 2.20
+[ 4.514] (II) modeset(0): No DPMS capabilities specified
+[ 4.514] (II) modeset(0): Supported color encodings: RGB 4:4:4
+[ 4.514] (II) modeset(0): First detailed timing is preferred mode
+[ 4.514] (II) modeset(0): Preferred mode is native pixel format and refresh rate
+[ 4.514] (II) modeset(0): redX: 0.631 redY: 0.336 greenX: 0.323 greenY: 0.614
+[ 4.514] (II) modeset(0): blueX: 0.156 blueY: 0.040 whiteX: 0.313 whiteY: 0.329
+[ 4.514] (II) modeset(0): Manufacturer's mask: 0
+[ 4.514] (II) modeset(0): Supported detailed timing:
+[ 4.514] (II) modeset(0): clock: 141.0 MHz Image Size: 309 x 174 mm
+[ 4.514] (II) modeset(0): h_active: 1920 h_sync: 1936 h_sync_end 1952 h_blank_end 2104 h_border: 0
+[ 4.514] (II) modeset(0): v_active: 1080 v_sync: 1083 v_sync_end 1097 v_blanking: 1116 v_border: 0
+[ 4.514] (II) modeset(0): Unknown vendor-specific block f
+[ 4.514] (II) modeset(0): AUO
+[ 4.514] (II) modeset(0): B140HAN03.1
+[ 4.514] (II) modeset(0): EDID (in hex):
+[ 4.514] (II) modeset(0): 00ffffffffffff0006af3d3100000000
+[ 4.514] (II) modeset(0): 001a0104a51f1178028d15a156529d28
+[ 4.514] (II) modeset(0): 0a505400000001010101010101010101
+[ 4.514] (II) modeset(0): 010101010101143780b8703824401010
+[ 4.514] (II) modeset(0): 3e0035ae100000180000000f00000000
+[ 4.514] (II) modeset(0): 00000000000000000020000000fe0041
+[ 4.514] (II) modeset(0): 554f0a202020202020202020000000fe
+[ 4.514] (II) modeset(0): 004231343048414e30332e31200a003b
+[ 4.514] (II) modeset(0): Printing probed modes for output eDP-1
+[ 4.514] (II) modeset(0): Modeline "1920x1080"x60.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 4.514] (II) modeset(0): Modeline "1920x1080"x120.0 356.38 1920 2080 2288 2656 1080 1081 1084 1118 doublescan -hsync +vsync (134.2 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1920x1080"x119.9 266.50 1920 1944 1960 2000 1080 1081 1084 1111 doublescan +hsync -vsync (133.2 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1920x1080"x60.0 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync (67.2 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1920x1080"x59.9 138.50 1920 1968 2000 2080 1080 1083 1088 1111 +hsync -vsync (66.6 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1680x1050"x60.0 146.25 1680 1784 1960 2240 1050 1053 1059 1089 -hsync +vsync (65.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1680x1050"x59.9 119.00 1680 1728 1760 1840 1050 1053 1059 1080 +hsync -vsync (64.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1600x1024"x60.2 103.12 1600 1600 1656 1664 1024 1024 1029 1030 +hsync +vsync (62.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1400x1050"x60.0 122.00 1400 1488 1640 1880 1050 1052 1064 1082 +hsync +vsync (64.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1600x900"x120.0 246.00 1600 1728 1900 2200 900 901 904 932 doublescan -hsync +vsync (111.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1600x900"x119.9 186.50 1600 1624 1640 1680 900 901 904 926 doublescan +hsync -vsync (111.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1600x900"x59.9 118.25 1600 1696 1856 2112 900 903 908 934 -hsync +vsync (56.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1600x900"x59.8 97.50 1600 1648 1680 1760 900 903 908 926 +hsync -vsync (55.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x1024"x60.0 108.00 1280 1328 1440 1688 1024 1025 1028 1066 +hsync +vsync (64.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1440x900"x59.9 106.50 1440 1520 1672 1904 900 903 909 934 -hsync +vsync (55.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1400x900"x60.0 103.50 1400 1480 1624 1848 900 903 913 934 -hsync +vsync (56.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1400x900"x59.9 86.50 1400 1448 1480 1560 900 903 913 926 +hsync -vsync (55.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x960"x60.0 108.00 1280 1376 1488 1800 960 961 964 1000 +hsync +vsync (60.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1440x810"x120.0 198.12 1440 1548 1704 1968 810 811 814 839 doublescan -hsync +vsync (100.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1440x810"x119.9 151.88 1440 1464 1480 1520 810 811 814 833 doublescan +hsync -vsync (99.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1368x768"x59.9 85.25 1368 1440 1576 1784 768 771 781 798 -hsync +vsync (47.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1368x768"x59.9 72.25 1368 1416 1448 1528 768 771 781 790 +hsync -vsync (47.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1360x768"x59.8 84.75 1360 1432 1568 1776 768 771 781 798 -hsync +vsync (47.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1360x768"x60.0 72.00 1360 1408 1440 1520 768 771 781 790 +hsync -vsync (47.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x800"x120.0 174.25 1280 1380 1516 1752 800 801 804 829 doublescan -hsync +vsync (99.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x800"x119.9 134.25 1280 1304 1320 1360 800 801 804 823 doublescan +hsync -vsync (98.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x800"x59.8 83.50 1280 1352 1480 1680 800 803 809 831 -hsync +vsync (49.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x800"x59.9 71.00 1280 1328 1360 1440 800 803 809 823 +hsync -vsync (49.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1152x864"x60.0 81.62 1152 1216 1336 1520 864 865 868 895 -hsync +vsync (53.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x720"x120.0 156.12 1280 1376 1512 1744 720 721 724 746 doublescan -hsync +vsync (89.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x720"x120.0 120.75 1280 1304 1320 1360 720 721 724 740 doublescan +hsync -vsync (88.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x720"x59.9 74.50 1280 1344 1472 1664 720 723 728 748 -hsync +vsync (44.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1280x720"x59.7 63.75 1280 1328 1360 1440 720 723 728 741 +hsync -vsync (44.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1024x768"x120.1 133.47 1024 1100 1212 1400 768 768 770 794 doublescan -hsync +vsync (95.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1024x768"x60.0 65.00 1024 1048 1184 1344 768 771 777 806 -hsync -vsync (48.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x720"x120.0 117.00 960 1024 1128 1300 720 720 722 750 doublescan -hsync +vsync (90.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "928x696"x120.1 109.15 928 976 1088 1264 696 696 698 719 doublescan -hsync +vsync (86.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "896x672"x120.0 102.40 896 960 1060 1224 672 672 674 697 doublescan -hsync +vsync (83.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1024x576"x119.9 98.50 1024 1092 1200 1376 576 577 580 597 doublescan -hsync +vsync (71.6 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1024x576"x119.9 78.38 1024 1048 1064 1104 576 577 580 592 doublescan +hsync -vsync (71.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1024x576"x59.9 46.50 1024 1064 1160 1296 576 579 584 599 -hsync +vsync (35.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "1024x576"x59.8 42.00 1024 1072 1104 1184 576 579 584 593 +hsync -vsync (35.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x600"x119.9 96.62 960 1028 1128 1296 600 601 604 622 doublescan -hsync +vsync (74.6 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x600"x120.0 77.00 960 984 1000 1040 600 601 604 617 doublescan +hsync -vsync (74.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x540"x119.9 86.50 960 1024 1124 1288 540 541 544 560 doublescan -hsync +vsync (67.2 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x540"x120.0 69.25 960 984 1000 1040 540 541 544 555 doublescan +hsync -vsync (66.6 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x540"x59.6 40.75 960 992 1088 1216 540 543 548 562 -hsync +vsync (33.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "960x540"x59.8 37.25 960 1008 1040 1120 540 543 548 556 +hsync -vsync (33.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "800x600"x120.0 81.00 800 832 928 1080 600 600 602 625 doublescan +hsync +vsync (75.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "800x600"x60.3 40.00 800 840 968 1056 600 601 605 628 +hsync +vsync (37.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "800x600"x56.2 36.00 800 824 896 1024 600 601 603 625 +hsync +vsync (35.2 kHz d)
+[ 4.514] (II) modeset(0): Modeline "840x525"x120.0 73.12 840 892 980 1120 525 526 529 544 doublescan -hsync +vsync (65.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "840x525"x119.8 59.50 840 864 880 920 525 526 529 540 doublescan +hsync -vsync (64.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "864x486"x59.9 32.50 864 888 968 1072 486 489 494 506 -hsync +vsync (30.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "864x486"x59.6 30.50 864 912 944 1024 486 489 494 500 +hsync -vsync (29.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "800x512"x120.3 51.56 800 800 828 832 512 512 514 515 doublescan +hsync +vsync (62.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "700x525"x120.0 61.00 700 744 820 940 525 526 532 541 doublescan +hsync +vsync (64.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "800x450"x119.9 59.12 800 848 928 1056 450 451 454 467 doublescan -hsync +vsync (56.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "800x450"x119.6 48.75 800 824 840 880 450 451 454 463 doublescan +hsync -vsync (55.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x512"x120.0 54.00 640 664 720 844 512 512 514 533 doublescan +hsync +vsync (64.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "720x450"x119.8 53.25 720 760 836 952 450 451 454 467 doublescan -hsync +vsync (55.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "700x450"x119.9 51.75 700 740 812 924 450 451 456 467 doublescan -hsync +vsync (56.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "700x450"x119.8 43.25 700 724 740 780 450 451 456 463 doublescan +hsync -vsync (55.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x480"x120.0 54.00 640 688 744 900 480 480 482 500 doublescan +hsync +vsync (60.0 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x480"x59.9 25.18 640 656 752 800 480 490 492 525 -hsync -vsync (31.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "720x405"x59.5 22.50 720 744 808 896 405 408 413 422 -hsync +vsync (25.1 kHz d)
+[ 4.514] (II) modeset(0): Modeline "720x405"x59.0 21.75 720 768 800 880 405 408 413 419 +hsync -vsync (24.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "684x384"x119.8 42.62 684 720 788 892 384 385 390 399 doublescan -hsync +vsync (47.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "684x384"x119.7 36.12 684 708 724 764 384 385 390 395 doublescan +hsync -vsync (47.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "680x384"x119.6 42.38 680 716 784 888 384 385 390 399 doublescan -hsync +vsync (47.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "680x384"x119.9 36.00 680 704 720 760 384 385 390 395 doublescan +hsync -vsync (47.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x400"x119.8 41.75 640 676 740 840 400 401 404 415 doublescan -hsync +vsync (49.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x400"x120.0 35.50 640 664 680 720 400 401 404 411 doublescan +hsync -vsync (49.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "576x432"x120.1 40.81 576 608 668 760 432 432 434 447 doublescan -hsync +vsync (53.7 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x360"x119.7 37.25 640 672 736 832 360 361 364 374 doublescan -hsync +vsync (44.8 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x360"x119.7 31.88 640 664 680 720 360 361 364 370 doublescan +hsync -vsync (44.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x360"x59.8 18.00 640 664 720 800 360 363 368 376 -hsync +vsync (22.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "640x360"x59.3 17.75 640 688 720 800 360 363 368 374 +hsync -vsync (22.2 kHz d)
+[ 4.514] (II) modeset(0): Modeline "512x384"x120.0 32.50 512 524 592 672 384 385 388 403 doublescan -hsync -vsync (48.4 kHz d)
+[ 4.514] (II) modeset(0): Modeline "512x288"x120.0 23.25 512 532 580 648 288 289 292 299 doublescan -hsync +vsync (35.9 kHz d)
+[ 4.514] (II) modeset(0): Modeline "512x288"x119.8 21.00 512 536 552 592 288 289 292 296 doublescan +hsync -vsync (35.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "480x270"x119.3 20.38 480 496 544 608 270 271 274 281 doublescan -hsync +vsync (33.5 kHz d)
+[ 4.514] (II) modeset(0): Modeline "480x270"x119.6 18.62 480 504 520 560 270 271 274 278 doublescan +hsync -vsync (33.3 kHz d)
+[ 4.514] (II) modeset(0): Modeline "400x300"x120.6 20.00 400 420 484 528 300 300 302 314 doublescan +hsync +vsync (37.9 kHz d)
+[ 4.515] (II) modeset(0): Modeline "400x300"x112.7 18.00 400 412 448 512 300 300 301 312 doublescan +hsync +vsync (35.2 kHz d)
+[ 4.515] (II) modeset(0): Modeline "432x243"x119.8 16.25 432 444 484 536 243 244 247 253 doublescan -hsync +vsync (30.3 kHz d)
+[ 4.515] (II) modeset(0): Modeline "432x243"x119.1 15.25 432 456 472 512 243 244 247 250 doublescan +hsync -vsync (29.8 kHz d)
+[ 4.515] (II) modeset(0): Modeline "320x240"x120.1 12.59 320 328 376 400 240 245 246 262 doublescan -hsync -vsync (31.5 kHz d)
+[ 4.515] (II) modeset(0): Modeline "360x202"x119.0 11.25 360 372 404 448 202 204 206 211 doublescan -hsync +vsync (25.1 kHz d)
+[ 4.515] (II) modeset(0): Modeline "360x202"x118.3 10.88 360 384 400 440 202 204 206 209 doublescan +hsync -vsync (24.7 kHz d)
+[ 4.515] (II) modeset(0): Modeline "320x180"x119.7 9.00 320 332 360 400 180 181 184 188 doublescan -hsync +vsync (22.5 kHz d)
+[ 4.515] (II) modeset(0): Modeline "320x180"x118.6 8.88 320 344 360 400 180 181 184 187 doublescan +hsync -vsync (22.2 kHz d)
+[ 4.515] (II) modeset(0): EDID for output DP-1
+[ 4.531] (II) modeset(0): EDID for output HDMI-1
+[ 4.531] (II) modeset(0): EDID for output DP-2
+[ 4.640] (II) modeset(0): EDID for output HDMI-2
+[ 4.640] (II) modeset(0): Output eDP-1 connected
+[ 4.640] (II) modeset(0): Output DP-1 disconnected
+[ 4.640] (II) modeset(0): Output HDMI-1 disconnected
+[ 4.640] (II) modeset(0): Output DP-2 disconnected
+[ 4.640] (II) modeset(0): Output HDMI-2 disconnected
+[ 4.640] (II) modeset(0): Using exact sizes for initial modes
+[ 4.640] (II) modeset(0): Output eDP-1 using initial mode 1920x1080 +0+0
+[ 4.641] (==) modeset(0): Using gamma correction (1.0, 1.0, 1.0)
+[ 4.641] (==) modeset(0): DPI set to (96, 96)
+[ 4.641] (II) Loading sub module "fb"
+[ 4.641] (II) LoadModule: "fb"
+[ 4.641] (II) Loading /usr/lib/xorg/modules/libfb.so
+[ 4.642] (II) Module fb: vendor="X.Org Foundation"
+[ 4.642] compiled for 1.20.4, module version = 1.0.0
+[ 4.642] ABI class: X.Org ANSI C Emulation, version 0.4
+[ 4.642] (II) UnloadModule: "fbdev"
+[ 4.642] (II) Unloading fbdev
+[ 4.642] (II) UnloadSubModule: "fbdevhw"
+[ 4.642] (II) Unloading fbdevhw
+[ 4.642] (II) UnloadModule: "vesa"
+[ 4.642] (II) Unloading vesa
+[ 4.651] (==) modeset(0): Backing store enabled
+[ 4.651] (==) modeset(0): Silken mouse enabled
+[ 4.716] (II) modeset(0): Initializing kms color map for depth 24, 8 bpc.
+[ 4.716] (==) modeset(0): DPMS enabled
+[ 4.717] (II) modeset(0): [DRI2] Setup complete
+[ 4.717] (II) modeset(0): [DRI2] DRI driver: i965
+[ 4.717] (II) modeset(0): [DRI2] VDPAU driver: i965
+[ 4.717] (II) Initializing extension Generic Event Extension
+[ 4.717] (II) Initializing extension SHAPE
+[ 4.717] (II) Initializing extension MIT-SHM
+[ 4.717] (II) Initializing extension XInputExtension
+[ 4.718] (II) Initializing extension XTEST
+[ 4.718] (II) Initializing extension BIG-REQUESTS
+[ 4.718] (II) Initializing extension SYNC
+[ 4.718] (II) Initializing extension XKEYBOARD
+[ 4.718] (II) Initializing extension XC-MISC
+[ 4.718] (II) Initializing extension SECURITY
+[ 4.718] (II) Initializing extension XFIXES
+[ 4.718] (II) Initializing extension RENDER
+[ 4.718] (II) Initializing extension RANDR
+[ 4.719] (II) Initializing extension COMPOSITE
+[ 4.719] (II) Initializing extension DAMAGE
+[ 4.719] (II) Initializing extension MIT-SCREEN-SAVER
+[ 4.719] (II) Initializing extension DOUBLE-BUFFER
+[ 4.719] (II) Initializing extension RECORD
+[ 4.719] (II) Initializing extension DPMS
+[ 4.719] (II) Initializing extension Present
+[ 4.719] (II) Initializing extension DRI3
+[ 4.719] (II) Initializing extension X-Resource
+[ 4.719] (II) Initializing extension XVideo
+[ 4.720] (II) Initializing extension XVideo-MotionCompensation
+[ 4.720] (II) Initializing extension SELinux
+[ 4.720] (II) SELinux: Disabled on system
+[ 4.720] (II) Initializing extension GLX
+[ 4.724] (II) AIGLX: Loaded and initialized i965
+[ 4.724] (II) GLX: Initialized DRI2 GL provider for screen 0
+[ 4.724] (II) Initializing extension XFree86-VidModeExtension
+[ 4.724] (II) Initializing extension XFree86-DGA
+[ 4.724] (II) Initializing extension XFree86-DRI
+[ 4.724] (II) Initializing extension DRI2
+[ 4.733] (II) modeset(0): Damage tracking initialized
+[ 4.733] (II) modeset(0): Setting screen physical size to 508 x 285
+[ 4.771] (II) config/udev: Adding input device Power Button (/dev/input/event2)
+[ 4.771] (**) Power Button: Applying InputClass "libinput keyboard catchall"
+[ 4.771] (II) LoadModule: "libinput"
+[ 4.771] (II) Loading /usr/lib/xorg/modules/input/libinput_drv.so
+[ 4.774] (II) Module libinput: vendor="X.Org Foundation"
+[ 4.774] compiled for 1.20.4, module version = 0.29.0
+[ 4.774] Module class: X.Org XInput Driver
+[ 4.774] ABI class: X.Org XInput driver, version 24.1
+[ 4.774] (II) Using input driver 'libinput' for 'Power Button'
+[ 4.774] (**) Power Button: always reports core events
+[ 4.774] (**) Option "Device" "/dev/input/event2"
+[ 4.774] (**) Option "_source" "server/udev"
+[ 4.776] (II) event2 - Power Button: is tagged by udev as: Keyboard
+[ 4.776] (II) event2 - Power Button: device is a keyboard
+[ 4.776] (II) event2 - Power Button: device removed
+[ 4.796] (**) Option "config_info" "udev:/sys/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2"
+[ 4.796] (II) XINPUT: Adding extended input device "Power Button" (type: KEYBOARD, id 6)
+[ 4.796] (**) Option "xkb_model" "pc105"
+[ 4.796] (**) Option "xkb_layout" "us"
+[ 4.796] (II) event2 - Power Button: is tagged by udev as: Keyboard
+[ 4.796] (II) event2 - Power Button: device is a keyboard
+[ 4.797] (II) config/udev: Adding input device Video Bus (/dev/input/event5)
+[ 4.797] (**) Video Bus: Applying InputClass "libinput keyboard catchall"
+[ 4.797] (II) Using input driver 'libinput' for 'Video Bus'
+[ 4.797] (**) Video Bus: always reports core events
+[ 4.797] (**) Option "Device" "/dev/input/event5"
+[ 4.797] (**) Option "_source" "server/udev"
+[ 4.797] (II) event5 - Video Bus: is tagged by udev as: Keyboard
+[ 4.797] (II) event5 - Video Bus: device is a keyboard
+[ 4.797] (II) event5 - Video Bus: device removed
+[ 4.819] (**) Option "config_info" "udev:/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input7/event5"
+[ 4.819] (II) XINPUT: Adding extended input device "Video Bus" (type: KEYBOARD, id 7)
+[ 4.819] (**) Option "xkb_model" "pc105"
+[ 4.819] (**) Option "xkb_layout" "us"
+[ 4.819] (II) event5 - Video Bus: is tagged by udev as: Keyboard
+[ 4.819] (II) event5 - Video Bus: device is a keyboard
+[ 4.819] (II) config/udev: Adding input device Lid Switch (/dev/input/event1)
+[ 4.819] (II) No input driver specified, ignoring this device.
+[ 4.819] (II) This device may have been added with another device file.
+[ 4.820] (II) config/udev: Adding input device Sleep Button (/dev/input/event0)
+[ 4.820] (**) Sleep Button: Applying InputClass "libinput keyboard catchall"
+[ 4.820] (II) Using input driver 'libinput' for 'Sleep Button'
+[ 4.820] (**) Sleep Button: always reports core events
+[ 4.820] (**) Option "Device" "/dev/input/event0"
+[ 4.820] (**) Option "_source" "server/udev"
+[ 4.820] (II) event0 - Sleep Button: is tagged by udev as: Keyboard
+[ 4.820] (II) event0 - Sleep Button: device is a keyboard
+[ 4.820] (II) event0 - Sleep Button: device removed
+[ 4.834] (**) Option "config_info" "udev:/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input0/event0"
+[ 4.834] (II) XINPUT: Adding extended input device "Sleep Button" (type: KEYBOARD, id 8)
+[ 4.834] (**) Option "xkb_model" "pc105"
+[ 4.834] (**) Option "xkb_layout" "us"
+[ 4.834] (II) event0 - Sleep Button: is tagged by udev as: Keyboard
+[ 4.834] (II) event0 - Sleep Button: device is a keyboard
+[ 4.835] (II) config/udev: Adding input device serio3 (/dev/input/event19)
+[ 4.835] (**) serio3: Applying InputClass "libinput keyboard catchall"
+[ 4.835] (II) Using input driver 'libinput' for 'serio3'
+[ 4.835] (**) serio3: always reports core events
+[ 4.835] (**) Option "Device" "/dev/input/event19"
+[ 4.835] (**) Option "_source" "server/udev"
+[ 4.836] (II) event19 - serio3: is tagged by udev as: Keyboard
+[ 4.836] (II) event19 - serio3: device is a keyboard
+[ 4.836] (II) event19 - serio3: device removed
+[ 4.847] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio3/rc/rc0/input21/event19"
+[ 4.847] (II) XINPUT: Adding extended input device "serio3" (type: KEYBOARD, id 9)
+[ 4.847] (**) Option "xkb_model" "pc105"
+[ 4.847] (**) Option "xkb_layout" "us"
+[ 4.848] (II) event19 - serio3: is tagged by udev as: Keyboard
+[ 4.848] (II) event19 - serio3: device is a keyboard
+[ 4.848] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 4.848] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 4.848] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 4.848] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 4.848] (**) Option "Device" "/dev/input/event6"
+[ 4.848] (**) Option "_source" "server/udev"
+[ 4.900] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 4.900] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 4.900] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 4.915] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0001/input/input8/event6"
+[ 4.915] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 10)
+[ 4.915] (**) Option "AccelerationScheme" "none"
+[ 4.915] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 4.915] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 4.915] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 4.967] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 4.967] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 4.968] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 4.968] (II) No input driver specified, ignoring this device.
+[ 4.968] (II) This device may have been added with another device file.
+[ 4.968] (II) config/udev: Adding input device Integrated IR Camera: Integrate (/dev/input/event17)
+[ 4.968] (**) Integrated IR Camera: Integrate: Applying InputClass "libinput keyboard catchall"
+[ 4.968] (II) Using input driver 'libinput' for 'Integrated IR Camera: Integrate'
+[ 4.968] (**) Integrated IR Camera: Integrate: always reports core events
+[ 4.968] (**) Option "Device" "/dev/input/event17"
+[ 4.968] (**) Option "_source" "server/udev"
+[ 4.969] (II) event17 - Integrated IR Camera: Integrate: is tagged by udev as: Keyboard
+[ 4.969] (II) event17 - Integrated IR Camera: Integrate: device is a keyboard
+[ 4.969] (II) event17 - Integrated IR Camera: Integrate: device removed
+[ 4.985] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.0/input/input19/event17"
+[ 4.985] (II) XINPUT: Adding extended input device "Integrated IR Camera: Integrate" (type: KEYBOARD, id 11)
+[ 4.985] (**) Option "xkb_model" "pc105"
+[ 4.985] (**) Option "xkb_layout" "us"
+[ 4.985] (II) event17 - Integrated IR Camera: Integrate: is tagged by udev as: Keyboard
+[ 4.985] (II) event17 - Integrated IR Camera: Integrate: device is a keyboard
+[ 4.986] (II) config/udev: Adding input device Integrated Camera: Integrated C (/dev/input/event18)
+[ 4.986] (**) Integrated Camera: Integrated C: Applying InputClass "libinput keyboard catchall"
+[ 4.986] (II) Using input driver 'libinput' for 'Integrated Camera: Integrated C'
+[ 4.986] (**) Integrated Camera: Integrated C: always reports core events
+[ 4.986] (**) Option "Device" "/dev/input/event18"
+[ 4.986] (**) Option "_source" "server/udev"
+[ 4.986] (II) event18 - Integrated Camera: Integrated C: is tagged by udev as: Keyboard
+[ 4.987] (II) event18 - Integrated Camera: Integrated C: device is a keyboard
+[ 4.987] (II) event18 - Integrated Camera: Integrated C: device removed
+[ 5.005] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/input/input20/event18"
+[ 5.005] (II) XINPUT: Adding extended input device "Integrated Camera: Integrated C" (type: KEYBOARD, id 12)
+[ 5.005] (**) Option "xkb_model" "pc105"
+[ 5.005] (**) Option "xkb_layout" "us"
+[ 5.005] (II) event18 - Integrated Camera: Integrated C: is tagged by udev as: Keyboard
+[ 5.005] (II) event18 - Integrated Camera: Integrated C: device is a keyboard
+[ 5.006] (II) config/udev: Adding input device HDA Intel PCH Mic (/dev/input/event8)
+[ 5.006] (II) No input driver specified, ignoring this device.
+[ 5.006] (II) This device may have been added with another device file.
+[ 5.006] (II) config/udev: Adding input device HDA Intel PCH Headphone (/dev/input/event9)
+[ 5.006] (II) No input driver specified, ignoring this device.
+[ 5.006] (II) This device may have been added with another device file.
+[ 5.006] (II) config/udev: Adding input device HDA Intel PCH HDMI/DP,pcm=3 (/dev/input/event10)
+[ 5.006] (II) No input driver specified, ignoring this device.
+[ 5.006] (II) This device may have been added with another device file.
+[ 5.006] (II) config/udev: Adding input device HDA Intel PCH HDMI/DP,pcm=7 (/dev/input/event11)
+[ 5.006] (II) No input driver specified, ignoring this device.
+[ 5.006] (II) This device may have been added with another device file.
+[ 5.006] (II) config/udev: Adding input device HDA Intel PCH HDMI/DP,pcm=8 (/dev/input/event12)
+[ 5.006] (II) No input driver specified, ignoring this device.
+[ 5.006] (II) This device may have been added with another device file.
+[ 5.007] (II) config/udev: Adding input device HDA Intel PCH HDMI/DP,pcm=9 (/dev/input/event13)
+[ 5.007] (II) No input driver specified, ignoring this device.
+[ 5.007] (II) This device may have been added with another device file.
+[ 5.007] (II) config/udev: Adding input device HDA Intel PCH HDMI/DP,pcm=10 (/dev/input/event14)
+[ 5.007] (II) No input driver specified, ignoring this device.
+[ 5.007] (II) This device may have been added with another device file.
+[ 5.007] (II) config/udev: Adding input device HDA Digital PCBeep (/dev/input/event7)
+[ 5.007] (II) No input driver specified, ignoring this device.
+[ 5.007] (II) This device may have been added with another device file.
+[ 5.007] (II) config/udev: Adding input device AT Translated Set 2 keyboard (/dev/input/event4)
+[ 5.007] (**) AT Translated Set 2 keyboard: Applying InputClass "libinput keyboard catchall"
+[ 5.007] (II) Using input driver 'libinput' for 'AT Translated Set 2 keyboard'
+[ 5.007] (**) AT Translated Set 2 keyboard: always reports core events
+[ 5.007] (**) Option "Device" "/dev/input/event4"
+[ 5.007] (**) Option "_source" "server/udev"
+[ 5.008] (II) event4 - AT Translated Set 2 keyboard: is tagged by udev as: Keyboard
+[ 5.008] (II) event4 - AT Translated Set 2 keyboard: device is a keyboard
+[ 5.008] (II) event4 - AT Translated Set 2 keyboard: device removed
+[ 5.017] (**) Option "config_info" "udev:/sys/devices/platform/i8042/serio0/input/input3/event4"
+[ 5.017] (II) XINPUT: Adding extended input device "AT Translated Set 2 keyboard" (type: KEYBOARD, id 13)
+[ 5.017] (**) Option "xkb_model" "pc105"
+[ 5.017] (**) Option "xkb_layout" "us"
+[ 5.017] (II) event4 - AT Translated Set 2 keyboard: is tagged by udev as: Keyboard
+[ 5.017] (II) event4 - AT Translated Set 2 keyboard: device is a keyboard
+[ 5.018] (II) config/udev: Adding input device PC Speaker (/dev/input/event3)
+[ 5.018] (II) No input driver specified, ignoring this device.
+[ 5.018] (II) This device may have been added with another device file.
+[ 5.018] (II) config/udev: Adding input device Synaptics TM3289-021 (/dev/input/event15)
+[ 5.018] (**) Synaptics TM3289-021: Applying InputClass "libinput touchpad catchall"
+[ 5.018] (II) Using input driver 'libinput' for 'Synaptics TM3289-021'
+[ 5.018] (**) Synaptics TM3289-021: always reports core events
+[ 5.018] (**) Option "Device" "/dev/input/event15"
+[ 5.018] (**) Option "_source" "server/udev"
+[ 5.019] (II) event15 - Synaptics TM3289-021: is tagged by udev as: Touchpad
+[ 5.019] (II) event15 - Synaptics TM3289-021: device is a touchpad
+[ 5.019] (II) event15 - Synaptics TM3289-021: device removed
+[ 5.037] (**) Option "config_info" "udev:/sys/devices/rmi4-00/input/input12/event15"
+[ 5.037] (II) XINPUT: Adding extended input device "Synaptics TM3289-021" (type: TOUCHPAD, id 14)
+[ 5.037] (**) Option "AccelerationScheme" "none"
+[ 5.037] (**) Synaptics TM3289-021: (accel) selected scheme none/0
+[ 5.037] (**) Synaptics TM3289-021: (accel) acceleration factor: 2.000
+[ 5.037] (**) Synaptics TM3289-021: (accel) acceleration threshold: 4
+[ 5.038] (II) event15 - Synaptics TM3289-021: is tagged by udev as: Touchpad
+[ 5.038] (II) event15 - Synaptics TM3289-021: device is a touchpad
+[ 5.039] (II) config/udev: Adding input device Synaptics TM3289-021 (/dev/input/mouse1)
+[ 5.039] (II) No input driver specified, ignoring this device.
+[ 5.039] (II) This device may have been added with another device file.
+[ 5.039] (II) config/udev: Adding input device TPPS/2 Elan TrackPoint (/dev/input/event16)
+[ 5.039] (**) TPPS/2 Elan TrackPoint: Applying InputClass "libinput pointer catchall"
+[ 5.039] (II) Using input driver 'libinput' for 'TPPS/2 Elan TrackPoint'
+[ 5.039] (**) TPPS/2 Elan TrackPoint: always reports core events
+[ 5.039] (**) Option "Device" "/dev/input/event16"
+[ 5.039] (**) Option "_source" "server/udev"
+[ 5.040] (II) event16 - TPPS/2 Elan TrackPoint: is tagged by udev as: Mouse Pointingstick
+[ 5.040] (II) event16 - TPPS/2 Elan TrackPoint: trackpoint multiplier is 0.40
+[ 5.040] (II) event16 - TPPS/2 Elan TrackPoint: device is a pointer
+[ 5.040] (II) event16 - TPPS/2 Elan TrackPoint: device removed
+[ 5.062] (**) Option "config_info" "udev:/sys/devices/rmi4-00/rmi4-00.fn03/serio2/input/input18/event16"
+[ 5.062] (II) XINPUT: Adding extended input device "TPPS/2 Elan TrackPoint" (type: MOUSE, id 15)
+[ 5.062] (**) Option "AccelerationScheme" "none"
+[ 5.062] (**) TPPS/2 Elan TrackPoint: (accel) selected scheme none/0
+[ 5.062] (**) TPPS/2 Elan TrackPoint: (accel) acceleration factor: 2.000
+[ 5.062] (**) TPPS/2 Elan TrackPoint: (accel) acceleration threshold: 4
+[ 5.062] (II) event16 - TPPS/2 Elan TrackPoint: is tagged by udev as: Mouse Pointingstick
+[ 5.062] (II) event16 - TPPS/2 Elan TrackPoint: trackpoint multiplier is 0.40
+[ 5.063] (II) event16 - TPPS/2 Elan TrackPoint: device is a pointer
+[ 5.063] (II) config/udev: Adding input device TPPS/2 Elan TrackPoint (/dev/input/mouse2)
+[ 5.063] (II) No input driver specified, ignoring this device.
+[ 5.063] (II) This device may have been added with another device file.
+[ 5.132] (EE) Failed to open authorization file "/var/run/sddm/{f25a154d-0c5d-47a7-b540-6fc92b41d2f3}": No such file or directory
+[ 12.176] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 12.176] (II) modeset(0): Printing DDC gathered Modelines:
+[ 12.176] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 12.669] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 12.670] (II) modeset(0): Printing DDC gathered Modelines:
+[ 12.670] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 148.238] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 148.238] (II) modeset(0): Printing DDC gathered Modelines:
+[ 148.238] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 148.829] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 149.714] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 219.576] (II) config/udev: removing device serio3
+[ 219.577] (II) event19 - serio3: device removed
+[ 219.591] (II) UnloadModule: "libinput"
+[ 219.591] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 219.591] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 219.605] (II) UnloadModule: "libinput"
+[ 222.688] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 222.688] (II) No input driver specified, ignoring this device.
+[ 222.688] (II) This device may have been added with another device file.
+[ 222.770] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 222.771] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 222.771] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 222.771] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 222.771] (**) Option "Device" "/dev/input/event6"
+[ 222.771] (**) Option "_source" "server/udev"
+[ 222.824] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 222.824] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 222.824] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 222.854] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0002/input/input22/event6"
+[ 222.854] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 222.854] (**) Option "AccelerationScheme" "none"
+[ 222.854] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 222.854] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 222.854] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 222.910] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 222.910] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 222.913] (II) config/udev: Adding input device serio4 (/dev/input/event19)
+[ 222.913] (**) serio4: Applying InputClass "libinput keyboard catchall"
+[ 222.913] (II) Using input driver 'libinput' for 'serio4'
+[ 222.913] (**) serio4: always reports core events
+[ 222.913] (**) Option "Device" "/dev/input/event19"
+[ 222.913] (**) Option "_source" "server/udev"
+[ 222.917] (II) event19 - serio4: is tagged by udev as: Keyboard
+[ 222.918] (II) event19 - serio4: device is a keyboard
+[ 222.918] (II) event19 - serio4: device removed
+[ 222.933] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio4/rc/rc0/input23/event19"
+[ 222.933] (II) XINPUT: Adding extended input device "serio4" (type: KEYBOARD, id 10)
+[ 222.933] (**) Option "xkb_model" "pc105"
+[ 222.933] (**) Option "xkb_layout" "us"
+[ 222.933] (WW) Option "xkb_variant" requires a string value
+[ 222.933] (WW) Option "xkb_options" requires a string value
+[ 222.938] (II) event19 - serio4: is tagged by udev as: Keyboard
+[ 222.938] (II) event19 - serio4: device is a keyboard
+[ 268.794] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 268.794] (II) modeset(0): Printing DDC gathered Modelines:
+[ 268.794] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 269.884] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 272.592] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 272.592] (II) modeset(0): Printing DDC gathered Modelines:
+[ 272.592] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 272.927] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 273.756] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 282.930] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 282.930] (II) modeset(0): Printing DDC gathered Modelines:
+[ 282.930] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 283.262] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 307.150] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 309.524] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 309.524] (II) modeset(0): Printing DDC gathered Modelines:
+[ 309.524] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 309.860] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 310.742] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 325.273] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 325.273] (II) modeset(0): Printing DDC gathered Modelines:
+[ 325.273] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 325.607] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 361.491] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 364.630] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 364.630] (II) modeset(0): Printing DDC gathered Modelines:
+[ 364.630] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 364.965] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 365.793] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 740.130] (II) config/udev: removing device serio4
+[ 740.130] (II) event19 - serio4: device removed
+[ 740.144] (II) UnloadModule: "libinput"
+[ 740.149] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 740.149] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 740.160] (II) UnloadModule: "libinput"
+[ 767.809] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 767.809] (II) No input driver specified, ignoring this device.
+[ 767.809] (II) This device may have been added with another device file.
+[ 767.889] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 767.889] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 767.889] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 767.889] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 767.889] (**) Option "Device" "/dev/input/event6"
+[ 767.889] (**) Option "_source" "server/udev"
+[ 767.945] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 767.945] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 767.946] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 767.973] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0003/input/input24/event6"
+[ 767.973] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 767.974] (**) Option "AccelerationScheme" "none"
+[ 767.975] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 767.975] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 767.975] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 768.030] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 768.030] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 768.033] (II) config/udev: Adding input device serio5 (/dev/input/event19)
+[ 768.033] (**) serio5: Applying InputClass "libinput keyboard catchall"
+[ 768.033] (II) Using input driver 'libinput' for 'serio5'
+[ 768.033] (**) serio5: always reports core events
+[ 768.033] (**) Option "Device" "/dev/input/event19"
+[ 768.033] (**) Option "_source" "server/udev"
+[ 768.037] (II) event19 - serio5: is tagged by udev as: Keyboard
+[ 768.038] (II) event19 - serio5: device is a keyboard
+[ 768.039] (II) event19 - serio5: device removed
+[ 768.049] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio5/rc/rc0/input25/event19"
+[ 768.049] (II) XINPUT: Adding extended input device "serio5" (type: KEYBOARD, id 10)
+[ 768.049] (**) Option "xkb_model" "pc105"
+[ 768.049] (**) Option "xkb_layout" "us"
+[ 768.049] (WW) Option "xkb_variant" requires a string value
+[ 768.049] (WW) Option "xkb_options" requires a string value
+[ 768.054] (II) event19 - serio5: is tagged by udev as: Keyboard
+[ 768.054] (II) event19 - serio5: device is a keyboard
+[ 854.957] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 854.957] (II) modeset(0): Printing DDC gathered Modelines:
+[ 854.957] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 856.045] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 1518.312] (II) config/udev: removing device serio5
+[ 1518.312] (II) event19 - serio5: device removed
+[ 1518.328] (II) UnloadModule: "libinput"
+[ 1518.331] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 1518.331] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 1518.343] (II) UnloadModule: "libinput"
+[ 1524.043] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 1524.043] (II) No input driver specified, ignoring this device.
+[ 1524.043] (II) This device may have been added with another device file.
+[ 1524.122] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 1524.122] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 1524.122] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 1524.122] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 1524.122] (**) Option "Device" "/dev/input/event6"
+[ 1524.122] (**) Option "_source" "server/udev"
+[ 1524.175] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 1524.175] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 1524.176] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 1524.208] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0004/input/input26/event6"
+[ 1524.208] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 1524.209] (**) Option "AccelerationScheme" "none"
+[ 1524.210] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 1524.210] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 1524.210] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 1524.265] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 1524.265] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 1524.268] (II) config/udev: Adding input device serio6 (/dev/input/event19)
+[ 1524.268] (**) serio6: Applying InputClass "libinput keyboard catchall"
+[ 1524.268] (II) Using input driver 'libinput' for 'serio6'
+[ 1524.268] (**) serio6: always reports core events
+[ 1524.268] (**) Option "Device" "/dev/input/event19"
+[ 1524.268] (**) Option "_source" "server/udev"
+[ 1524.273] (II) event19 - serio6: is tagged by udev as: Keyboard
+[ 1524.273] (II) event19 - serio6: device is a keyboard
+[ 1524.274] (II) event19 - serio6: device removed
+[ 1524.292] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio6/rc/rc0/input27/event19"
+[ 1524.292] (II) XINPUT: Adding extended input device "serio6" (type: KEYBOARD, id 10)
+[ 1524.292] (**) Option "xkb_model" "pc105"
+[ 1524.292] (**) Option "xkb_layout" "us"
+[ 1524.292] (WW) Option "xkb_variant" requires a string value
+[ 1524.292] (WW) Option "xkb_options" requires a string value
+[ 1524.297] (II) event19 - serio6: is tagged by udev as: Keyboard
+[ 1524.297] (II) event19 - serio6: device is a keyboard
+[ 1536.974] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 1536.974] (II) modeset(0): Printing DDC gathered Modelines:
+[ 1536.974] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 1537.311] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 1538.189] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 15936.823] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 15936.823] (II) modeset(0): Printing DDC gathered Modelines:
+[ 15936.823] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 15937.913] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 67070.083] (II) config/udev: removing device serio6
+[ 67070.083] (II) event19 - serio6: device removed
+[ 67070.090] (II) UnloadModule: "libinput"
+[ 67070.100] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 67070.100] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67070.116] (II) UnloadModule: "libinput"
+[ 67074.730] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 67074.730] (II) No input driver specified, ignoring this device.
+[ 67074.730] (II) This device may have been added with another device file.
+[ 67074.811] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 67074.811] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 67074.811] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 67074.811] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 67074.811] (**) Option "Device" "/dev/input/event6"
+[ 67074.811] (**) Option "_source" "server/udev"
+[ 67074.867] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67074.867] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67074.868] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67074.890] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0005/input/input28/event6"
+[ 67074.890] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 67074.891] (**) Option "AccelerationScheme" "none"
+[ 67074.892] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 67074.892] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 67074.892] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 67074.947] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67074.948] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67095.126] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 67095.126] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67095.138] (II) UnloadModule: "libinput"
+[ 67100.773] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 67100.773] (II) No input driver specified, ignoring this device.
+[ 67100.773] (II) This device may have been added with another device file.
+[ 67100.854] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 67100.854] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 67100.854] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 67100.854] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 67100.854] (**) Option "Device" "/dev/input/event6"
+[ 67100.854] (**) Option "_source" "server/udev"
+[ 67100.907] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67100.907] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67100.907] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67100.926] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0006/input/input30/event6"
+[ 67100.926] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 67100.926] (**) Option "AccelerationScheme" "none"
+[ 67100.927] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 67100.927] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 67100.927] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 67100.982] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67100.982] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67100.985] (II) config/udev: Adding input device serio8 (/dev/input/event19)
+[ 67100.985] (**) serio8: Applying InputClass "libinput keyboard catchall"
+[ 67100.985] (II) Using input driver 'libinput' for 'serio8'
+[ 67100.985] (**) serio8: always reports core events
+[ 67100.985] (**) Option "Device" "/dev/input/event19"
+[ 67100.985] (**) Option "_source" "server/udev"
+[ 67100.990] (II) event19 - serio8: is tagged by udev as: Keyboard
+[ 67100.990] (II) event19 - serio8: device is a keyboard
+[ 67100.991] (II) event19 - serio8: device removed
+[ 67101.001] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio8/rc/rc0/input31/event19"
+[ 67101.001] (II) XINPUT: Adding extended input device "serio8" (type: KEYBOARD, id 10)
+[ 67101.001] (**) Option "xkb_model" "pc105"
+[ 67101.001] (**) Option "xkb_layout" "us"
+[ 67101.001] (WW) Option "xkb_variant" requires a string value
+[ 67101.001] (WW) Option "xkb_options" requires a string value
+[ 67101.006] (II) event19 - serio8: is tagged by udev as: Keyboard
+[ 67101.006] (II) event19 - serio8: device is a keyboard
+[ 67127.966] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 67127.966] (II) modeset(0): Printing DDC gathered Modelines:
+[ 67127.966] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 67128.302] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 67129.177] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 67359.952] (II) config/udev: removing device Integrated Camera: Integrated C
+[ 67359.952] (II) event18 - Integrated Camera: Integrated C: device removed
+[ 67359.965] (II) UnloadModule: "libinput"
+[ 67360.066] (II) config/udev: removing device Integrated IR Camera: Integrate
+[ 67360.066] (II) event17 - Integrated IR Camera: Integrate: device removed
+[ 67360.078] (II) UnloadModule: "libinput"
+[ 67360.125] (II) config/udev: removing device serio8
+[ 67360.125] (II) event19 - serio8: device removed
+[ 67360.140] (II) UnloadModule: "libinput"
+[ 67374.717] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 67374.717] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67374.729] (II) UnloadModule: "libinput"
+[ 67379.434] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 67379.434] (II) No input driver specified, ignoring this device.
+[ 67379.434] (II) This device may have been added with another device file.
+[ 67379.514] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 67379.514] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 67379.515] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 67379.515] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 67379.515] (**) Option "Device" "/dev/input/event6"
+[ 67379.515] (**) Option "_source" "server/udev"
+[ 67379.568] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67379.569] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67379.569] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67379.590] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0007/input/input32/event6"
+[ 67379.590] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 67379.590] (**) Option "AccelerationScheme" "none"
+[ 67379.591] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 67379.591] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 67379.591] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 67379.646] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67379.646] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67379.649] (II) config/udev: Adding input device serio9 (/dev/input/event17)
+[ 67379.649] (**) serio9: Applying InputClass "libinput keyboard catchall"
+[ 67379.649] (II) Using input driver 'libinput' for 'serio9'
+[ 67379.649] (**) serio9: always reports core events
+[ 67379.649] (**) Option "Device" "/dev/input/event17"
+[ 67379.649] (**) Option "_source" "server/udev"
+[ 67379.654] (II) event17 - serio9: is tagged by udev as: Keyboard
+[ 67379.654] (II) event17 - serio9: device is a keyboard
+[ 67379.655] (II) event17 - serio9: device removed
+[ 67379.672] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio9/rc/rc0/input33/event17"
+[ 67379.672] (II) XINPUT: Adding extended input device "serio9" (type: KEYBOARD, id 10)
+[ 67379.672] (**) Option "xkb_model" "pc105"
+[ 67379.672] (**) Option "xkb_layout" "us"
+[ 67379.672] (WW) Option "xkb_variant" requires a string value
+[ 67379.672] (WW) Option "xkb_options" requires a string value
+[ 67379.677] (II) event17 - serio9: is tagged by udev as: Keyboard
+[ 67379.677] (II) event17 - serio9: device is a keyboard
+[ 67466.307] (II) config/udev: removing device serio9
+[ 67466.307] (II) event17 - serio9: device removed
+[ 67466.313] (II) UnloadModule: "libinput"
+[ 67466.323] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 67466.323] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67466.335] (II) UnloadModule: "libinput"
+[ 67489.814] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 67489.814] (II) No input driver specified, ignoring this device.
+[ 67489.814] (II) This device may have been added with another device file.
+[ 67489.894] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 67489.895] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 67489.895] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 67489.895] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 67489.895] (**) Option "Device" "/dev/input/event6"
+[ 67489.895] (**) Option "_source" "server/udev"
+[ 67489.950] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67489.950] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67489.951] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 67489.974] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0008/input/input34/event6"
+[ 67489.974] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 67489.975] (**) Option "AccelerationScheme" "none"
+[ 67489.976] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 67489.976] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 67489.976] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 67490.031] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 67490.031] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 67490.034] (II) config/udev: Adding input device serio10 (/dev/input/event17)
+[ 67490.034] (**) serio10: Applying InputClass "libinput keyboard catchall"
+[ 67490.034] (II) Using input driver 'libinput' for 'serio10'
+[ 67490.034] (**) serio10: always reports core events
+[ 67490.034] (**) Option "Device" "/dev/input/event17"
+[ 67490.034] (**) Option "_source" "server/udev"
+[ 67490.039] (II) event17 - serio10: is tagged by udev as: Keyboard
+[ 67490.039] (II) event17 - serio10: device is a keyboard
+[ 67490.040] (II) event17 - serio10: device removed
+[ 67490.051] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio10/rc/rc0/input35/event17"
+[ 67490.051] (II) XINPUT: Adding extended input device "serio10" (type: KEYBOARD, id 10)
+[ 67490.051] (**) Option "xkb_model" "pc105"
+[ 67490.051] (**) Option "xkb_layout" "us"
+[ 67490.051] (WW) Option "xkb_variant" requires a string value
+[ 67490.051] (WW) Option "xkb_options" requires a string value
+[ 67490.056] (II) event17 - serio10: is tagged by udev as: Keyboard
+[ 67490.056] (II) event17 - serio10: device is a keyboard
+[ 67517.542] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 67517.542] (II) modeset(0): Printing DDC gathered Modelines:
+[ 67517.542] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 67518.681] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 74816.397] (II) config/udev: removing device serio10
+[ 74816.398] (II) event17 - serio10: device removed
+[ 74816.415] (II) UnloadModule: "libinput"
+[ 74816.420] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 74816.420] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 74816.432] (II) UnloadModule: "libinput"
+[ 74820.247] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 74820.247] (II) No input driver specified, ignoring this device.
+[ 74820.247] (II) This device may have been added with another device file.
+[ 74820.328] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 74820.328] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 74820.328] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 74820.328] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 74820.328] (**) Option "Device" "/dev/input/event6"
+[ 74820.328] (**) Option "_source" "server/udev"
+[ 74820.384] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 74820.385] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 74820.386] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 74820.408] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.0009/input/input36/event6"
+[ 74820.408] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 74820.408] (**) Option "AccelerationScheme" "none"
+[ 74820.409] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 74820.409] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 74820.409] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 74820.462] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 74820.463] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 74820.465] (II) config/udev: Adding input device serio11 (/dev/input/event17)
+[ 74820.465] (**) serio11: Applying InputClass "libinput keyboard catchall"
+[ 74820.465] (II) Using input driver 'libinput' for 'serio11'
+[ 74820.465] (**) serio11: always reports core events
+[ 74820.465] (**) Option "Device" "/dev/input/event17"
+[ 74820.465] (**) Option "_source" "server/udev"
+[ 74820.468] (II) event17 - serio11: is tagged by udev as: Keyboard
+[ 74820.468] (II) event17 - serio11: device is a keyboard
+[ 74820.469] (II) event17 - serio11: device removed
+[ 74820.484] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio11/rc/rc0/input37/event17"
+[ 74820.484] (II) XINPUT: Adding extended input device "serio11" (type: KEYBOARD, id 10)
+[ 74820.484] (**) Option "xkb_model" "pc105"
+[ 74820.484] (**) Option "xkb_layout" "us"
+[ 74820.484] (WW) Option "xkb_variant" requires a string value
+[ 74820.484] (WW) Option "xkb_options" requires a string value
+[ 74820.489] (II) event17 - serio11: is tagged by udev as: Keyboard
+[ 74820.489] (II) event17 - serio11: device is a keyboard
+[ 74844.099] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 74844.099] (II) modeset(0): Printing DDC gathered Modelines:
+[ 74844.099] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 74844.434] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 74845.241] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 75427.645] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 75427.645] (II) modeset(0): Printing DDC gathered Modelines:
+[ 75427.645] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 75428.764] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 75434.846] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 75434.846] (II) modeset(0): Printing DDC gathered Modelines:
+[ 75434.846] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 75435.181] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 75436.047] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 75439.476] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 75439.476] (II) modeset(0): Printing DDC gathered Modelines:
+[ 75439.476] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 75439.809] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 75480.334] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 75486.077] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 75486.077] (II) modeset(0): Printing DDC gathered Modelines:
+[ 75486.077] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 75486.414] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 75487.224] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76141.668] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76141.668] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76141.668] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76142.772] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76145.989] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76145.989] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76145.989] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76146.324] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76147.204] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76181.810] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76181.811] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76181.811] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76182.899] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76195.390] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76195.390] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76195.390] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76195.726] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76196.600] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76204.061] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76204.061] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76204.061] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76205.167] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76213.216] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76213.216] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76213.216] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76213.552] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76214.382] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76221.922] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76221.922] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76221.922] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76222.989] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76231.080] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76231.080] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76231.080] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76231.415] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76232.234] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76242.944] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76242.944] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76242.944] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76244.041] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76252.117] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76252.117] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76252.117] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76252.453] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76253.267] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76259.170] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76259.170] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76259.170] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76260.333] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76268.331] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76268.331] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76268.331] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76268.666] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76269.545] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76275.429] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76275.429] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76275.429] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76276.517] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76284.585] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76284.585] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76284.585] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76284.921] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76285.767] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76362.635] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76362.635] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76362.635] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76363.754] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 76777.916] (II) config/udev: removing device serio11
+[ 76777.916] (II) event17 - serio11: device removed
+[ 76777.926] (II) UnloadModule: "libinput"
+[ 76777.928] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 76777.929] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 76777.944] (II) UnloadModule: "libinput"
+[ 76791.743] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 76791.743] (II) No input driver specified, ignoring this device.
+[ 76791.743] (II) This device may have been added with another device file.
+[ 76791.824] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 76791.824] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 76791.824] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 76791.824] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 76791.824] (**) Option "Device" "/dev/input/event6"
+[ 76791.824] (**) Option "_source" "server/udev"
+[ 76791.880] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 76791.880] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 76791.881] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 76791.904] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.000A/input/input38/event6"
+[ 76791.904] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 76791.905] (**) Option "AccelerationScheme" "none"
+[ 76791.906] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 76791.906] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 76791.906] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 76791.961] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 76791.961] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 76791.964] (II) config/udev: Adding input device serio12 (/dev/input/event17)
+[ 76791.964] (**) serio12: Applying InputClass "libinput keyboard catchall"
+[ 76791.964] (II) Using input driver 'libinput' for 'serio12'
+[ 76791.964] (**) serio12: always reports core events
+[ 76791.964] (**) Option "Device" "/dev/input/event17"
+[ 76791.964] (**) Option "_source" "server/udev"
+[ 76791.969] (II) event17 - serio12: is tagged by udev as: Keyboard
+[ 76791.969] (II) event17 - serio12: device is a keyboard
+[ 76791.970] (II) event17 - serio12: device removed
+[ 76791.988] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio12/rc/rc0/input39/event17"
+[ 76791.988] (II) XINPUT: Adding extended input device "serio12" (type: KEYBOARD, id 10)
+[ 76791.988] (**) Option "xkb_model" "pc105"
+[ 76791.988] (**) Option "xkb_layout" "us"
+[ 76791.988] (WW) Option "xkb_variant" requires a string value
+[ 76791.988] (WW) Option "xkb_options" requires a string value
+[ 76791.993] (II) event17 - serio12: is tagged by udev as: Keyboard
+[ 76791.993] (II) event17 - serio12: device is a keyboard
+[ 76823.321] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76823.321] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76823.321] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76823.656] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 76824.514] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 76830.683] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 76830.683] (II) modeset(0): Printing DDC gathered Modelines:
+[ 76830.683] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 76831.804] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 77846.982] (II) config/udev: removing device serio12
+[ 77846.982] (II) event17 - serio12: device removed
+[ 77846.997] (II) UnloadModule: "libinput"
+[ 77849.045] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 77849.045] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 77849.066] (II) UnloadModule: "libinput"
+[ 77854.150] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 77854.150] (II) No input driver specified, ignoring this device.
+[ 77854.150] (II) This device may have been added with another device file.
+[ 77854.231] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 77854.231] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 77854.231] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 77854.231] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 77854.231] (**) Option "Device" "/dev/input/event6"
+[ 77854.231] (**) Option "_source" "server/udev"
+[ 77854.286] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 77854.286] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 77854.287] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 77854.308] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.000B/input/input40/event6"
+[ 77854.308] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 77854.309] (**) Option "AccelerationScheme" "none"
+[ 77854.309] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 77854.309] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 77854.309] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 77854.362] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 77854.362] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 77854.364] (II) config/udev: Adding input device serio13 (/dev/input/event17)
+[ 77854.364] (**) serio13: Applying InputClass "libinput keyboard catchall"
+[ 77854.364] (II) Using input driver 'libinput' for 'serio13'
+[ 77854.364] (**) serio13: always reports core events
+[ 77854.364] (**) Option "Device" "/dev/input/event17"
+[ 77854.364] (**) Option "_source" "server/udev"
+[ 77854.366] (II) event17 - serio13: is tagged by udev as: Keyboard
+[ 77854.366] (II) event17 - serio13: device is a keyboard
+[ 77854.366] (II) event17 - serio13: device removed
+[ 77854.375] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio13/rc/rc0/input41/event17"
+[ 77854.375] (II) XINPUT: Adding extended input device "serio13" (type: KEYBOARD, id 10)
+[ 77854.375] (**) Option "xkb_model" "pc105"
+[ 77854.375] (**) Option "xkb_layout" "us"
+[ 77854.375] (WW) Option "xkb_variant" requires a string value
+[ 77854.375] (WW) Option "xkb_options" requires a string value
+[ 77854.377] (II) event17 - serio13: is tagged by udev as: Keyboard
+[ 77854.378] (II) event17 - serio13: device is a keyboard
+[ 77875.697] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 77875.697] (II) modeset(0): Printing DDC gathered Modelines:
+[ 77875.697] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 77876.295] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 77877.173] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 77883.094] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 77883.094] (II) modeset(0): Printing DDC gathered Modelines:
+[ 77883.094] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 77884.250] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 77892.255] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 77892.255] (II) modeset(0): Printing DDC gathered Modelines:
+[ 77892.255] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 77892.589] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 77893.419] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 77899.589] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 77899.589] (II) modeset(0): Printing DDC gathered Modelines:
+[ 77899.589] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 77900.717] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 77963.554] (II) config/udev: removing device serio13
+[ 77963.554] (II) event17 - serio13: device removed
+[ 77963.565] (II) UnloadModule: "libinput"
+[ 77970.162] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 77970.162] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 77970.174] (II) UnloadModule: "libinput"
+[ 77973.982] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 77973.982] (II) No input driver specified, ignoring this device.
+[ 77973.982] (II) This device may have been added with another device file.
+[ 77974.061] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 77974.061] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 77974.061] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 77974.061] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 77974.061] (**) Option "Device" "/dev/input/event6"
+[ 77974.061] (**) Option "_source" "server/udev"
+[ 77974.116] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 77974.116] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 77974.117] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 77974.134] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.000C/input/input42/event6"
+[ 77974.134] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 77974.135] (**) Option "AccelerationScheme" "none"
+[ 77974.136] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 77974.136] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 77974.136] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 77974.191] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 77974.191] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 77974.194] (II) config/udev: Adding input device serio14 (/dev/input/event17)
+[ 77974.194] (**) serio14: Applying InputClass "libinput keyboard catchall"
+[ 77974.194] (II) Using input driver 'libinput' for 'serio14'
+[ 77974.194] (**) serio14: always reports core events
+[ 77974.194] (**) Option "Device" "/dev/input/event17"
+[ 77974.194] (**) Option "_source" "server/udev"
+[ 77974.199] (II) event17 - serio14: is tagged by udev as: Keyboard
+[ 77974.199] (II) event17 - serio14: device is a keyboard
+[ 77974.200] (II) event17 - serio14: device removed
+[ 77974.211] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio14/rc/rc0/input43/event17"
+[ 77974.211] (II) XINPUT: Adding extended input device "serio14" (type: KEYBOARD, id 10)
+[ 77974.211] (**) Option "xkb_model" "pc105"
+[ 77974.211] (**) Option "xkb_layout" "us"
+[ 77974.211] (WW) Option "xkb_variant" requires a string value
+[ 77974.211] (WW) Option "xkb_options" requires a string value
+[ 77974.216] (II) event17 - serio14: is tagged by udev as: Keyboard
+[ 77974.216] (II) event17 - serio14: device is a keyboard
+[ 77993.113] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 77993.113] (II) modeset(0): Printing DDC gathered Modelines:
+[ 77993.113] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 77993.450] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 77994.265] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78084.447] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78084.447] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78084.447] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78085.516] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78098.033] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78098.033] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78098.033] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78098.368] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78099.229] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78192.777] (II) config/udev: removing device serio14
+[ 78192.777] (II) event17 - serio14: device removed
+[ 78192.794] (II) UnloadModule: "libinput"
+[ 78197.693] (II) config/udev: removing device Pulse-Eight CEC Adapter
+[ 78197.718] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 78197.739] (II) UnloadModule: "libinput"
+[ 78201.274] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/mouse0)
+[ 78201.274] (II) No input driver specified, ignoring this device.
+[ 78201.274] (II) This device may have been added with another device file.
+[ 78201.354] (II) config/udev: Adding input device Pulse-Eight CEC Adapter (/dev/input/event6)
+[ 78201.354] (**) Pulse-Eight CEC Adapter: Applying InputClass "libinput pointer catchall"
+[ 78201.354] (II) Using input driver 'libinput' for 'Pulse-Eight CEC Adapter'
+[ 78201.354] (**) Pulse-Eight CEC Adapter: always reports core events
+[ 78201.354] (**) Option "Device" "/dev/input/event6"
+[ 78201.354] (**) Option "_source" "server/udev"
+[ 78201.410] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 78201.410] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 78201.411] (II) event6 - Pulse-Eight CEC Adapter: device removed
+[ 78201.437] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.2/0003:2548:1002.000D/input/input44/event6"
+[ 78201.437] (II) XINPUT: Adding extended input device "Pulse-Eight CEC Adapter" (type: MOUSE, id 9)
+[ 78201.438] (**) Option "AccelerationScheme" "none"
+[ 78201.439] (**) Pulse-Eight CEC Adapter: (accel) selected scheme none/0
+[ 78201.439] (**) Pulse-Eight CEC Adapter: (accel) acceleration factor: 2.000
+[ 78201.439] (**) Pulse-Eight CEC Adapter: (accel) acceleration threshold: 4
+[ 78201.494] (II) event6 - Pulse-Eight CEC Adapter: is tagged by udev as: Mouse
+[ 78201.494] (II) event6 - Pulse-Eight CEC Adapter: device is a pointer
+[ 78201.497] (II) config/udev: Adding input device serio15 (/dev/input/event17)
+[ 78201.497] (**) serio15: Applying InputClass "libinput keyboard catchall"
+[ 78201.497] (II) Using input driver 'libinput' for 'serio15'
+[ 78201.497] (**) serio15: always reports core events
+[ 78201.497] (**) Option "Device" "/dev/input/event17"
+[ 78201.497] (**) Option "_source" "server/udev"
+[ 78201.502] (II) event17 - serio15: is tagged by udev as: Keyboard
+[ 78201.502] (II) event17 - serio15: device is a keyboard
+[ 78201.503] (II) event17 - serio15: device removed
+[ 78201.517] (**) Option "config_info" "udev:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/tty/ttyACM0/serio15/rc/rc0/input45/event17"
+[ 78201.517] (II) XINPUT: Adding extended input device "serio15" (type: KEYBOARD, id 10)
+[ 78201.517] (**) Option "xkb_model" "pc105"
+[ 78201.517] (**) Option "xkb_layout" "us"
+[ 78201.517] (WW) Option "xkb_variant" requires a string value
+[ 78201.517] (WW) Option "xkb_options" requires a string value
+[ 78201.522] (II) event17 - serio15: is tagged by udev as: Keyboard
+[ 78201.522] (II) event17 - serio15: device is a keyboard
+[ 78213.346] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78213.346] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78213.346] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78214.434] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78226.951] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78226.951] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78226.951] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78227.287] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78228.166] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78277.188] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78277.188] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78277.188] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78278.321] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78286.362] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78286.362] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78286.362] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78286.697] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78287.575] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78295.348] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78295.348] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78295.348] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78296.435] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78304.505] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78304.505] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78304.505] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78304.840] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78305.700] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78352.203] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78352.203] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78352.203] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78353.314] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78361.371] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78361.371] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78361.371] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78361.706] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78362.580] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78368.424] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78368.424] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78368.424] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78369.512] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78377.605] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78377.605] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78377.605] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78377.941] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78378.771] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78384.669] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78384.669] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78384.669] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78385.788] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78394.096] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78394.096] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78394.096] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78394.690] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78395.570] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78444.790] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78444.790] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78444.790] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78445.880] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78453.945] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78453.945] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78453.945] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78454.281] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78455.157] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78464.542] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78464.542] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78464.542] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78465.678] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78473.963] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78473.963] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78473.963] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78474.299] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78475.176] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78525.923] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78525.923] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78525.923] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78527.030] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78535.100] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78535.100] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78535.100] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78535.436] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78536.241] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78548.356] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78548.356] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78548.356] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78549.443] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78561.941] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78561.941] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78561.941] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78562.276] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78563.155] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78574.565] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78574.565] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78574.565] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78575.706] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78588.167] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78588.167] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78588.167] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78588.503] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78589.308] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78641.884] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78641.884] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78641.884] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78642.953] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78655.433] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78655.433] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78655.433] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78655.768] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78656.628] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78706.340] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78706.340] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78706.340] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78707.427] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78720.183] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78720.183] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78720.183] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78720.776] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78721.633] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78733.986] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78733.986] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78733.986] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78735.098] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78747.830] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78747.830] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78747.830] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78748.166] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78749.028] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78765.426] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78765.426] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78765.426] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78766.517] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78779.033] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78779.033] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78779.033] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78779.370] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78780.241] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78833.447] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78833.447] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78833.447] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78834.517] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78851.444] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78851.444] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78851.444] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78851.780] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78852.640] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78869.513] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78869.513] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78869.513] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78870.652] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78887.543] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78887.543] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78887.543] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78887.879] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78888.739] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 78936.478] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78936.478] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78936.478] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78937.596] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 78954.489] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 78954.489] (II) modeset(0): Printing DDC gathered Modelines:
+[ 78954.489] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 78954.825] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 78955.684] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79147.422] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79147.422] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79147.422] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79148.512] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79161.002] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79161.002] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79161.002] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79161.337] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79162.216] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79168.120] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79168.120] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79168.120] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79169.249] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79177.275] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79177.275] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79177.275] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79177.610] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79178.427] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79184.695] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79184.695] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79184.695] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79185.827] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79193.869] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79193.869] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79193.869] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79194.207] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79195.068] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79205.712] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79205.712] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79205.712] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79206.827] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79214.870] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79214.870] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79214.870] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79215.205] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79216.064] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79227.200] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79227.200] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79227.200] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79228.316] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79236.354] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79236.354] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79236.354] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79236.689] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79237.562] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79248.981] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79248.981] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79248.981] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79250.115] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79258.142] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79258.142] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79258.142] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79258.476] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79259.281] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79308.283] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79308.283] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79308.283] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79309.351] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79317.457] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79317.457] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79317.457] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79317.793] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79318.659] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79326.944] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79326.944] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79326.944] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79328.034] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79336.108] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79336.108] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79336.108] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79336.444] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79337.271] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79386.976] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79386.976] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79386.976] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79388.115] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79396.138] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79396.138] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79396.138] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79396.473] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79397.282] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79449.733] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79449.733] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79449.733] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79450.827] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79459.162] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79459.162] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79459.162] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79459.497] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79460.316] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79510.506] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79510.506] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79510.506] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79511.646] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79524.086] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79524.086] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79524.086] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79524.421] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79525.239] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79573.820] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79573.820] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79573.821] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79574.909] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79587.400] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79587.400] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79587.400] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79587.736] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79588.617] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79637.896] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79637.896] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79637.896] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79638.965] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79651.477] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79651.477] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79651.477] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79651.812] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79652.666] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79703.092] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79703.092] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79703.092] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79704.249] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79716.657] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79716.657] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79716.657] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79717.250] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79718.115] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79766.974] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79766.974] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79766.974] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79768.115] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79780.576] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79780.576] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79780.576] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79780.912] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79781.743] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79796.010] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79796.010] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79796.010] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79797.131] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79814.279] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79814.279] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79814.279] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79814.614] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79815.428] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79834.672] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79834.672] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79834.672] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79835.791] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79852.685] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79852.685] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79852.685] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79853.021] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79853.830] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79901.975] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79901.975] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79901.975] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79903.099] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79920.246] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79920.246] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79920.246] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79920.581] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79921.409] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79928.145] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79928.145] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79928.145] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79929.287] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[ 79955.264] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79955.264] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79955.264] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79955.599] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[ 79956.425] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[ 79964.166] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[ 79964.166] (II) modeset(0): Printing DDC gathered Modelines:
+[ 79964.166] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[ 79965.303] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[244829.768] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[244829.768] (II) modeset(0): Printing DDC gathered Modelines:
+[244829.768] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[244854.926] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[244854.926] (II) modeset(0): Printing DDC gathered Modelines:
+[244854.926] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[244862.679] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[244862.679] (II) modeset(0): Printing DDC gathered Modelines:
+[244862.679] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[251356.090] (EE) event15 - Synaptics TM3289-021: kernel bug: Touch jump detected and discarded.
+See https://wayland.freedesktop.org/libinput/doc/1.14.3/touchpad-jumping-cursors.html for details
+[251356.764] (EE) event15 - Synaptics TM3289-021: kernel bug: Touch jump detected and discarded.
+See https://wayland.freedesktop.org/libinput/doc/1.14.3/touchpad-jumping-cursors.html for details
+[251381.276] (EE) event15 - Synaptics TM3289-021: kernel bug: Touch jump detected and discarded.
+See https://wayland.freedesktop.org/libinput/doc/1.14.3/touchpad-jumping-cursors.html for details
+[251381.334] (EE) event15 - Synaptics TM3289-021: kernel bug: Touch jump detected and discarded.
+See https://wayland.freedesktop.org/libinput/doc/1.14.3/touchpad-jumping-cursors.html for details
+[256669.045] (EE) event15 - Synaptics TM3289-021: kernel bug: Touch jump detected and discarded.
+See https://wayland.freedesktop.org/libinput/doc/1.14.3/touchpad-jumping-cursors.html for details
+[259363.543] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[259363.543] (II) modeset(0): Printing DDC gathered Modelines:
+[259363.543] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[259363.878] (--) modeset(0): HDMI max TMDS frequency 300000KHz
+[259368.816] (II) modeset(0): Allocate new frame buffer 5760x2160 stride
+[259466.484] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[259466.484] (II) modeset(0): Printing DDC gathered Modelines:
+[259466.484] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[259467.567] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
+[259508.601] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[259508.601] (II) modeset(0): Printing DDC gathered Modelines:
+[259508.601] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[259508.936] (--) modeset(0): HDMI max TMDS frequency 150000KHz
+[259509.763] (II) modeset(0): Allocate new frame buffer 3840x1080 stride
+[273910.770] (II) modeset(0): EDID vendor "AUO", prod id 12605
+[273910.770] (II) modeset(0): Printing DDC gathered Modelines:
+[273910.770] (II) modeset(0): Modeline "1920x1080"x0.0 141.00 1920 1936 1952 2104 1080 1083 1097 1116 -hsync -vsync (67.0 kHz eP)
+[273911.866] (II) modeset(0): Allocate new frame buffer 1920x1080 stride
diff --git a/test/xrandr.test b/test/xrandr.test
new file mode 100644
index 0000000..ba90e3a
--- /dev/null
+++ b/test/xrandr.test
@@ -0,0 +1,279 @@
+Screen 0: minimum 8 x 8, current 3840 x 2160, maximum 16384 x 16384
+DVI-I-0 disconnected (normal left inverted right x axis y axis)
+ Identifier: 0x1be
+ Timestamp: 99649
+ Subpixel: unknown
+ Clones:
+ CRTCs: 0 1 2 3
+ Transform: 1.000000 0.000000 0.000000
+ 0.000000 1.000000 0.000000
+ 0.000000 0.000000 1.000000
+ filter:
+ CscMatrix: 65536 0 0 0 0 65536 0 0 0 0 65536 0
+ BorderDimensions: 4
+ supported: 4
+ Border: 0 0 0 0
+ range: (0, 65535)
+ SignalFormat: VGA
+ supported: VGA
+ ConnectorType: DVI-I
+ ConnectorNumber: 0
+ _ConnectorLocation: 0
+ non-desktop: 0
+ supported: 0, 1
+DVI-I-1 disconnected (normal left inverted right x axis y axis)
+ Identifier: 0x1bf
+ Timestamp: 99649
+ Subpixel: unknown
+ Clones:
+ CRTCs: 0 1 2 3
+ Transform: 1.000000 0.000000 0.000000
+ 0.000000 1.000000 0.000000
+ 0.000000 0.000000 1.000000
+ filter:
+ CscMatrix: 65536 0 0 0 0 65536 0 0 0 0 65536 0
+ BorderDimensions: 4
+ supported: 4
+ Border: 0 0 0 0
+ range: (0, 65535)
+ SignalFormat: TMDS
+ supported: TMDS
+ ConnectorType: DVI-I
+ ConnectorNumber: 0
+ _ConnectorLocation: 0
+ non-desktop: 0
+ supported: 0, 1
+DP-0 disconnected (normal left inverted right x axis y axis)
+ Identifier: 0x1c0
+ Timestamp: 99649
+ Subpixel: unknown
+ Clones:
+ CRTCs: 0 1 2 3
+ Transform: 1.000000 0.000000 0.000000
+ 0.000000 1.000000 0.000000
+ 0.000000 0.000000 1.000000
+ filter:
+ CscMatrix: 65536 0 0 0 0 65536 0 0 0 0 65536 0
+ BorderDimensions: 4
+ supported: 4
+ Border: 0 0 0 0
+ range: (0, 65535)
+ SignalFormat: DisplayPort
+ supported: DisplayPort
+ ConnectorType: DisplayPort
+ ConnectorNumber: 1
+ _ConnectorLocation: 1
+ non-desktop: 0
+ supported: 0, 1
+DP-1 disconnected (normal left inverted right x axis y axis)
+ Identifier: 0x1c1
+ Timestamp: 99649
+ Subpixel: unknown
+ Clones:
+ CRTCs: 0 1 2 3
+ Transform: 1.000000 0.000000 0.000000
+ 0.000000 1.000000 0.000000
+ 0.000000 0.000000 1.000000
+ filter:
+ CscMatrix: 65536 0 0 0 0 65536 0 0 0 0 65536 0
+ BorderDimensions: 4
+ supported: 4
+ Border: 0 0 0 0
+ range: (0, 65535)
+ SignalFormat: TMDS
+ supported: TMDS
+ ConnectorType: DisplayPort
+ ConnectorNumber: 1
+ _ConnectorLocation: 1
+ non-desktop: 0
+ supported: 0, 1
+HDMI-0 connected primary 3840x2160+0+0 (0x1c3) normal (normal left inverted right x axis y axis) 1872mm x 1053mm
+ Identifier: 0x1c2
+ Timestamp: 99649
+ Subpixel: unknown
+ Gamma: 1.0:1.0:1.0
+ Brightness: 1.0
+ Clones:
+ CRTC: 0
+ CRTCs: 0 1 2 3
+ Transform: 1.000000 0.000000 0.000000
+ 0.000000 1.000000 0.000000
+ 0.000000 0.000000 1.000000
+ filter:
+ CscMatrix: 65536 0 0 0 0 65536 0 0 0 0 65536 0
+ EDID:
+ 00ffffffffffff004c2d010c01060001
+ 2a18010380693b780a23ada4544d9926
+ 0f474abdef80714f81c0810081809500
+ a9c0b300010108e80030f2705a80b058
+ 8a00501d7400001e023a801871382d40
+ 582c4500501d7400001e000000fd0018
+ 4b0f873c000a202020202020000000fc
+ 0053414d53554e470a20202020200156
+ 020358f15761101f041305142021225d
+ 5e5f6065666263640716031229090707
+ 1507503d04c083010000e2000fe305c0
+ 0076030c001000b83c21d08801020304
+ 01403fff5060809067d85dc401788003
+ e3060501e30f01e0011d80d0721c1620
+ 102c2580501d7400009e662156aa5100
+ 1e30468f3300501d7400001e000000a6
+ BorderDimensions: 4
+ supported: 4
+ Border: 0 0 0 0
+ range: (0, 65535)
+ SignalFormat: TMDS
+ supported: TMDS
+ ConnectorType: HDMI
+ ConnectorNumber: 2
+ _ConnectorLocation: 2
+ non-desktop: 0
+ supported: 0, 1
+ 3840x2160 (0x1c3) 594.000MHz +HSync +VSync *current +preferred
+ h: width 3840 start 4016 end 4104 total 4400 skew 0 clock 135.00KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 60.00Hz
+ 4096x2160 (0x1c4) 593.410MHz +HSync +VSync
+ h: width 4096 start 4184 end 4272 total 4400 skew 0 clock 134.87KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 59.94Hz
+ 4096x2160 (0x1c5) 594.000MHz +HSync +VSync
+ h: width 4096 start 5064 end 5152 total 5280 skew 0 clock 112.50KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 50.00Hz
+ 4096x2160 (0x1c6) 296.700MHz +HSync +VSync
+ h: width 4096 start 4184 end 4272 total 4400 skew 0 clock 67.43KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 29.97Hz
+ 4096x2160 (0x1c7) 297.000MHz +HSync +VSync
+ h: width 4096 start 5064 end 5152 total 5280 skew 0 clock 56.25KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 25.00Hz
+ 4096x2160 (0x1c8) 297.000MHz +HSync +VSync
+ h: width 4096 start 5116 end 5204 total 5500 skew 0 clock 54.00KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 24.00Hz
+ 4096x2160 (0x1c9) 296.750MHz +HSync +VSync
+ h: width 4096 start 5116 end 5204 total 5500 skew 0 clock 53.95KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 23.98Hz
+ 3840x2160 (0x1ca) 593.410MHz +HSync +VSync
+ h: width 3840 start 4016 end 4104 total 4400 skew 0 clock 134.87KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 59.94Hz
+ 3840x2160 (0x1cb) 594.000MHz +HSync +VSync
+ h: width 3840 start 4896 end 4984 total 5280 skew 0 clock 112.50KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 50.00Hz
+ 3840x2160 (0x1cc) 296.700MHz +HSync +VSync
+ h: width 3840 start 4016 end 4104 total 4400 skew 0 clock 67.43KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 29.97Hz
+ 3840x2160 (0x1cd) 297.000MHz +HSync +VSync
+ h: width 3840 start 4896 end 4984 total 5280 skew 0 clock 56.25KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 25.00Hz
+ 3840x2160 (0x1ce) 296.750MHz +HSync +VSync
+ h: width 3840 start 5116 end 5204 total 5500 skew 0 clock 53.95KHz
+ v: height 2160 start 2168 end 2178 total 2250 clock 23.98Hz
+ 1920x1080 (0x1cf) 148.500MHz +HSync +VSync
+ h: width 1920 start 2008 end 2052 total 2200 skew 0 clock 67.50KHz
+ v: height 1080 start 1084 end 1089 total 1125 clock 60.00Hz
+ 1920x1080 (0x1d0) 148.350MHz +HSync +VSync
+ h: width 1920 start 2008 end 2052 total 2200 skew 0 clock 67.43KHz
+ v: height 1080 start 1084 end 1089 total 1125 clock 59.94Hz
+ 1920x1080 (0x1d1) 148.500MHz +HSync +VSync
+ h: width 1920 start 2448 end 2492 total 2640 skew 0 clock 56.25KHz
+ v: height 1080 start 1084 end 1089 total 1125 clock 50.00Hz
+ 1920x1080 (0x1d2) 74.180MHz +HSync +VSync
+ h: width 1920 start 2008 end 2052 total 2200 skew 0 clock 33.72KHz
+ v: height 1080 start 1084 end 1089 total 1125 clock 29.97Hz
+ 1920x1080 (0x1d3) 74.250MHz +HSync +VSync
+ h: width 1920 start 2448 end 2492 total 2640 skew 0 clock 28.12KHz
+ v: height 1080 start 1084 end 1089 total 1125 clock 25.00Hz
+ 1920x1080 (0x1d4) 74.180MHz +HSync +VSync
+ h: width 1920 start 2558 end 2602 total 2750 skew 0 clock 26.97KHz
+ v: height 1080 start 1084 end 1089 total 1125 clock 23.98Hz
+ 1920x1080 (0x1d5) 74.180MHz +HSync +VSync Interlace
+ h: width 1920 start 2008 end 2052 total 2200 skew 0 clock 33.72KHz
+ v: height 1080 start 1084 end 1094 total 1124 clock 60.00Hz
+ 1920x1080 (0x1d6) 74.250MHz +HSync +VSync Interlace
+ h: width 1920 start 2448 end 2492 total 2640 skew 0 clock 28.12KHz
+ v: height 1080 start 1084 end 1094 total 1124 clock 50.04Hz
+ 1680x1050 (0x1d7) 146.250MHz -HSync +VSync
+ h: width 1680 start 1784 end 1960 total 2240 skew 0 clock 65.29KHz
+ v: height 1050 start 1053 end 1059 total 1089 clock 59.95Hz
+ 1600x900 (0x1d8) 108.000MHz +HSync +VSync
+ h: width 1600 start 1624 end 1704 total 1800 skew 0 clock 60.00KHz
+ v: height 900 start 901 end 904 total 1000 clock 60.00Hz
+ 1440x900 (0x1d9) 106.500MHz -HSync +VSync
+ h: width 1440 start 1520 end 1672 total 1904 skew 0 clock 55.93KHz
+ v: height 900 start 903 end 909 total 934 clock 59.89Hz
+ 1366x768 (0x1da) 85.500MHz +HSync +VSync
+ h: width 1366 start 1436 end 1579 total 1792 skew 0 clock 47.71KHz
+ v: height 768 start 771 end 774 total 798 clock 59.79Hz
+ 1280x1024 (0x1db) 135.000MHz +HSync +VSync
+ h: width 1280 start 1296 end 1440 total 1688 skew 0 clock 79.98KHz
+ v: height 1024 start 1025 end 1028 total 1066 clock 75.02Hz
+ 1280x1024 (0x1dc) 108.000MHz +HSync +VSync
+ h: width 1280 start 1328 end 1440 total 1688 skew 0 clock 63.98KHz
+ v: height 1024 start 1025 end 1028 total 1066 clock 60.02Hz
+ 1280x800 (0x1dd) 83.500MHz -HSync +VSync
+ h: width 1280 start 1352 end 1480 total 1680 skew 0 clock 49.70KHz
+ v: height 800 start 803 end 809 total 831 clock 59.81Hz
+ 1280x720 (0x1de) 74.250MHz +HSync +VSync
+ h: width 1280 start 1390 end 1430 total 1650 skew 0 clock 45.00KHz
+ v: height 720 start 725 end 730 total 750 clock 60.00Hz
+ 1280x720 (0x1df) 74.180MHz +HSync +VSync
+ h: width 1280 start 1390 end 1430 total 1650 skew 0 clock 44.96KHz
+ v: height 720 start 725 end 730 total 750 clock 59.94Hz
+ 1280x720 (0x1e0) 74.250MHz +HSync +VSync
+ h: width 1280 start 1720 end 1760 total 1980 skew 0 clock 37.50KHz
+ v: height 720 start 725 end 730 total 750 clock 50.00Hz
+ 1152x864 (0x1e1) 108.000MHz +HSync +VSync
+ h: width 1152 start 1216 end 1344 total 1600 skew 0 clock 67.50KHz
+ v: height 864 start 865 end 868 total 900 clock 75.00Hz
+ 1024x768 (0x1e2) 78.750MHz +HSync +VSync
+ h: width 1024 start 1040 end 1136 total 1312 skew 0 clock 60.02KHz
+ v: height 768 start 769 end 772 total 800 clock 75.03Hz
+ 1024x768 (0x1e3) 75.000MHz -HSync -VSync
+ h: width 1024 start 1048 end 1184 total 1328 skew 0 clock 56.48KHz
+ v: height 768 start 771 end 777 total 806 clock 70.07Hz
+ 1024x768 (0x1e4) 65.000MHz -HSync -VSync
+ h: width 1024 start 1048 end 1184 total 1344 skew 0 clock 48.36KHz
+ v: height 768 start 771 end 777 total 806 clock 60.00Hz
+ 800x600 (0x1e5) 49.500MHz +HSync +VSync
+ h: width 800 start 816 end 896 total 1056 skew 0 clock 46.88KHz
+ v: height 600 start 601 end 604 total 625 clock 75.00Hz
+ 800x600 (0x1e6) 50.000MHz +HSync +VSync
+ h: width 800 start 856 end 976 total 1040 skew 0 clock 48.08KHz
+ v: height 600 start 637 end 643 total 666 clock 72.19Hz
+ 800x600 (0x1e7) 40.000MHz +HSync +VSync
+ h: width 800 start 840 end 968 total 1056 skew 0 clock 37.88KHz
+ v: height 600 start 601 end 605 total 628 clock 60.32Hz
+ 720x576 (0x1e8) 27.000MHz -HSync -VSync
+ h: width 720 start 732 end 796 total 864 skew 0 clock 31.25KHz
+ v: height 576 start 581 end 586 total 625 clock 50.00Hz
+ 720x480 (0x1e9) 27.000MHz -HSync -VSync
+ h: width 720 start 736 end 798 total 858 skew 0 clock 31.47KHz
+ v: height 480 start 489 end 495 total 525 clock 59.94Hz
+ 640x480 (0x1ea) 31.500MHz -HSync -VSync
+ h: width 640 start 656 end 720 total 840 skew 0 clock 37.50KHz
+ v: height 480 start 481 end 484 total 500 clock 75.00Hz
+ 640x480 (0x1eb) 31.500MHz -HSync -VSync
+ h: width 640 start 656 end 696 total 832 skew 0 clock 37.86KHz
+ v: height 480 start 481 end 484 total 520 clock 72.81Hz
+ 640x480 (0x1ec) 25.175MHz -HSync -VSync
+ h: width 640 start 656 end 752 total 800 skew 0 clock 31.47KHz
+ v: height 480 start 490 end 492 total 525 clock 59.94Hz
+DVI-D-0 disconnected (normal left inverted right x axis y axis)
+ Identifier: 0x1ed
+ Timestamp: 99649
+ Subpixel: unknown
+ Clones:
+ CRTCs: 0 1 2 3
+ Transform: 1.000000 0.000000 0.000000
+ 0.000000 1.000000 0.000000
+ 0.000000 0.000000 1.000000
+ filter:
+ CscMatrix: 65536 0 0 0 0 65536 0 0 0 0 65536 0
+ BorderDimensions: 4
+ supported: 4
+ Border: 0 0 0 0
+ range: (0, 65535)
+ SignalFormat: TMDS
+ supported: TMDS
+ ConnectorType: DVI-D
+ ConnectorNumber: 3
+ _ConnectorLocation: 3
+ non-desktop: 0
+ supported: 0, 1
diff --git a/vs/edid-decode.sln b/vs/edid-decode.sln
new file mode 100644
index 0000000..53fc406
--- /dev/null
+++ b/vs/edid-decode.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30011.22
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "edid-decode", "edid-decode.vcxproj", "{245F601B-02E4-43DF-B64C-B49088673764}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {245F601B-02E4-43DF-B64C-B49088673764}.Debug|x64.ActiveCfg = Debug|x64
+ {245F601B-02E4-43DF-B64C-B49088673764}.Debug|x64.Build.0 = Debug|x64
+ {245F601B-02E4-43DF-B64C-B49088673764}.Release|x64.ActiveCfg = Release|x64
+ {245F601B-02E4-43DF-B64C-B49088673764}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DDBA565E-4EC9-46DF-88E0-12F489BB3FCF}
+ EndGlobalSection
+EndGlobal
diff --git a/vs/edid-decode.vcxproj b/vs/edid-decode.vcxproj
new file mode 100644
index 0000000..c9213ab
--- /dev/null
+++ b/vs/edid-decode.vcxproj
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <ProjectGuid>{245F601B-02E4-43DF-B64C-B49088673764}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>ediddecode</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4244; 4018; 4267; 4996; 26451; 6385; 6001</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>
+ </Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4244; 4018; 4267; 4996; 26451; 6385; 6001</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="getopt.c" />
+ <ClCompile Include="..\edid-decode.cpp" />
+ <ClCompile Include="..\parse-base-block.cpp" />
+ <ClCompile Include="..\parse-cta-block.cpp" />
+ <ClCompile Include="..\parse-di-ext-block.cpp" />
+ <ClCompile Include="..\parse-displayid-block.cpp" />
+ <ClCompile Include="..\parse-ls-ext-block.cpp" />
+ <ClCompile Include="..\parse-vtb-ext-block.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="getopt.h" />
+ <ClInclude Include="unistd.h" />
+ <ClInclude Include="..\edid-decode.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/vs/edid-decode.vcxproj.filters b/vs/edid-decode.vcxproj.filters
new file mode 100644
index 0000000..68e8b7d
--- /dev/null
+++ b/vs/edid-decode.vcxproj.filters
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="windows-unix">
+ <UniqueIdentifier>{757b13a8-3e1d-43af-acf1-d03963acb2e6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="edid-decode">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\parse-base-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-cta-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-di-ext-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-displayid-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-ls-ext-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-vtb-ext-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="getopt.c">
+ <Filter>windows-unix</Filter>
+ </ClCompile>
+ <ClCompile Include="..\edid-decode.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\edid-decode.h">
+ <Filter>edid-decode</Filter>
+ </ClInclude>
+ <ClInclude Include="getopt.h">
+ <Filter>windows-unix</Filter>
+ </ClInclude>
+ <ClInclude Include="unistd.h">
+ <Filter>windows-unix</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/vs/getopt.c b/vs/getopt.c
new file mode 100644
index 0000000..d5868a5
--- /dev/null
+++ b/vs/getopt.c
@@ -0,0 +1,787 @@
+/**
+ * @file getopt.c
+ * @copy 2012 MinGW.org project
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ */
+
+/*
+ * Implementation of the `getopt', `getopt_long' and `getopt_long_only'
+ * APIs, for inclusion in the MinGW runtime library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <getopt.h>
+
+/* Identify how to get the calling program name, for use in messages...
+ */
+#ifdef __CYGWIN__
+/*
+ * CYGWIN uses this DLL reference...
+ */
+# define PROGNAME __progname
+extern char __declspec(dllimport) *__progname;
+#else
+/*
+ * ...while elsewhere, we simply use the first argument passed.
+ */
+# define PROGNAME *argv
+# define __inline__ __inline
+#endif
+
+/* Initialise the public variables. */
+
+int optind = 1; /* index for first non-option arg */
+int opterr = 1; /* enable built-in error messages */
+
+char *optarg = NULL; /* pointer to current option argument */
+
+#define CHAR char /* argument type selector */
+
+#define getopt_switchar '-' /* option prefix character in argv */
+#define getopt_pluschar '+' /* prefix for POSIX mode in optstring */
+#define getopt_takes_argument ':' /* marker for optarg in optstring */
+#define getopt_arg_assign '=' /* longopt argument field separator */
+#define getopt_unknown '?' /* return code for unmatched option */
+#define getopt_ordered 1 /* return code for ordered non-option */
+
+#define getopt_all_done -1 /* return code to indicate completion */
+
+enum
+{ /* All `getopt' API functions are implemented via calls to the
+ * common static function `getopt_parse()'; these `mode' selectors
+ * determine the behaviour of `getopt_parse()', to deliver the
+ * appropriate result in each case.
+ */
+ getopt_mode_standard = 0, /* getopt() */
+ getopt_mode_long, /* getopt_long() */
+ getopt_mode_long_only /* getopt_long_only() */
+};
+
+enum
+{ /* When attempting to match a command line argument to a long form option,
+ * these indicate the status of the match.
+ */
+ getopt_no_match = 0, /* no successful match */
+ getopt_abbreviated_match, /* argument is an abbreviation for an option */
+ getopt_exact_match /* argument matches the full option name */
+};
+
+int optopt = getopt_unknown; /* return value for option being evaluated */
+
+/* Some BSD applications expect to be able to reinitialise `getopt' parsing
+ * by setting a global variable called `optreset'. We provide an obfuscated
+ * API, which allows applications to emulate this brain damage; however, any
+ * use of this is non-portable, and is strongly discouraged.
+ */
+#define optreset __mingw_optreset
+int optreset = 0;
+
+static __inline__
+int getopt_missing_arg( const CHAR *optstring )
+{
+ /* Helper function to determine the appropriate return value,
+ * for the case where a required option argument is missing.
+ */
+ if( (*optstring == getopt_pluschar) || (*optstring == getopt_switchar) )
+ ++optstring;
+ return (*optstring == getopt_takes_argument)
+ ? getopt_takes_argument
+ : getopt_unknown;
+}
+
+/* `complain' macro facilitates the generation of simple built-in
+ * error messages, displayed on various fault conditions, provided
+ * `opterr' is non-zero.
+ */
+#define complain( MSG, ARG ) if( opterr ) \
+ fprintf( stderr, "%s: "MSG"\n", PROGNAME, ARG )
+
+static __inline__
+int getopt_argerror( int mode, char *fmt, CHAR *prog, struct option *opt, int retval )
+{
+ /* Helper function, to generate more complex built-in error
+ * messages, for invalid arguments to long form options ...
+ */
+ if( opterr )
+ {
+ /* ... but, displayed only if `opterr' is non-zero.
+ */
+ char flag[] = "--";
+ if( mode != getopt_mode_long )
+ /*
+ * only display one hyphen, for implicit long form options,
+ * improperly resolved by `getopt_long_only()'.
+ */
+ flag[1] = 0;
+ /*
+ * always preface the program name ...
+ */
+ fprintf( stderr, "%s: ", prog );
+ /*
+ * to the appropriate, option specific message.
+ */
+ fprintf( stderr, fmt, flag, opt->name );
+ }
+ /* Whether displaying the message, or not, always set `optopt'
+ * to identify the faulty option ...
+ */
+ optopt = opt->val;
+ /*
+ * and return the `invalid option' indicator.
+ */
+ return retval;
+}
+
+/* `getopt_conventions' establish behavioural options, to control
+ * the operation of `getopt_parse()', e.g. to select between POSIX
+ * and GNU style argument parsing behaviour.
+ */
+#define getopt_set_conventions 0x1000
+#define getopt_posixly_correct 0x0010
+
+static __inline__
+int getopt_conventions( int flags )
+{
+ static int conventions = 0;
+
+ if( (conventions == 0) && ((flags & getopt_set_conventions) == 0) )
+ {
+ /* default conventions have not yet been established;
+ * initialise them now!
+ */
+ conventions = getopt_set_conventions;
+ if( (flags == getopt_pluschar) || (getenv( "POSIXLY_CORRECT" ) != NULL) )
+ conventions |= getopt_posixly_correct;
+ }
+
+ else if( flags & getopt_set_conventions )
+ /*
+ * default conventions may have already been established,
+ * but this is a specific request to augment them.
+ */
+ conventions |= flags;
+
+ /* in any event, return the currently established conventions.
+ */
+ return conventions;
+}
+
+static __inline__
+int is_switchar( CHAR flag )
+{
+ /* A simple helper function, used to identify the switch character
+ * introducing an optional command line argument.
+ */
+ return flag == getopt_switchar;
+}
+
+static __inline__
+const CHAR *getopt_match( CHAR lookup, const CHAR *opt_string )
+{
+ /* Helper function, used to identify short form options.
+ */
+ if( (*opt_string == getopt_pluschar) || (*opt_string == getopt_switchar) )
+ ++opt_string;
+ if( *opt_string == getopt_takes_argument )
+ ++opt_string;
+ do if( lookup == *opt_string ) return opt_string;
+ while( *++opt_string );
+ return NULL;
+}
+
+static __inline__
+int getopt_match_long( const CHAR *nextchar, const CHAR *optname )
+{
+ /* Helper function, used to identify potential matches for
+ * long form options.
+ */
+ CHAR matchchar;
+ while( (matchchar = *nextchar++) && (matchchar == *optname) )
+ /*
+ * skip over initial substring which DOES match.
+ */
+ ++optname;
+
+ if( matchchar )
+ {
+ /* did NOT match the entire argument to an initial substring
+ * of a defined option name ...
+ */
+ if( matchchar != getopt_arg_assign )
+ /*
+ * ... and didn't stop at an `=' internal field separator,
+ * so this is NOT a possible match.
+ */
+ return getopt_no_match;
+
+ /* DID stop at an `=' internal field separator,
+ * so this IS a possible match, and what follows is an
+ * argument to the possibly matched option.
+ */
+ optarg = (char *)(nextchar);
+ }
+ return *optname
+ /*
+ * if we DIDN'T match the ENTIRE text of the option name,
+ * then it's a possible abbreviated match ...
+ */
+ ? getopt_abbreviated_match
+ /*
+ * but if we DID match the entire option name,
+ * then it's a DEFINITE EXACT match.
+ */
+ : getopt_exact_match;
+}
+
+static __inline__
+int getopt_resolved( int mode, int argc, CHAR *const *argv, int *argind,
+struct option *opt, int index, int *retindex, const CHAR *optstring )
+{
+ /* Helper function to establish appropriate return conditions,
+ * on resolution of a long form option.
+ */
+ if( retindex != NULL )
+ *retindex = index;
+
+ /* On return, `optind' should normally refer to the argument, if any,
+ * which follows the current one; it is convenient to set this, before
+ * checking for the presence of any `optarg'.
+ */
+ optind = *argind + 1;
+
+ if( optarg && (opt[index].has_arg == no_argument) )
+ /*
+ * it is an error for the user to specify an option specific argument
+ * with an option which doesn't expect one!
+ */
+ return getopt_argerror( mode, "option `%s%s' doesn't accept an argument\n",
+ PROGNAME, opt + index, getopt_unknown );
+
+ else if( (optarg == NULL) && (opt[index].has_arg == required_argument) )
+ {
+ /* similarly, it is an error if no argument is specified
+ * with an option which requires one ...
+ */
+ if( optind < argc )
+ /*
+ * ... except that the requirement may be satisfied from
+ * the following command line argument, if any ...
+ */
+ optarg = argv[*argind = optind++];
+
+ else
+ /* so fail this case, only if no such argument exists!
+ */
+ return getopt_argerror( mode, "option `%s%s' requires an argument\n",
+ PROGNAME, opt + index, getopt_missing_arg( optstring ) );
+ }
+
+ /* when the caller has provided a return buffer ...
+ */
+ if( opt[index].flag != NULL )
+ {
+ /* ... then we place the proper return value there,
+ * and return a status code of zero ...
+ */
+ *(opt[index].flag) = opt[index].val;
+ return 0;
+ }
+ /* ... otherwise, the return value becomes the status code.
+ */
+ return opt[index].val;
+}
+
+static __inline__
+int getopt_verify( const CHAR *nextchar, const CHAR *optstring )
+{
+ /* Helper function, called by getopt_parse() when invoked
+ * by getopt_long_only(), to verify when an unmatched or an
+ * ambiguously matched long form option string is valid as
+ * a short form option specification.
+ */
+ if( ! (nextchar && *nextchar && optstring && *optstring) )
+ /*
+ * There are no characters to be matched, or there are no
+ * valid short form option characters to which they can be
+ * matched, so this can never be valid.
+ */
+ return 0;
+
+ while( *nextchar )
+ {
+ /* For each command line character in turn ...
+ */
+ const CHAR *test;
+ if( (test = getopt_match( *nextchar++, optstring )) == NULL )
+ /*
+ * ... there is no short form option to match the current
+ * candidate, so the entire argument fails.
+ */
+ return 0;
+
+ if( test[1] == getopt_takes_argument )
+ /*
+ * The current candidate is valid, and it matches an option
+ * which takes an argument, so this command line argument is
+ * a valid short form option specification; accept it.
+ */
+ return 1;
+ }
+ /* If we get to here, then every character in the command line
+ * argument was valid as a short form option; accept it.
+ */
+ return 1;
+}
+
+static
+#define getopt_std_args int argc, CHAR *const argv[], const CHAR *optstring
+int getopt_parse( int mode, getopt_std_args, ... )
+{
+ /* Common core implementation for ALL `getopt' functions.
+ */
+ static int argind = 0;
+ static int optbase = 0;
+ static const CHAR *nextchar = NULL;
+ static int optmark = 0;
+
+ if( (optreset |= (optind < 1)) || (optind < optbase) )
+ {
+ /* POSIX does not prescribe any definitive mechanism for restarting
+ * a `getopt' scan, but some applications may require such capability.
+ * We will support it, by allowing the caller to adjust the value of
+ * `optind' downwards, (nominally setting it to zero). Since POSIX
+ * wants `optind' to have an initial value of one, but we want all
+ * of our internal place holders to be initialised to zero, when we
+ * are called for the first time, we will handle such a reset by
+ * adjusting all of the internal place holders to one less than
+ * the adjusted `optind' value, (but never to less than zero).
+ */
+ if( optreset )
+ {
+ /* User has explicitly requested reinitialisation...
+ * We need to reset `optind' to it's normal initial value of 1,
+ * to avoid a potential infinitely recursive loop; by doing this
+ * up front, we also ensure that the remaining place holders
+ * will be correctly reinitialised to no less than zero.
+ */
+ optind = 1;
+
+ /* We also need to clear the `optreset' request...
+ */
+ optreset = 0;
+ }
+
+ /* Now, we may safely reinitialise the internal place holders, to
+ * one less than `optind', without fear of making them negative.
+ */
+ optmark = optbase = argind = optind - 1;
+ nextchar = NULL;
+ }
+
+ /* From a POSIX perspective, the following is `undefined behaviour';
+ * we implement it thus, for compatibility with GNU and BSD getopt.
+ */
+ else if( optind > (argind + 1) )
+ {
+ /* Some applications expect to be able to manipulate `optind',
+ * causing `getopt' to skip over one or more elements of `argv';
+ * POSIX doesn't require us to support this brain-damaged concept;
+ * (indeed, POSIX defines no particular behaviour, in the event of
+ * such usage, so it must be considered a bug for an application
+ * to rely on any particular outcome); nonetheless, Mac-OS-X and
+ * BSD actually provide *documented* support for this capability,
+ * so we ensure that our internal place holders keep track of
+ * external `optind' increments; (`argind' must lag by one).
+ */
+ argind = optind - 1;
+
+ /* When `optind' is misused, in this fashion, we also abandon any
+ * residual text in the argument we had been parsing; this is done
+ * without any further processing of such abandoned text, assuming
+ * that the caller is equipped to handle it appropriately.
+ */
+ nextchar = NULL;
+ }
+
+ if( nextchar && *nextchar )
+ {
+ /* we are parsing a standard, or short format, option argument ...
+ */
+ const CHAR *optchar;
+ if( (optchar = getopt_match( optopt = *nextchar++, optstring )) != NULL )
+ {
+ /* we have identified it as valid ...
+ */
+ if( optchar[1] == getopt_takes_argument )
+ {
+ /* and determined that it requires an associated argument ...
+ */
+ if( ! *(optarg = (char *)(nextchar)) )
+ {
+ /* the argument is NOT attached ...
+ */
+ if( optchar[2] == getopt_takes_argument )
+ /*
+ * but this GNU extension marks it as optional,
+ * so we don't provide one on this occasion.
+ */
+ optarg = NULL;
+
+ /* otherwise this option takes a mandatory argument,
+ * so, provided there is one available ...
+ */
+ else if( (argc - argind) > 1 )
+ /*
+ * we take the following command line argument,
+ * as the appropriate option argument.
+ */
+ optarg = argv[++argind];
+
+ /* but if no further argument is available,
+ * then there is nothing we can do, except for
+ * issuing the requisite diagnostic message.
+ */
+ else
+ {
+ complain( "option requires an argument -- %c", optopt );
+ return getopt_missing_arg( optstring );
+ }
+ }
+ optind = argind + 1;
+ nextchar = NULL;
+ }
+ else
+ optarg = NULL;
+ optind = (nextchar && *nextchar) ? argind : argind + 1;
+ return optopt;
+ }
+ /* if we didn't find a valid match for the specified option character,
+ * then we fall through to here, so take appropriate diagnostic action.
+ */
+ if( mode == getopt_mode_long_only )
+ {
+ complain( "unrecognised option `-%s'", --nextchar );
+ nextchar = NULL;
+ optopt = 0;
+ }
+ else
+ complain( "invalid option -- %c", optopt );
+ optind = (nextchar && *nextchar) ? argind : argind + 1;
+ return getopt_unknown;
+ }
+
+ if( optmark > optbase )
+ {
+ /* This can happen, in GNU parsing mode ONLY, when we have
+ * skipped over non-option arguments, and found a subsequent
+ * option argument; in this case we permute the arguments.
+ */
+ int index;
+ /*
+ * `optspan' specifies the number of contiguous arguments
+ * which are spanned by the current option, and so must be
+ * moved together during permutation.
+ */
+ int optspan = argind - optmark + 1;
+ /*
+ * we use `this_arg' to store these temporarily.
+ */
+ CHAR **this_arg = (CHAR **)malloc(optspan * sizeof(CHAR *));
+ if( this_arg == NULL )
+ return getopt_unknown;
+ /*
+ * we cannot manipulate `argv' directly, since the `getopt'
+ * API prototypes it as `read-only'; this cast to `arglist'
+ * allows us to work around that restriction.
+ */
+ CHAR **arglist = (char **)(argv);
+
+ /* save temporary copies of the arguments which are associated
+ * with the current option ...
+ */
+ for( index = 0; index < optspan; ++index )
+ this_arg[index] = arglist[optmark + index];
+
+ /* move all preceding non-option arguments to the right,
+ * overwriting these saved arguments, while making space
+ * to replace them in their permuted location.
+ */
+ for( --optmark; optmark >= optbase; --optmark )
+ arglist[optmark + optspan] = arglist[optmark];
+
+ /* restore the temporarily saved option arguments to
+ * their permuted location.
+ */
+ for( index = 0; index < optspan; ++index )
+ arglist[optbase + index] = this_arg[index];
+
+ free(this_arg);
+
+ /* adjust `optbase', to account for the relocated option.
+ */
+ optbase += optspan;
+ }
+
+ else
+ /* no permutation occurred ...
+ * simply adjust `optbase' for all options parsed so far.
+ */
+ optbase = argind + 1;
+
+ /* enter main parsing loop ...
+ */
+ while( argc > ++argind )
+ {
+ /* inspect each argument in turn, identifying possible options ...
+ */
+ if( is_switchar( *(nextchar = argv[optmark = argind]) ) && *++nextchar )
+ {
+ /* we've found a candidate option argument ... */
+
+ if( is_switchar( *nextchar ) )
+ {
+ /* it's a double hyphen argument ... */
+
+ const CHAR *refchar = nextchar;
+ if( *++refchar )
+ {
+ /* and it looks like a long format option ...
+ * `getopt_long' mode must be active to accept it as such,
+ * `getopt_long_only' also qualifies, but we must downgrade
+ * it to force explicit handling as a long format option.
+ */
+ if( mode >= getopt_mode_long )
+ {
+ nextchar = refchar;
+ mode = getopt_mode_long;
+ }
+ }
+ else
+ {
+ /* this is an explicit `--' end of options marker, so wrap up now!
+ */
+ if( optmark > optbase )
+ {
+ /* permuting the argument list as necessary ...
+ * (note use of `this_arg' and `arglist', as above).
+ */
+ CHAR *this_arg = argv[optmark];
+ CHAR **arglist = (CHAR **)(argv);
+
+ /* move all preceding non-option arguments to the right ...
+ */
+ do arglist[optmark] = arglist[optmark - 1];
+ while( optmark-- > optbase );
+
+ /* reinstate the `--' marker, in its permuted location.
+ */
+ arglist[optbase] = this_arg;
+ }
+ /* ... before finally bumping `optbase' past the `--' marker,
+ * and returning the `all done' completion indicator.
+ */
+ optind = ++optbase;
+ return getopt_all_done;
+ }
+ }
+ else if( mode < getopt_mode_long_only )
+ {
+ /* it's not an explicit long option, and `getopt_long_only' isn't active,
+ * so we must explicitly try to match it as a short option.
+ */
+ mode = getopt_mode_standard;
+ }
+
+ if( mode >= getopt_mode_long )
+ {
+ /* the current argument is a long form option, (either explicitly,
+ * introduced by a double hyphen, or implicitly because we were called
+ * by `getopt_long_only'); this is where we parse it.
+ */
+ int lookup;
+ int matched = -1;
+
+ /* we need to fetch the `extra' function arguments, which are
+ * specified for the `getopt_long' APIs.
+ */
+ va_list refptr;
+ va_start( refptr, optstring );
+ struct option *longopts = va_arg( refptr, struct option * );
+ int *optindex = va_arg( refptr, int * );
+ va_end( refptr );
+
+ /* ensuring that `optarg' does not inherit any junk, from parsing
+ * preceding arguments ...
+ */
+ optarg = NULL;
+ for( lookup = 0; longopts && longopts[lookup].name; ++lookup )
+ {
+ /* scan the list of defined long form options ...
+ */
+ switch( getopt_match_long( nextchar, longopts[lookup].name ) )
+ {
+ /* looking for possible matches for the current argument.
+ */
+ case getopt_exact_match:
+ /*
+ * when an exact match is found,
+ * return it immediately, setting `nextchar' to NULL,
+ * to ensure we don't mistakenly try to match any
+ * subsequent characters as short form options.
+ */
+ nextchar = NULL;
+ return getopt_resolved( mode, argc, argv, &argind,
+ longopts, lookup, optindex, optstring );
+
+ case getopt_abbreviated_match:
+ /*
+ * but, for a partial (initial substring) match ...
+ */
+ if( matched >= 0 )
+ {
+ /* if this is not the first, then we have an ambiguity ...
+ */
+ if( (mode == getopt_mode_long_only)
+ /*
+ * However, in the case of getopt_long_only(), if
+ * the entire ambiguously matched string represents
+ * a valid short option specification, then we may
+ * proceed to interpret it as such.
+ */
+ && getopt_verify( nextchar, optstring ) )
+ return getopt_parse( mode, argc, argv, optstring );
+
+ /* If we get to here, then the ambiguously matched
+ * partial long option isn't valid for short option
+ * evaluation; reset parser context to resume with
+ * the following command line argument, diagnose
+ * ambiguity, and bail out.
+ */
+ optopt = 0;
+ nextchar = NULL;
+ optind = argind + 1;
+ complain( "option `%s' is ambiguous", argv[argind] );
+ return getopt_unknown;
+ }
+ /* otherwise just note that we've found a possible match ...
+ */
+ matched = lookup;
+ }
+ }
+ if( matched >= 0 )
+ {
+ /* if we get to here, then we found exactly one partial match,
+ * so return it, as for an exact match.
+ */
+ nextchar = NULL;
+ return getopt_resolved( mode, argc, argv, &argind,
+ longopts, matched, optindex, optstring );
+ }
+ /* if here, then we had what SHOULD have been a long form option,
+ * but it is unmatched ...
+ */
+ if( (mode < getopt_mode_long_only)
+ /*
+ * ... although paradoxically, `mode == getopt_mode_long_only'
+ * allows us to still try to match it as a short form option.
+ */
+ || (getopt_verify( nextchar, optstring ) == 0) )
+ {
+ /* When it cannot be matched, reset the parsing context to
+ * resume from the next argument, diagnose the failed match,
+ * and bail out.
+ */
+ optopt = 0;
+ nextchar = NULL;
+ optind = argind + 1;
+ complain( "unrecognised option `%s'", argv[argind] );
+ return getopt_unknown;
+ }
+ }
+ /* fall through to handle standard short form options...
+ * when the option argument format is neither explictly identified
+ * as long, nor implicitly matched as such, and the argument isn't
+ * just a bare hyphen, (which isn't an option), then we make one
+ * recursive call to explicitly interpret it as short format.
+ */
+ if( *nextchar )
+ return getopt_parse( mode, argc, argv, optstring );
+ }
+ /* if we get to here, then we've parsed a non-option argument ...
+ * in GNU compatibility mode, we step over it, so we can permute
+ * any subsequent option arguments, but ...
+ */
+ if( *optstring == getopt_switchar )
+ {
+ /* if `optstring' begins with a `-' character, this special
+ * GNU specific behaviour requires us to return the non-option
+ * arguments in strict order, as pseudo-arguments to a special
+ * option, with return value defined as `getopt_ordered'.
+ */
+ nextchar = NULL;
+ optind = argind + 1;
+ optarg = argv[argind];
+ return getopt_ordered;
+ }
+ if( getopt_conventions( *optstring ) & getopt_posixly_correct )
+ /*
+ * otherwise ...
+ * for POSIXLY_CORRECT behaviour, or if `optstring' begins with
+ * a `+' character, then we break out of the parsing loop, so that
+ * the scan ends at the current argument, with no permutation.
+ */
+ break;
+ }
+ /* fall through when all arguments have been evaluated,
+ */
+ optind = optbase;
+ return getopt_all_done;
+}
+
+/* All three public API entry points are trivially defined,
+ * in terms of the internal `getopt_parse' function.
+ */
+int getopt( getopt_std_args )
+{
+ return getopt_parse( getopt_mode_standard, argc, argv, optstring );
+}
+
+int getopt_long( getopt_std_args, const struct option *opts, int *index )
+{
+ return getopt_parse( getopt_mode_long, argc, argv, optstring, opts, index );
+}
+
+int getopt_long_only( getopt_std_args, const struct option *opts, int *index )
+{
+ return getopt_parse( getopt_mode_long_only, argc, argv, optstring, opts, index );
+}
+
+#ifdef __weak_alias
+/*
+ * These Microsnot style uglified aliases are provided for compatibility
+ * with the previous MinGW implementation of the getopt API.
+ */
+__weak_alias( getopt, _getopt )
+__weak_alias( getopt_long, _getopt_long )
+__weak_alias( getopt_long_only, _getopt_long_only )
+#endif
diff --git a/vs/getopt.h b/vs/getopt.h
new file mode 100644
index 0000000..e34761d
--- /dev/null
+++ b/vs/getopt.h
@@ -0,0 +1,107 @@
+/**
+ * @file getopt.h
+ * @copy 2012 MinGW.org project
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ */
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+/*
+ * Defines constants and function prototypes required to implement
+ * the `getopt', `getopt_long' and `getopt_long_only' APIs.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int optind; /* index of first non-option in argv */
+extern int optopt; /* single option character, as parsed */
+extern int opterr; /* flag to enable built-in diagnostics... */
+ /* (user may set to zero, to suppress) */
+
+extern char *optarg; /* pointer to argument of current option */
+
+extern int getopt( int, char * const [], const char * );
+
+#ifdef _BSD_SOURCE
+/*
+ * BSD adds the non-standard `optreset' feature, for reinitialisation
+ * of `getopt' parsing. We support this feature, for applications which
+ * proclaim their BSD heritage, before including this header; however,
+ * to maintain portability, developers are advised to avoid it.
+ */
+# define optreset __mingw_optreset
+
+extern int optreset;
+#endif
+#ifdef __cplusplus
+}
+#endif
+/*
+ * POSIX requires the `getopt' API to be specified in `unistd.h';
+ * thus, `unistd.h' includes this header. However, we do not want
+ * to expose the `getopt_long' or `getopt_long_only' APIs, when
+ * included in this manner. Thus, close the standard __GETOPT_H__
+ * declarations block, and open an additional __GETOPT_LONG_H__
+ * specific block, only when *not* __UNISTD_H_SOURCED__, in which
+ * to declare the extended API.
+ */
+#endif /* !defined(__GETOPT_H__) */
+#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
+#define __GETOPT_LONG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct option /* specification for a long form option... */
+{
+ const char *name; /* option name, without leading hyphens */
+ int has_arg; /* does it take an argument? */
+ int *flag; /* where to save its status, or NULL */
+ int val; /* its associated status value */
+};
+
+enum /* permitted values for its `has_arg' field... */
+{
+ no_argument = 0, /* option never takes an argument */
+ required_argument, /* option always requires an argument */
+ optional_argument /* option may take an argument */
+};
+
+extern int getopt_long( int, char * const [], const char *, const struct option *, int * );
+extern int getopt_long_only( int, char * const [], const char *, const struct option *, int * );
+/*
+ * Previous MinGW implementation had...
+ */
+#ifndef HAVE_DECL_GETOPT
+/*
+ * ...for the long form API only; keep this for compatibility.
+ */
+# define HAVE_DECL_GETOPT 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
diff --git a/vs/unistd.h b/vs/unistd.h
new file mode 100644
index 0000000..afa0d18
--- /dev/null
+++ b/vs/unistd.h
@@ -0,0 +1,10 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H 1
+
+#include <stdlib.h>
+#include <io.h>
+#include <getopt.h>
+
+#define ssize_t int
+
+#endif /* unistd.h */