1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2009 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 // -----------------------------------------------------------------------
26 
27 #include "systems/base/voice_cache.h"
28 
29 #include <boost/algorithm/string.hpp>
30 #include <boost/filesystem/path.hpp>
31 #include <iomanip>
32 #include <sstream>
33 #include <string>
34 
35 #include "systems/base/koepac_voice_archive.h"
36 #include "systems/base/nwk_voice_archive.h"
37 #include "systems/base/ovk_voice_archive.h"
38 #include "systems/base/ovk_voice_sample.h"
39 #include "systems/base/sound_system.h"
40 #include "systems/base/system.h"
41 #include "systems/base/voice_archive.h"
42 #include "utilities/exception.h"
43 
44 const int ID_RADIX = 100000;
45 
46 using boost::iends_with;
47 using std::string;
48 
49 namespace fs = boost::filesystem;
50 
VoiceCache(SoundSystem & sound_system)51 VoiceCache::VoiceCache(SoundSystem& sound_system)
52     : sound_system_(sound_system), file_cache_(7) {}
53 
~VoiceCache()54 VoiceCache::~VoiceCache() {}
55 
Find(int id)56 std::shared_ptr<VoiceSample> VoiceCache::Find(int id) {
57   int file_no = id / ID_RADIX;
58   int index = id % ID_RADIX;
59 
60   std::shared_ptr<VoiceArchive> archive = file_cache_.fetch(file_no);
61   if (archive) {
62     return archive->FindSample(index);
63   } else {
64     archive = FindArchive(file_no);
65     if (archive) {
66       // Cache for later use.
67       file_cache_.insert(file_no, archive);
68       return archive->FindSample(index);
69     } else {
70       // There aren't any archives with |file_no|. Look for an individual file
71       // instead.
72       std::shared_ptr<VoiceSample> sample =
73           FindUnpackedSample(file_no, index);
74       if (sample) {
75         return sample;
76       } else {
77         throw rlvm::Exception("No such voice archive or sample");
78       }
79     }
80   }
81 }
82 
FindArchive(int file_no) const83 std::shared_ptr<VoiceArchive> VoiceCache::FindArchive(int file_no) const {
84   std::ostringstream oss;
85   oss << "z" << std::setw(4) << std::setfill('0') << file_no;
86 
87   fs::path file =
88       sound_system_.system().FindFile(oss.str(), KOE_ARCHIVE_FILETYPES);
89   if (file.empty()) {
90     return std::shared_ptr<VoiceArchive>();
91   }
92 
93   string file_str = file.string();
94   if (iends_with(file_str, "ovk")) {
95     return std::shared_ptr<VoiceArchive>(new OVKVoiceArchive(file, file_no));
96   } else if (iends_with(file_str, "nwk")) {
97     return std::shared_ptr<VoiceArchive>(new NWKVoiceArchive(file, file_no));
98   } else if (iends_with(file_str, "koe")) {
99     return std::shared_ptr<VoiceArchive>(
100         new KOEPACVoiceArchive(file, file_no));
101   }
102 
103   return std::shared_ptr<VoiceArchive>();
104 }
105 
FindUnpackedSample(int file_no,int index) const106 std::shared_ptr<VoiceSample> VoiceCache::FindUnpackedSample(int file_no,
107                                                               int index) const {
108   // Loose voice files are packed into directories, like:
109   // /KOE/0008/z000800073.ogg. We only need to search for the filename though.
110   std::ostringstream oss;
111   oss << "z" << std::setw(4) << std::setfill('0') << file_no << std::setw(5)
112       << std::setfill('0') << index;
113 
114   fs::path file =
115       sound_system_.system().FindFile(oss.str(), KOE_LOOSE_FILETYPES);
116   string file_str = file.string();
117 
118   if (iends_with(file_str, "ogg")) {
119     return std::shared_ptr<VoiceSample>(new OVKVoiceSample(file));
120   }
121 
122   return std::shared_ptr<VoiceSample>();
123 }
124