diff --git a/src/components/text_boxes.rs b/src/components/text_boxes.rs
index 63ea38b..bf2bceb 100644
--- a/src/components/text_boxes.rs
+++ b/src/components/text_boxes.rs
@@ -1,4 +1,5 @@
 use crate::common::{Color, Rect};
+use crate::engine_constants::AnimatedFace;
 use crate::entity::GameEntity;
 use crate::frame::Frame;
 use crate::framework::context::Context;
@@ -9,22 +10,49 @@ use crate::scripting::tsc::text_script::{ConfirmSelection, TextScriptExecutionSt
 use crate::shared_game_state::SharedGameState;
 
 pub struct TextBoxes {
+    pub slide_in: u8,
     pub anim_counter: usize,
+    animated_face: AnimatedFace,
 }
 
 const FACE_TEX: &str = "Face";
-const SWITCH_FACE_TEX: [&str; 4] = ["Face1", "Face2", "Face3", "Face4"];
+const SWITCH_FACE_TEX: [&str; 5] = ["Face1", "Face2", "Face3", "Face4", "Face5"];
 
 impl TextBoxes {
     pub fn new() -> TextBoxes {
-        TextBoxes { anim_counter: 0 }
+        TextBoxes {
+            slide_in: 7,
+            anim_counter: 0,
+            animated_face: AnimatedFace { face_id: 0, anim_id: 0, anim_frames: vec![(0, 0)] },
+        }
     }
 }
 
 impl GameEntity<()> for TextBoxes {
     fn tick(&mut self, state: &mut SharedGameState, _custom: ()) -> GameResult {
         if state.textscript_vm.face != 0 {
+            self.slide_in = self.slide_in.saturating_sub(1);
             self.anim_counter = self.anim_counter.wrapping_add(1);
+
+            let face_num = state.textscript_vm.face % 100;
+            let animation = state.textscript_vm.face % 1000 / 100;
+
+            if state.constants.textscript.animated_face_pics
+                && (self.animated_face.anim_id != animation || self.animated_face.face_id != face_num)
+            {
+                self.animated_face = state
+                    .constants
+                    .animated_face_table
+                    .clone()
+                    .into_iter()
+                    .find(|face| face.face_id == face_num && face.anim_id == animation)
+                    .unwrap_or_else(|| AnimatedFace { face_id: face_num, anim_id: 0, anim_frames: vec![(0, 0)] });
+            }
+
+            if self.anim_counter > self.animated_face.anim_frames.first().unwrap().1 as usize {
+                self.animated_face.anim_frames.rotate_left(1);
+                self.anim_counter = 0;
+            }
         }
         Ok(())
     }
@@ -121,16 +149,16 @@ impl GameEntity<()> for TextBoxes {
 
             graphics::set_clip_rect(ctx, Some(clip_rect))?;
 
-            let tex_name = if state.constants.textscript.animated_face_pics { SWITCH_FACE_TEX[0] } else { FACE_TEX };
-            let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex_name)?;
-
             // switch version uses 1xxx flag to show a flipped version of face
             let flip = state.textscript_vm.face > 1000;
-            // x1xx flag shows a talking animation
-            let _talking = (state.textscript_vm.face % 1000) > 100;
             let face_num = state.textscript_vm.face % 100;
+            let animation_frame = self.animated_face.anim_frames.first().unwrap().0 as usize;
 
-            let face_x = (4.0 + (self.anim_counter.min(7) - 1) as f32 * 8.0) - 52.0;
+            let tex_name =
+                if state.constants.textscript.animated_face_pics { SWITCH_FACE_TEX[animation_frame] } else { FACE_TEX };
+            let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex_name)?;
+
+            let face_x = (4.0 + (6 - self.slide_in) as f32 * 8.0) - 52.0;
 
             batch.add_rect_flip(
                 left_pos + 14.0 + face_x,
diff --git a/src/engine_constants/mod.rs b/src/engine_constants/mod.rs
index ed8fc86..aba5341 100644
--- a/src/engine_constants/mod.rs
+++ b/src/engine_constants/mod.rs
@@ -1,5 +1,5 @@
 use std::collections::HashMap;
-use std::io::{Cursor, Read};
+use std::io::{BufRead, BufReader, Cursor, Read};
 
 use byteorder::{ReadBytesExt, LE};
 use case_insensitive_hashmap::CaseInsensitiveHashMap;
@@ -194,6 +194,13 @@ pub struct WorldConsts {
     pub water_push_rect: Rect<u16>,
 }
 
+#[derive(Debug, Clone)]
+pub struct AnimatedFace {
+    pub face_id: u16,
+    pub anim_id: u16,
+    pub anim_frames: Vec<(u16, u16)>,
+}
+
 #[derive(Debug, Copy, Clone)]
 pub struct TextScriptConsts {
     pub encoding: TextScriptEncoding,
@@ -290,6 +297,7 @@ pub struct EngineConstants {
     pub music_table: Vec<String>,
     pub organya_paths: Vec<String>,
     pub credit_illustration_paths: Vec<String>,
+    pub animated_face_table: Vec<AnimatedFace>,
 }
 
 impl Clone for EngineConstants {
@@ -316,6 +324,7 @@ impl Clone for EngineConstants {
             music_table: self.music_table.clone(),
             organya_paths: self.organya_paths.clone(),
             credit_illustration_paths: self.credit_illustration_paths.clone(),
+            animated_face_table: self.animated_face_table.clone(),
         }
     }
 }
@@ -1586,6 +1595,7 @@ impl EngineConstants {
                 "Resource/BITMAP/".to_owned(), // CSE2E
                 "endpic/".to_owned(),          // NXEngine
             ],
+            animated_face_table: vec![AnimatedFace { face_id: 0, anim_id: 0, anim_frames: vec![(0, 0)] }],
         }
     }
 
@@ -1650,6 +1660,7 @@ impl EngineConstants {
         self.textscript.text_speed_fast = 0;
         self.soundtracks.insert("Famitracks".to_owned(), "/base/ogg17/".to_owned());
         self.soundtracks.insert("Ridiculon".to_owned(), "/base/ogg_ridic/".to_owned());
+        self.animated_face_table.push(AnimatedFace { face_id: 5, anim_id: 4, anim_frames: vec![(4, 0)] }); // Teethrog fix
         self.game.tile_offset_x = 3;
     }
 
@@ -1713,4 +1724,48 @@ impl EngineConstants {
 
         Ok(())
     }
+
+    /// Load in the `faceanm.dat` file that details the Switch extensions to the <FAC command
+    /// It's actually a text file, go figure
+    pub fn load_animated_faces(&mut self, ctx: &mut Context) -> GameResult {
+        if filesystem::exists(ctx, "/base/faceanm.dat") {
+            let file = filesystem::open(ctx, "/base/faceanm.dat")?;
+            let buf = BufReader::new(file);
+            let mut face_id = 1;
+            let mut anim_id = 0;
+
+            for line in buf.lines() {
+                let line_str = line?.to_owned().replace(",", " ");
+                let mut anim_frames = Vec::new();
+
+                if line_str.find("\\") == None {
+                    continue;
+                } else if line_str == "\\end" {
+                    face_id += 1;
+                    anim_id = 0;
+                    continue;
+                }
+
+                for split in line_str.split_whitespace() {
+                    // The animation labels aren't actually used
+                    // There are also comments on some lines that we need to ignore
+                    if split.find("\\") != None {
+                        continue;
+                    } else if split.find("//") != None {
+                        break;
+                    }
+                    let mut parse = split.split(":");
+                    let frame = (
+                        parse.next().unwrap().parse::<u16>().unwrap_or(0),
+                        parse.next().unwrap().parse::<u16>().unwrap_or(0),
+                    );
+                    anim_frames.push(frame);
+                }
+
+                self.animated_face_table.push(AnimatedFace { face_id, anim_id, anim_frames });
+                anim_id += 1;
+            }
+        }
+        Ok(())
+    }
 }
diff --git a/src/scripting/tsc/text_script.rs b/src/scripting/tsc/text_script.rs
index 0defeeb..4092bd4 100644
--- a/src/scripting/tsc/text_script.rs
+++ b/src/scripting/tsc/text_script.rs
@@ -991,7 +991,7 @@ impl TextScriptVM {
                 let face = read_cur_varint(&mut cursor)? as u16;
                 // Switch uses xx00 for face animation states
                 if face % 100 != state.textscript_vm.face % 100 {
-                    game_scene.text_boxes.anim_counter = 0;
+                    game_scene.text_boxes.slide_in = 7;
                 }
                 state.textscript_vm.face = face;
 
diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs
index 0f2edd6..5750220 100644
--- a/src/shared_game_state.rs
+++ b/src/shared_game_state.rs
@@ -201,6 +201,7 @@ impl SharedGameState {
             constants.apply_csplus_patches(&sound_manager);
             constants.apply_csplus_nx_patches();
             constants.load_csplus_tables(ctx)?;
+            constants.load_animated_faces(ctx)?;
             base_path = "/base/";
         } else if filesystem::exists(ctx, "/base/Nicalis.bmp") || filesystem::exists(ctx, "/base/Nicalis.png") {
             info!("Cave Story+ (PC) data files detected.");