diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index 3494318ca0..2208e19226 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -1,4 +1,5 @@
 set(SHADER_FILES
+    astc_decoder.comp
     block_linear_unswizzle_2d.comp
     block_linear_unswizzle_3d.comp
     convert_depth_to_float.frag
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
index c0fc49768b..1b4bc6103c 100644
--- a/src/video_core/host_shaders/StringShaderHeader.cmake
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -6,7 +6,27 @@ get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
 string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
 string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
 
-file(READ ${SOURCE_FILE} CONTENTS)
+FILE(READ ${SOURCE_FILE} line_contents)
+
+# Replace double quotes with single quotes,
+# as double quotes will be used to wrap the lines
+STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}")
+
+# CMake separates list elements with semicolons, but semicolons
+# are used extensively in the shader code.
+# Replace with a temporary marker, to be reverted later.
+STRING(REGEX REPLACE ";" "{{SEMICOLON}}" line_contents "${line_contents}")
+
+# Make every line an individual element in the CMake list.
+STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}")
+
+# Build the shader string, wrapping each line in double quotes.
+foreach(line IN LISTS line_contents)
+    string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n)
+endforeach()
+
+# Revert the original semicolons in the source.
+STRING(REGEX REPLACE "{{SEMICOLON}}" ";" CONTENTS "${CONTENTS}")
 
 get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY)
 make_directory(${OUTPUT_DIR})
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
index ccdb0d2a9c..929dec39bb 100644
--- a/src/video_core/host_shaders/source_shader.h.in
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -4,6 +4,8 @@
 
 namespace HostShaders {
 
-constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
+constexpr std::string_view @CONTENTS_NAME@ = {
+@CONTENTS@
+};
 
 } // namespace HostShaders
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 2a42206619..d0979dab1a 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -14,6 +14,7 @@
 #include "common/assert.h"
 #include "common/common_types.h"
 #include "common/div_ceil.h"
+#include "video_core/host_shaders/astc_decoder_comp.h"
 #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
 #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
 #include "video_core/host_shaders/opengl_copy_bc4_comp.h"
@@ -62,17 +63,12 @@ size_t NumPixelsInCopy(const VideoCommon::ImageCopy& copy) {
 } // Anonymous namespace
 
 UtilShaders::UtilShaders(ProgramManager& program_manager_)
-    : program_manager{program_manager_},
+    : program_manager{program_manager_}, astc_decoder_program(MakeProgram(ASTC_DECODER_COMP)),
       block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
       block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
       pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
       copy_bgra_program(MakeProgram(OPENGL_COPY_BGRA_COMP)),
       copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
-    // TODO: Load shader string as a header
-    std::string astc_path = "astc_decoder.comp";
-    std::ifstream t(astc_path);
-    std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
-    astc_decoder_program = MakeProgram(str);
     MakeBuffers();
 }