diff --git a/edid-decode.h b/edid-decode.h
index 83ded83..758bdcf 100644
--- a/edid-decode.h
+++ b/edid-decode.h
@@ -111,6 +111,7 @@ struct edid_state {
 	void cta_y420cmdb(const unsigned char *x, unsigned length);
 	void cta_vfpdb(const unsigned char *x, unsigned length);
 	void cta_hdmi_block(const unsigned char *x, unsigned length);
+	void cta_oui(const char *block_name, const unsigned char *x, unsigned length, unsigned *ouinum);
 	void cta_block(const unsigned char *x);
 	void preparse_cta_block(const unsigned char *x);
 	void parse_cta_block(const unsigned char *x);
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
index dea87c1..e52822a 100644
--- a/parse-cta-block.cpp
+++ b/parse-cta-block.cpp
@@ -375,6 +375,11 @@ void edid_state::cta_svd(const unsigned char *x, unsigned n, int for_ycbcr420)
 
 		if (vic == 1 && !for_ycbcr420)
 			has_cta861_vic_1 = 1;
+
+		// vics and has_vic are basically the same (if has_vic was not bool), except vics
+		// is built after preparse (during parse) which allows errors for duplicates to be
+		// output in parse order. has_vic is built during preparse and is used when vics
+		// from other blocks need to be checked.
 		if (++vics[vic][for_ycbcr420] == 2)
 			fail("Duplicate %sVIC %u\n", for_ycbcr420 ? "YCbCr 4:2:0 " : "", vic);
 		if (for_ycbcr420 && has_vic[0][vic])
@@ -485,7 +490,6 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
 {
 	unsigned len_vic, len_3d;
 
-	printf(" (HDMI)\n");
 	printf("    Source physical address %u.%u.%u.%u\n", x[3] >> 4, x[3] & 0x0f,
 	       x[4] >> 4, x[4] & 0x0f);
 
@@ -1228,10 +1232,20 @@ static void cta_hdmi_audio_block(const unsigned char *x, unsigned length)
 		x += 4;
 	}
 }
+const unsigned kOUI_Unknown   = 1<<12;
+const unsigned kOUI_HDMI      = 2<<12;
+const unsigned kOUI_HDMIForum = 3<<12;
+const unsigned kOUI_HDR10     = 4<<12;
 
-static const char *oui_name(unsigned oui)
+static const char *oui_name(unsigned oui, unsigned *ouinum = NULL)
 {
+	unsigned ouinumscratch;
+	if (!ouinum) ouinum = &ouinumscratch;
+	*ouinum = kOUI_Unknown;
 	switch (oui) {
+	case 0x000c03: *ouinum = kOUI_HDMI      ; return "HDMI";
+	case 0xc45dd8: *ouinum = kOUI_HDMIForum ; return "HDMI Forum";
+	case 0x90848b: *ouinum = kOUI_HDR10     ; return "HDR10+";
 	case 0x00001a: return "AMD";
 	case 0x00044b: return "NVIDIA";
 	case 0x000c6e: return "ASUS";
@@ -1244,212 +1258,136 @@ static const char *oui_name(unsigned oui)
 	}
 }
 
+void edid_state::cta_oui(const char *block_name, const unsigned char *x, unsigned length, unsigned *ouinum)
+{
+	char buf[10];
+	unsigned oui;
+
+	if (length < 3) {
+		oui = 0xffffffff;
+		sprintf(buf, "?");
+	} else {
+		oui = (x[2] << 16) + (x[1] << 8) + x[0];
+		sprintf(buf, "0x%06x", oui);
+	}
+	
+	const char *ouiname = oui_name(oui, ouinum);
+	std::string name = std::string(block_name) + ", OUI " + buf;
+	if (ouiname) name += std::string(" (") + ouiname + ")";
+	data_block = name;
+	
+	if (oui == 0xffffffff)
+		fail("CTA data block is not long enough to contain an OUI\n");
+}
+
+#define data_block_o(n) cta_oui(n, x + 1 + extended, length - extended, &ouinum)
+
 void edid_state::cta_block(const unsigned char *x)
 {
-	static int last_block_was_hdmi_vsdb;
-	static int have_hf_vsdb, have_hf_scdb;
-	static int first_block = 1;
+	static unsigned previous_cta_tag = 0;
+	static bool have_hf_vsdb = false;
+	static bool have_hf_scdb = false;
+	static unsigned cta_block_number = 0;
+
 	unsigned length = x[0] & 0x1f;
-	const char *name;
-	unsigned oui;
+	unsigned ouinum = 0;
+	unsigned tag=(x[0] & 0xe0) >> 5;
+	unsigned extended = tag == 0x07 ? 1 : 0;
+	if (extended) tag = 0x700 + x[1];
+
+	switch (tag) {
+	case 0x001: data_block = "Audio Data Block"; break;
+	case 0x002: data_block = "Video Data Block"; break;
+	case 0x003: data_block_o("Vendor-Specific Data Block"); break;
+	case 0x004: data_block = "Speaker Allocation Data Block"; break;
+	case 0x005: data_block = "VESA DTC Data Block"; break; // not implemented
+
+	case 0x700: data_block = "Video Capability Data Block"; break;
+	case 0x701: data_block_o("Vendor-Specific Video Data Block"); break;
+	case 0x702: data_block = "VESA Video Display Device Data Block"; break; // not implemented
+	case 0x703: data_block = "VESA Video Timing Block Extension"; break; // not implemented
+	case 0x704: data_block = "Reserved for HDMI Video Data Block"; break; // reserved
+	case 0x705: data_block = "Colorimetry Data Block"; break;
+	case 0x706: data_block = "HDR Static Metadata Data Block"; break;
+	case 0x707: data_block = "HDR Dynamic Metadata Data Block"; break;
+
+	case 0x70d: data_block = "Video Format Preference Data Block"; break;
+	case 0x70e: data_block = "YCbCr 4:2:0 Video Data Block"; break;
+	case 0x70f: data_block = "YCbCr 4:2:0 Capability Map Data Block"; break;
+	case 0x710: data_block = "Reserved for CTA Miscellaneous Audio Fields"; break; // reserved
+	case 0x711: data_block_o("Vendor-Specific Audio Data Block"); break; // no vendors implemented
+	case 0x712: data_block = "HDMI Audio Data Block"; break;
+	case 0x713: data_block = "Room Configuration Data Block"; break;
+	case 0x714: data_block = "Speaker Location Data Block"; break;
+
+	case 0x720: data_block = "InfoFrame Data Block"; break;
+
+	case 0x778: data_block = "HDMI Forum EDID Extension Override Data Block"; break;
+	case 0x779: data_block = "HDMI Forum Sink Capability Data Block"; break;
+	default:
+		     if (tag < 0x700) data_block = "Unknown CTA Data Block";
+		else if (tag < 0x70d) data_block = "Unknown CTA Video-Related Data Block";
+		else if (tag < 0x720) data_block = "Unknown CTA Audio-Related Data Block";
+		else if (tag < 0x778) data_block = "Unknown CTA Data Block";
+		else if (tag < 0x780) data_block = "Unknown CTA HDMI-Related Data Block";
+		else                  data_block = "Unknown CTA Data Block";
+		data_block += std::string(" (") + (extended ? "extended " : "") + "tag " + utohex(tag & 0xff) + ")";
+	}
 
-	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);
+	printf("  %s\n", data_block.c_str());
+	
+	tag |= ouinum;
+	switch (tag) {
+	case 0x001: cta_audio_block(x + 1, length); break;
+	case 0x002: cta_svd(x + 1, length, 0); break;
+	case 0x003|kOUI_HDMI:
+		cta_hdmi_block(x + 1, length);
+		if (edid_minor != 3)
+			fail("The HDMI Specification uses EDID 1.3, not 1.%u\n", edid_minor);
 		break;
-	case 0x02:
-		data_block = "Video Data Block";
-		printf("  %s\n", data_block.c_str());
-		cta_svd(x + 1, length, 0);
+	case 0x003|kOUI_HDMIForum:
+		if (previous_cta_tag != (0x003|kOUI_HDMI))
+			fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB\n");
+		if (have_hf_scdb || have_hf_vsdb)
+			fail("Duplicate HDMI Forum VSDB/SCDB\n");
+		cta_hf_scdb(x + 4, length - 3);
+		have_hf_vsdb = true;
 		break;
-	case 0x03:
-		oui = (x[3] << 16) + (x[2] << 8) + x[1];
-		printf("  Vendor-Specific Data Block, OUI 0x%06x", oui);
-		name = oui_name(oui);
-		if (oui == 0x000c03) {
-			data_block = "Vendor-Specific Data Block (HDMI)";
-			cta_hdmi_block(x + 1, length);
-			last_block_was_hdmi_vsdb = 1;
-			first_block = 0;
-			if (edid_minor != 3)
-				fail("The HDMI Specification uses EDID 1.3, not 1.%u\n", edid_minor);
-			return;
-		}
-		if (oui == 0xc45dd8) {
-			data_block = "Vendor-Specific Data Block (HDMI Forum)";
-			if (!last_block_was_hdmi_vsdb)
-				fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB\n");
-			if (have_hf_scdb || have_hf_vsdb)
-				fail("Duplicate HDMI Forum VSDB/SCDB\n");
-			printf(" (HDMI Forum)\n");
-			cta_hf_scdb(x + 4, length - 3);
-			have_hf_vsdb = 1;
-		} else if (name) {
-			data_block = std::string("Vendor-Specific Data Block (") + name + ")";
-			printf(" (%s)\n", name);
-			hex_block("    ", x + 4, length - 3);
-		} else {
-			printf("\n");
-			hex_block("    ", x + 4, length - 3);
-			data_block.clear();
-			warn("Unknown Vendor-Specific Data Block, OUI 0x%06x\n", oui);
-		}
-		break;
-	case 0x04:
-		data_block = "Speaker Allocation Data Block";
-		printf("  %s\n", data_block.c_str());
-		cta_sadb(x + 1, length);
-		break;
-	case 0x05:
-		printf("  VESA DTC Data Block\n");
-		hex_block("  ", x + 1, length);
-		break;
-	case 0x07:
-		printf("  Extended tag: ");
-		switch (x[1]) {
-		case 0x00:
-			data_block = "Video Capability Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_vcdb(x + 2, length - 1);
-			break;
-		case 0x01:
-			oui = (x[4] << 16) + (x[3] << 8) + x[2];
-			printf("Vendor-Specific Video Data Block, OUI 0x%06x", oui);
-			name = oui_name(oui);
-			if (oui == 0x90848b) {
-				data_block = "Vendor-Specific Video Data Block (HDR10+)";
-				printf(" (HDR10+)\n");
-				cta_hdr10plus(x + 5, length - 4);
-			} else if (name) {
-				data_block = std::string("Vendor-Specific Data Block (") + name + ")";
-				printf(" (%s)\n", name);
-				hex_block("    ", x + 5, length - 4);
-			} else {
-				printf("\n");
-				hex_block("    ", x + 5, length - 4);
-				data_block.clear();
-				warn("Unknown Extended Vendor-Specific Data Block, OUI 0x%06x\n", oui);
-			}
-			break;
-		case 0x02:
-			printf("VESA Video Display Device Data Block\n");
-			hex_block("  ", x + 2, length - 1);
-			break;
-		case 0x03:
-			printf("VESA Video Timing Block Extension\n");
-			hex_block("  ", x + 2, length - 1);
-			break;
-		case 0x04:
-			printf("Reserved for HDMI Video Data Block\n");
-			hex_block("  ", x + 2, length - 1);
-			break;
-		case 0x05:
-			data_block = "Colorimetry Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_colorimetry_block(x + 2, length - 1);
-			break;
-		case 0x06:
-			data_block = "HDR Static Metadata Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_hdr_static_metadata_block(x + 2, length - 1);
-			break;
-		case 0x07:
-			data_block = "HDR Dynamic Metadata Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_hdr_dyn_metadata_block(x + 2, length - 1);
-			break;
-		case 0x0d:
-			data_block = "Video Format Preference Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_vfpdb(x + 2, length - 1);
-			break;
-		case 0x0e:
-			data_block = "YCbCr 4:2:0 Video Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_svd(x + 2, length - 1, 1);
-			break;
-		case 0x0f:
-			data_block = "YCbCr 4:2:0 Capability Map Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_y420cmdb(x + 2, length - 1);
-			break;
-		case 0x10:
-			printf("Reserved for CTA Miscellaneous Audio Fields\n");
-			hex_block("  ", x + 2, length - 1);
-			break;
-		case 0x11:
-			printf("Vendor-Specific Audio Data Block\n");
-			hex_block("  ", x + 2, length - 1);
-			break;
-		case 0x12:
-			data_block = "HDMI Audio Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_hdmi_audio_block(x + 2, length - 1);
-			break;
-		case 0x13:
-			data_block = "Room Configuration Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_rcdb(x + 2, length - 1);
-			break;
-		case 0x14:
-			data_block = "Speaker Location Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_sldb(x + 2, length - 1);
-			break;
-		case 0x20:
-			printf("InfoFrame Data Block\n");
-			cta_ifdb(x + 2, length - 1);
-			break;
-		case 0x78:
-			data_block = "HDMI Forum EDID Extension Override Data Block";
-			printf("%s\n", data_block.c_str());
-			cta_hf_eeodb(x + 2, length - 1);
-			// This must be the first CTA block
-			if (!first_block)
-				fail("Block starts at a wrong offset\n");
-			break;
-		case 0x79:
-			data_block = "HDMI Forum Sink Capability Data Block";
-			printf("%s\n", data_block.c_str());
-			if (!last_block_was_hdmi_vsdb)
-				fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB\n");
-			if (have_hf_scdb || have_hf_vsdb)
-				fail("Duplicate HDMI Forum VSDB/SCDB\n");
-			if (x[2] || x[3])
-				printf("  Non-zero SCDB reserved fields!\n");
-			cta_hf_scdb(x + 4, length - 3);
-			have_hf_scdb = 1;
-			break;
-		default:
-			if (x[1] <= 12)
-				printf("Unknown CTA Video-Related");
-			else if (x[1] <= 31)
-				printf("Unknown CTA Audio-Related");
-			else if (x[1] >= 120 && x[1] <= 127)
-				printf("Unknown CTA HDMI-Related");
-			else
-				printf("Unknown CTA");
-			printf(" Data Block (extended tag 0x%02x, length %u)\n", x[1], length - 1);
-			hex_block("    ", x + 2, length - 1);
-			data_block.clear();
-			warn("Unknown Extended CTA Data Block 0x%02x\n", x[1]);
-			break;
-		}
+	case 0x004: cta_sadb(x + 1, length); break;
+	case 0x700: cta_vcdb(x + 2, length - 1); break;
+	case 0x701|kOUI_HDR10: cta_hdr10plus(x + 5, length - 4); break;
+	case 0x705: cta_colorimetry_block(x + 2, length - 1); break;
+	case 0x706: cta_hdr_static_metadata_block(x + 2, length - 1); break;
+	case 0x707: cta_hdr_dyn_metadata_block(x + 2, length - 1); break;
+	case 0x70d: cta_vfpdb(x + 2, length - 1); break;
+	case 0x70e: cta_svd(x + 2, length - 1, 1); break;
+	case 0x70f: cta_y420cmdb(x + 2, length - 1); break;
+	case 0x712: cta_hdmi_audio_block(x + 2, length - 1); break;
+	case 0x713: cta_rcdb(x + 2, length - 1); break;
+	case 0x714: cta_sldb(x + 2, length - 1); break;
+	case 0x720: cta_ifdb(x + 2, length - 1); break;
+	case 0x778: 
+		cta_hf_eeodb(x + 2, length - 1);
+		// This must be the first CTA block
+		if (cta_block_number != 0)
+			fail("Block starts at a wrong offset\n");
 		break;
-	default: {
-		unsigned tag = (*x & 0xe0) >> 5;
-		unsigned length = *x & 0x1f;
-		printf("  Unknown CTA tag 0x%02x, length %u\n", tag, length);
-		hex_block("    ", x + 1, length);
-		data_block.clear();
-		warn("Unknown CTA Data Block %u\n", tag);
+	case 0x779:
+		if (previous_cta_tag != (0x003|kOUI_HDMI))
+			fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB\n");
+		if (have_hf_scdb || have_hf_vsdb)
+			fail("Duplicate HDMI Forum VSDB/SCDB\n");
+		if (x[2] || x[3])
+			printf("    Non-zero SCDB reserved fields!\n");
+		cta_hf_scdb(x + 4, length - 3);
+		have_hf_scdb = true;
 		break;
+	default:
+		warn("Unknown %s\n", data_block.c_str());
+		hex_block("    ", x + 1 + extended + (ouinum ? 3 : 0), length - (extended + (ouinum ? 3 : 0)));
 	}
-	}
-	first_block = 0;
-	last_block_was_hdmi_vsdb = 0;
+	cta_block_number++;
+	previous_cta_tag = tag;
 }
 
 void edid_state::preparse_cta_block(const unsigned char *x)
