diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj
index 5b7f842c8..24c2b772f 100644
--- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
+++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
@@ -27,6 +27,7 @@
 		4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; };
 		4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
 		4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; };
+		4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
 		4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; };
 		4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
 		4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
@@ -361,6 +362,7 @@
 		4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Tape.hpp; sourceTree = "<group>"; };
 		4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapeUEF.cpp; sourceTree = "<group>"; };
 		4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapeUEF.hpp; sourceTree = "<group>"; };
+		4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
 		4B92EAC91B7C112B00246143 /* TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingTests.swift; sourceTree = "<group>"; };
 		4BAE587D1C447B7A005B9AF0 /* KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyCodes.h; sourceTree = "<group>"; };
 		4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -655,6 +657,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -796,6 +799,7 @@
 		4B69FB411C4D941400B5F0AA /* Formats */ = {
 			isa = PBXGroup;
 			children = (
+				4B69FB451C4D950F00B5F0AA /* libz.tbd */,
 				4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */,
 				4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */,
 			);
diff --git a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift
index 75e42624a..e735c1a26 100644
--- a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift	
+++ b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift	
@@ -39,10 +39,11 @@ class ElectronDocument: MachineDocument {
 		print(url)
 		print(typeName)
 		switch typeName {
+			case "Electron/BBC Tape Image": // this somewhat implies I've misunderstood the info.plist, doesn't it?
+				electron.openUEFAtURL(url)
 			default:
 				let fileWrapper = try NSFileWrapper(URL: url, options: NSFileWrapperReadingOptions(rawValue: 0))
 				try self.readFromFileWrapper(fileWrapper, ofType: typeName)
-			break;
 		}
 	}
 
diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h
index aefd4d4e0..306e91ef7 100644
--- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h	
+++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h	
@@ -14,6 +14,7 @@
 - (void)setOSROM:(nonnull NSData *)rom;
 - (void)setBASICROM:(nonnull NSData *)rom;
 - (void)setROM:(nonnull NSData *)rom slot:(int)slot;
+- (void)openUEFAtURL:(NSURL *)URL;
 
 - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
 
diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm
index 446209f94..a7b47dbb0 100644
--- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm	
+++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm	
@@ -10,6 +10,7 @@
 
 #import "Electron.hpp"
 #import "CSMachine+Subclassing.h"
+#import "TapeUEF.hpp"
 
 @implementation CSElectron {
 	Electron::Machine _electron;
@@ -47,6 +48,11 @@
 	_electron.get_crt()->set_delegate(delegate);
 }
 
+- (void)openUEFAtURL:(NSURL *)URL {
+	Storage::UEF tape([URL fileSystemRepresentation]);
+//	_electron.
+}
+
 - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate {
 	_electron.get_speaker()->set_output_rate(sampleRate, 512);
 	_electron.get_speaker()->set_output_quality(15);
diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp
index c724a8c15..6fd56d3ab 100644
--- a/Storage/Tape/Formats/TapeUEF.cpp
+++ b/Storage/Tape/Formats/TapeUEF.cpp
@@ -7,3 +7,97 @@
 //
 
 #include "TapeUEF.hpp"
+#include <string.h>
+
+Storage::UEF::UEF(const char *file_name) :
+	_chunk_id(0), _chunk_length(0), _chunk_position(0),
+	_time_base(1200)
+{
+	_file = gzopen(file_name, "rb");
+
+	char identifier[10];
+	int bytes_read = gzread(_file, identifier, 10);
+	if(bytes_read < 10 || strcmp(identifier, "UEF File!"))
+	{
+		// exception?
+	}
+
+	int minor, major;
+	minor = gzgetc(_file);
+	major = gzgetc(_file);
+
+	if(major > 0 || minor > 10 || major < 0 || minor < 0)
+	{
+		// exception?
+	}
+
+	find_next_tape_chunk();
+}
+
+Storage::UEF::~UEF()
+{
+	gzclose(_file);
+}
+
+void Storage::UEF::reset()
+{
+	gzseek(_file, 12, SEEK_SET);
+}
+
+Storage::Tape::Pulse Storage::UEF::get_next_pulse()
+{
+	Pulse next_pulse;
+
+	return next_pulse;
+}
+
+void Storage::UEF::find_next_tape_chunk()
+{
+	int reset_count = 0;
+
+	while(1)
+	{
+		// read chunk ID
+		_chunk_id = (uint16_t)gzgetc(_file);
+		_chunk_id |= (uint16_t)(gzgetc(_file) << 8);
+
+		_chunk_length = (uint32_t)(gzgetc(_file) << 0);
+		_chunk_length |= (uint32_t)(gzgetc(_file) << 8);
+		_chunk_length |= (uint32_t)(gzgetc(_file) << 16);
+		_chunk_length |= (uint32_t)(gzgetc(_file) << 24);
+
+		printf("%04x: %d\n", _chunk_id, _chunk_length);
+
+		if (gzeof(_file))
+		{
+			reset_count++;
+			if(reset_count == 2) break;
+			reset();
+			continue;
+		}
+
+		switch(_chunk_id)
+		{
+			case 0x0100: case 0x0102: // implicit and explicit bit patterns
+			case 0x0112: case 0x0116: // gaps
+			return;
+
+			case 0x0110: // carrier tone
+				// TODO: read length
+			return;
+			case 0x0111: // carrier tone with dummy byte
+				// TODO: read length
+			return;
+			case 0x0114: // security cycles
+				// TODO: read number, Ps and Ws
+			break;
+
+			case 0x113: // change of base rate
+			break;
+
+			default:
+				gzseek(_file, _chunk_length, SEEK_CUR);
+			break;
+		}
+	}
+}
diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp
index 5cdf7869e..bf13f0ad5 100644
--- a/Storage/Tape/Formats/TapeUEF.hpp
+++ b/Storage/Tape/Formats/TapeUEF.hpp
@@ -10,13 +10,30 @@
 #define TapeUEF_hpp
 
 #include "../Tape.hpp"
+#include <zlib.h>
+#include <stdint.h>
 
-class UEF : public Storage::Tape {
+namespace Storage {
+
+class UEF : public Tape {
 	public:
 		UEF(const char *file_name);
-		Cycle get_next_cycle();
+		~UEF();
+
+		Pulse get_next_pulse();
+		void reset();
 
 	private:
+		gzFile _file;
+		unsigned int _time_base;
+
+		uint16_t _chunk_id;
+		uint32_t _chunk_length;
+		uint32_t _chunk_position;
+
+		void find_next_tape_chunk();
 };
 
+}
+
 #endif /* TapeUEF_hpp */
diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp
index 022bec740..9ee6c1fd0 100644
--- a/Storage/Tape/Tape.hpp
+++ b/Storage/Tape/Tape.hpp
@@ -20,14 +20,14 @@ class Tape {
 			unsigned int length, clock_rate;
 		};
 
-		struct Cycle {
+		struct Pulse {
 			enum {
 				High, Low, Zero
 			} type;
 			Time length;
 		};
 
-		virtual Cycle get_next_cycle() = 0;
+		virtual Pulse get_next_pulse() = 0;
 		virtual void reset() = 0;
 
 		virtual void seek(Time seek_time);