diff --git a/LEGO1/lego/legoomni/include/jukebox.h b/LEGO1/lego/legoomni/include/jukebox.h
index f760641d..9879a446 100644
--- a/LEGO1/lego/legoomni/include/jukebox.h
+++ b/LEGO1/lego/legoomni/include/jukebox.h
@@ -34,6 +34,7 @@ public:
 
 	// SYNTHETIC: LEGO1 0x1005d810
 	// JukeBox::`scalar deleting destructor'
+
 	enum JukeBoxScript {
 		e_mamaPapaBrickolini,
 		e_jailUnused,
@@ -65,29 +66,18 @@ public:
 		e_legoRadioReminder2,
 
 		e_legoRadioRacingAd,
-
 		e_legoRadioNews1,
 		e_legoRadioNews2,
-
 		e_legoRadioPizzaAd1,
-
 		e_legoRadioBricksterPSA,
-
 		e_legoRadioSports1,
-
 		e_legoRadioIntermission1,
 		e_legoRadioIntermission2,
-
 		e_legoRadioPizzaAd2,
-
 		e_legoRadioWeatherReport,
-
 		e_legoRadioSports2,
-
 		e_legoRadioPizzaAd3,
-
 		e_legoRadioIntermission3,
-
 		e_legoRadioSuperStoreAd,
 
 		e_legoRadioLuckyYou,
diff --git a/LEGO1/lego/legoomni/include/legostate.h b/LEGO1/lego/legoomni/include/legostate.h
index 41942312..de66cdc6 100644
--- a/LEGO1/lego/legoomni/include/legostate.h
+++ b/LEGO1/lego/legoomni/include/legostate.h
@@ -43,6 +43,19 @@ public:
 
 	// SYNTHETIC: LEGO1 0x10006160
 	// LegoState::`scalar deleting destructor'
+
+	// SIZE 0x0c
+	struct StateStruct {
+		void* m_unk0x00;      // 0x00
+		undefined2 m_unk0x04; // 0x04
+		undefined2 m_unk0x06; // 0x06
+		MxU16 m_unk0x08;      // 0x08
+
+		StateStruct();
+
+		MxU32 FUN_10014d00();
+		MxBool FUN_10014de0(MxU32 p_objectId);
+	};
 };
 
 #endif // LEGOSTATE_H
diff --git a/LEGO1/lego/legoomni/include/legovehiclebuildstate.h b/LEGO1/lego/legoomni/include/legovehiclebuildstate.h
index b0463448..7a78b4ed 100644
--- a/LEGO1/lego/legoomni/include/legovehiclebuildstate.h
+++ b/LEGO1/lego/legoomni/include/legovehiclebuildstate.h
@@ -28,18 +28,8 @@ public:
 	// SYNTHETIC: LEGO1 0x100260a0
 	// LegoVehicleBuildState::`scalar deleting destructor'
 
-public:
-	struct UnkStruct {
-		undefined4 m_unk0x00; // 0x00
-		undefined2 m_unk0x04; // 0x04
-		undefined2 m_unk0x06; // 0x06
-		undefined2 m_unk0x08; // 0x08
-
-		UnkStruct();
-	};
-
 private:
-	UnkStruct m_unk0x08[4]; // 0x08
+	StateStruct m_unk0x08[4]; // 0x08
 
 	// This can be one of the following:
 	// * LegoRaceCarBuildState
diff --git a/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h b/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h
index 787484b0..cb2e3435 100644
--- a/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h
+++ b/LEGO1/lego/legoomni/include/mxbackgroundaudiomanager.h
@@ -31,6 +31,8 @@ public:
 		return !strcmp(p_name, MxBackgroundAudioManager::ClassName()) || MxCore::IsA(p_name);
 	}
 
+	inline MxBool GetMusicEnabled() { return m_musicEnabled; }
+
 	void StartAction(MxParam& p_param);
 	void StopAction(MxParam& p_param);
 	MxResult PlayMusic(MxDSAction& p_action, undefined4 p_unk0x140, undefined4 p_unk0x13c);
diff --git a/LEGO1/lego/legoomni/include/mxcontrolpresenter.h b/LEGO1/lego/legoomni/include/mxcontrolpresenter.h
index 0903f557..1ead16da 100644
--- a/LEGO1/lego/legoomni/include/mxcontrolpresenter.h
+++ b/LEGO1/lego/legoomni/include/mxcontrolpresenter.h
@@ -43,7 +43,6 @@ public:
 
 private:
 	MxBool FUN_10044270(MxS32 p_x, MxS32 p_y, MxVideoPresenter* p_presenter);
-	void FUN_10044540(undefined2);
 
 	undefined2 m_unk0x4c; // 0x4c
 	MxS16 m_unk0x4e;      // 0x4e
diff --git a/LEGO1/lego/legoomni/include/radio.h b/LEGO1/lego/legoomni/include/radio.h
index 4edf734f..347a22d1 100644
--- a/LEGO1/lego/legoomni/include/radio.h
+++ b/LEGO1/lego/legoomni/include/radio.h
@@ -1,10 +1,13 @@
 #ifndef RADIO_H
 #define RADIO_H
 
+#include "legocontrolmanager.h"
+#include "mxactionnotificationparam.h"
 #include "mxcore.h"
 #include "radiostate.h"
 
 // VTABLE: LEGO1 0x100d6d10
+// SIZE 0x10
 class Radio : public MxCore {
 public:
 	Radio();
@@ -31,10 +34,15 @@ public:
 	// Radio::`scalar deleting destructor'
 
 private:
-	RadioState* m_state; // 0x08
-	MxBool m_unk0x0c;    // 0x0c
+	RadioState* m_state;               // 0x08
+	MxBool m_unk0x0c;                  // 0x0c
+	MxBool m_bgAudioPreviouslyEnabled; // 0x0d
 
 	void CreateRadioState();
+	void Play();
+	void Stop();
+	MxLong HandleEndAction(MxEndActionNotificationParam& p_param);
+	MxLong HandleNotification17(LegoControlManagerEvent& p_param);
 };
 
 #endif // RADIO_H
diff --git a/LEGO1/lego/legoomni/include/radiostate.h b/LEGO1/lego/legoomni/include/radiostate.h
index 68dc154a..adfbe623 100644
--- a/LEGO1/lego/legoomni/include/radiostate.h
+++ b/LEGO1/lego/legoomni/include/radiostate.h
@@ -2,6 +2,7 @@
 #define RADIOSTATE_H
 
 #include "legostate.h"
+#include "mxdsaction.h"
 
 // VTABLE: LEGO1 0x100d6d28
 // SIZE 0x30
@@ -26,6 +27,18 @@ public:
 
 	// SYNTHETIC: LEGO1 0x1002d020
 	// RadioState::`scalar deleting destructor'
+
+	inline MxBool IsActive() { return m_active; }
+
+	inline void SetActive(MxBool p_active) { m_active = p_active; }
+
+	undefined4 FUN_1002d090();
+	MxBool FUN_1002d0c0(const MxAtomId& p_atom, MxU32 p_objectId);
+
+private:
+	StateStruct m_unk0x08[3]; // 0x08
+	MxS16 m_unk0x2c;          // 0x2c
+	MxBool m_active;          // 0x2e
 };
 
 #endif // RADIOSTATE_H
diff --git a/LEGO1/lego/legoomni/src/build/legovehiclebuildstate.cpp b/LEGO1/lego/legoomni/src/build/legovehiclebuildstate.cpp
index f950c429..8cf60edd 100644
--- a/LEGO1/lego/legoomni/src/build/legovehiclebuildstate.cpp
+++ b/LEGO1/lego/legoomni/src/build/legovehiclebuildstate.cpp
@@ -2,17 +2,7 @@
 
 #include "decomp.h"
 
-DECOMP_SIZE_ASSERT(LegoVehicleBuildState, 0x50); // 1000acd7
-DECOMP_SIZE_ASSERT(LegoVehicleBuildState::UnkStruct, 0x0c);
-
-// FUNCTION: LEGO1 0x10017c00
-LegoVehicleBuildState::UnkStruct::UnkStruct()
-{
-	m_unk0x04 = 0;
-	m_unk0x00 = 0;
-	m_unk0x06 = 0;
-	m_unk0x08 = 0;
-}
+DECOMP_SIZE_ASSERT(LegoVehicleBuildState, 0x50)
 
 // FUNCTION: LEGO1 0x10025f30
 LegoVehicleBuildState::LegoVehicleBuildState(char* p_classType)
diff --git a/LEGO1/lego/legoomni/src/common/legostate.cpp b/LEGO1/lego/legoomni/src/common/legostate.cpp
index c9946492..5d3bcf4e 100644
--- a/LEGO1/lego/legoomni/src/common/legostate.cpp
+++ b/LEGO1/lego/legoomni/src/common/legostate.cpp
@@ -1,3 +1,27 @@
 #include "legostate.h"
 
 DECOMP_SIZE_ASSERT(LegoState, 0x08)
+DECOMP_SIZE_ASSERT(LegoState::StateStruct, 0x0c)
+
+// STUB: LEGO1 0x10014d00
+MxU32 LegoState::StateStruct::FUN_10014d00()
+{
+	// TODO
+	return 0;
+}
+
+// STUB: LEGO1 0x10014de0
+MxBool LegoState::StateStruct::FUN_10014de0(MxU32 p_objectId)
+{
+	// TODO
+	return FALSE;
+}
+
+// FUNCTION: LEGO1 0x10017c00
+LegoState::StateStruct::StateStruct()
+{
+	m_unk0x04 = 0;
+	m_unk0x00 = 0;
+	m_unk0x06 = 0;
+	m_unk0x08 = 0;
+}
diff --git a/LEGO1/lego/legoomni/src/isle/radio.cpp b/LEGO1/lego/legoomni/src/isle/radio.cpp
index c0182a1c..ad5d96b7 100644
--- a/LEGO1/lego/legoomni/src/isle/radio.cpp
+++ b/LEGO1/lego/legoomni/src/isle/radio.cpp
@@ -3,6 +3,8 @@
 #include "legocontrolmanager.h"
 #include "legogamestate.h"
 #include "legoomni.h"
+#include "mxbackgroundaudiomanager.h"
+#include "mxcontrolpresenter.h"
 #include "mxnotificationmanager.h"
 
 DECOMP_SIZE_ASSERT(Radio, 0x10);
@@ -17,16 +19,119 @@ Radio::Radio()
 	CreateRadioState();
 }
 
-// STUB: LEGO1 0x1002c990
+// FUNCTION: LEGO1 0x1002c990
 Radio::~Radio()
 {
-	// TODO
+	if (m_state->IsActive()) {
+		BackgroundAudioManager()->Stop();
+		m_state->SetActive(FALSE);
+	}
+
+	ControlManager()->Unregister(this);
+	NotificationManager()->Unregister(this);
 }
 
-// STUB: LEGO1 0x1002ca30
+// FUNCTION: LEGO1 0x1002ca30
 MxLong Radio::Notify(MxParam& p_param)
 {
-	// TODO
+	MxLong result = 0;
+
+	if (m_unk0x0c) {
+		switch (((MxNotificationParam&) p_param).GetType()) {
+		case c_notificationEndAction:
+			result = HandleEndAction((MxEndActionNotificationParam&) p_param);
+			break;
+		case c_notificationType17:
+			result = HandleNotification17((LegoControlManagerEvent&) p_param);
+			break;
+		}
+	}
+
+	return result;
+}
+
+// FUNCTION: LEGO1 0x1002ca70
+void Radio::Play()
+{
+	if (!m_state->IsActive()) {
+		GetCurrentWorld();
+
+		MxDSAction action;
+		action.SetObjectId(m_state->FUN_1002d090());
+		action.SetAtomId(*g_jukeboxScript);
+		action.SetLoopCount(1);
+
+		m_bgAudioPreviouslyEnabled = BackgroundAudioManager()->GetMusicEnabled();
+		if (!m_bgAudioPreviouslyEnabled) {
+			BackgroundAudioManager()->Enable(TRUE);
+		}
+
+		BackgroundAudioManager()->PlayMusic(action, 3, 4);
+		m_state->SetActive(TRUE);
+	}
+}
+
+// FUNCTION: LEGO1 0x1002cb70
+void Radio::Stop()
+{
+	if (m_state->IsActive()) {
+		LegoWorld* world = GetCurrentWorld();
+
+		MxControlPresenter* presenter = (MxControlPresenter*) world->Find(world->GetAtom(), 18);
+
+		if (presenter)
+			presenter->VTable0x6c(0);
+
+		BackgroundAudioManager()->Stop();
+		BackgroundAudioManager()->Enable(m_bgAudioPreviouslyEnabled);
+		m_state->SetActive(FALSE);
+	}
+}
+
+// FUNCTION: LEGO1 0x1002cbc0
+MxLong Radio::HandleNotification17(LegoControlManagerEvent& p_param)
+{
+	MxDSAction action; // Unused
+	MxS32 objectId = p_param.GetClickedObjectId();
+
+	if (objectId == 18) {
+		if (m_state->IsActive()) {
+			Stop();
+		}
+		else {
+			Play();
+		}
+
+		if (GetCurrentWorld()) {
+#ifdef COMPAT_MODE
+			MxNotificationParam param(c_notificationEndAction, this);
+			GetCurrentWorld()->Notify(param);
+#else
+			GetCurrentWorld()->Notify(MxNotificationParam(c_notificationType0, this));
+#endif
+		}
+
+		return 1;
+	}
+
+	return 0;
+}
+
+// FUNCTION: LEGO1 0x1002ccc0
+MxLong Radio::HandleEndAction(MxEndActionNotificationParam& p_param)
+{
+	if (m_state->IsActive() &&
+		m_state->FUN_1002d0c0(p_param.GetAction()->GetAtomId(), p_param.GetAction()->GetObjectId())) {
+
+		MxDSAction action;
+		action.SetAtomId(*g_jukeboxScript);
+		action.SetObjectId(m_state->FUN_1002d090());
+		action.SetLoopCount(1);
+
+		BackgroundAudioManager()->PlayMusic(action, 3, 4);
+		return 1;
+	}
+
 	return 0;
 }
 
diff --git a/LEGO1/lego/legoomni/src/isle/radiostate.cpp b/LEGO1/lego/legoomni/src/isle/radiostate.cpp
index 08aa91fa..cb692fcf 100644
--- a/LEGO1/lego/legoomni/src/isle/radiostate.cpp
+++ b/LEGO1/lego/legoomni/src/isle/radiostate.cpp
@@ -1,14 +1,115 @@
 #include "radiostate.h"
 
-// STUB: LEGO1 0x1002ce10
+#include "jukebox.h"
+#include "legoomni.h"
+#include "mxtimer.h"
+
+// GLOBAL: LEGO1 0x100f3218
+JukeBox::JukeBoxScript g_unk0x100f3218[6] = {
+	JukeBox::e_legoRadioReminder1,
+	JukeBox::e_legoRadioJingle1,
+	JukeBox::e_legoRadioJingle2,
+	JukeBox::e_legoRadioJingle3,
+	JukeBox::e_legoRadioJingle4,
+	JukeBox::e_legoRadioReminder2
+};
+
+// GLOBAL: LEGO1 0x100f3230
+JukeBox::JukeBoxScript g_unk0x100f3230[14] = {
+	JukeBox::e_legoRadioRacingAd,
+	JukeBox::e_legoRadioNews1,
+	JukeBox::e_legoRadioNews2,
+	JukeBox::e_legoRadioPizzaAd1,
+	JukeBox::e_legoRadioBricksterPSA,
+	JukeBox::e_legoRadioSports1,
+	JukeBox::e_legoRadioIntermission1,
+	JukeBox::e_legoRadioIntermission2,
+	JukeBox::e_legoRadioPizzaAd2,
+	JukeBox::e_legoRadioWeatherReport,
+	JukeBox::e_legoRadioSports2,
+	JukeBox::e_legoRadioPizzaAd3,
+	JukeBox::e_legoRadioIntermission3,
+	JukeBox::e_legoRadioSuperStoreAd,
+};
+
+// GLOBAL: LEGO1 0x100f3268
+JukeBox::JukeBoxScript g_unk0x100f3268[9] = {
+	JukeBox::e_centralRoads,
+	JukeBox::e_beachBlvd,
+	JukeBox::e_residentialArea,
+	JukeBox::e_legoRadioLuckyYou,
+	JukeBox::e_legoRadioJazzInterlude,
+	JukeBox::e_legoRadioPianoInterlude1,
+	JukeBox::e_legoRadioPoliceStation,
+	JukeBox::e_legoRadioPianoInterlude2,
+	JukeBox::e_legoRadioCredits,
+};
+
+// FUNCTION: LEGO1 0x1002ce10
 RadioState::RadioState()
 {
-	// TODO
+	srand(Timer()->GetTime());
+
+	MxS32 random = rand();
+	m_unk0x2c = random % 3;
+
+	// TODO: Most likely inline function
+
+	m_unk0x08[0].m_unk0x04 = sizeof(g_unk0x100f3218) / sizeof(g_unk0x100f3218[0]);
+	m_unk0x08[0].m_unk0x00 = g_unk0x100f3218;
+	m_unk0x08[0].m_unk0x08 = 0;
+	m_unk0x08[0].m_unk0x06 = 0;
+
+	random = rand();
+
+	m_unk0x08[1].m_unk0x08 = 0;
+	m_unk0x08[1].m_unk0x06 = 0;
+	m_unk0x08[1].m_unk0x04 = sizeof(g_unk0x100f3230) / sizeof(g_unk0x100f3230[0]);
+	m_unk0x08[1].m_unk0x00 = g_unk0x100f3230;
+
+	m_unk0x08[0].m_unk0x08 = (MxU32) random % (sizeof(g_unk0x100f3218) / sizeof(g_unk0x100f3218[0]));
+	random = rand();
+
+	m_unk0x08[2].m_unk0x08 = 0;
+	m_unk0x08[2].m_unk0x06 = 0;
+	m_unk0x08[2].m_unk0x04 = sizeof(g_unk0x100f3268) / sizeof(g_unk0x100f3268[0]);
+	m_unk0x08[2].m_unk0x00 = g_unk0x100f3268;
+
+	m_unk0x08[1].m_unk0x08 = (MxU32) random % (sizeof(g_unk0x100f3230) / sizeof(g_unk0x100f3230[0]));
+	random = rand();
+
+	m_unk0x08[2].m_unk0x08 = (MxU32) random % (sizeof(g_unk0x100f3268) / sizeof(g_unk0x100f3268[0]));
+
+	m_active = FALSE;
 }
 
-// STUB: LEGO1 0x1002cf50
+// FUNCTION: LEGO1 0x1002cf50
 MxBool RadioState::VTable0x14()
 {
-	// TODO
+	return FALSE;
+}
+
+// FUNCTION: LEGO1 0x1002d090
+MxU32 RadioState::FUN_1002d090()
+{
+	if (m_unk0x2c == 2) {
+		m_unk0x2c = 0;
+	}
+	else {
+		m_unk0x2c++;
+	}
+
+	return m_unk0x08[m_unk0x2c].FUN_10014d00();
+}
+
+// FUNCTION: LEGO1 0x1002d0c0
+MxBool RadioState::FUN_1002d0c0(const MxAtomId& p_atom, MxU32 p_objectId)
+{
+	if (*g_jukeboxScript == p_atom) {
+		for (MxS16 i = 0; i < 3; i++)
+			if (m_unk0x08[i].FUN_10014de0(p_objectId))
+				return TRUE;
+	}
+
 	return FALSE;
 }