blob: 42014b9c958d802a03dc8c6400db0d36b5aa682c [file] [log] [blame]
Adrien Destugues374c6dc2015-01-11 16:37:56 +01001/*
2** Copyright 2009-2015 Adrien Destugues, pulkomandy@pulkomandy.tk.
3** Distributed under the terms of the MIT License.
4*/
5
6#include "AmigaCatalog.h"
7
8#include <iostream>
9#include <memory>
10#include <new>
11
12#include <Application.h>
13#include <Directory.h>
14#include <File.h>
15#include <FindDirectory.h>
16#include <fs_attr.h>
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010017#include <Language.h>
Adrien Destugues374c6dc2015-01-11 16:37:56 +010018#include <Mime.h>
19#include <Path.h>
20#include <Resources.h>
21#include <Roster.h>
22#include <StackOrHeapArray.h>
23#include <String.h>
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010024#include <UTF8.h>
Adrien Destugues374c6dc2015-01-11 16:37:56 +010025
26#include <LocaleRoster.h>
27#include <Catalog.h>
28
29
30class BMessage;
31
32
33using BPrivate::HashMapCatalog;
34using BPrivate::AmigaCatalog;
35
36
37/* This add-on implements reading of Amiga catalog files. These are IFF files
38 * of type CTLG and used to localize Amiga applications. In most cases you
39 * should use the Haiku standard catalogs instead, unless:
40 * - You are porting an application from Amiga and want to use the same locale
41 * files
42 * - You want to use ID-based string lookup instead of string-to-string, for
43 * performance reasons.
44 */
45
46
47static const char *kCatFolder = "Catalogs/";
48static const char *kCatExtension = ".catalog";
49
50const char *AmigaCatalog::kCatMimeType
51 = "locale/x-vnd.Be.locale-catalog.amiga";
52
53static int16 kCatArchiveVersion = 1;
54 // version of the catalog archive structure, bump this if you change it!
55
56
57/*
58 * constructs a AmigaCatalog with given signature and language and reads
59 * the catalog from disk.
60 * InitCheck() will be B_OK if catalog could be loaded successfully, it will
61 * give an appropriate error-code otherwise.
62 */
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010063AmigaCatalog::AmigaCatalog(const entry_ref& owner, const char *language,
Adrien Destugues374c6dc2015-01-11 16:37:56 +010064 uint32 fingerprint)
65 :
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010066 HashMapCatalog("", language, fingerprint)
Adrien Destugues374c6dc2015-01-11 16:37:56 +010067{
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010068 // This catalog uses the executable name to identify the catalog
69 // (not the MIME signature)
70 BEntry entry(&owner);
71 char buffer[256];
72 entry.GetName(buffer);
73 fSignature = buffer;
74
75 // This catalog uses the translated language name to identify the catalog
76 // (not the ISO language code)
77 BLanguage lang(language);
78 lang.GetNativeName(fLanguageName);
79
Adrien Destugues374c6dc2015-01-11 16:37:56 +010080 // give highest priority to catalog living in sub-folder of app's folder:
Adrien Destugues374c6dc2015-01-11 16:37:56 +010081 BString catalogName(kCatFolder);
82 catalogName << fLanguageName
83 << "/" << fSignature
84 << kCatExtension;
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010085 status_t status = ReadFromFile(catalogName.String());
Adrien Destugues374c6dc2015-01-11 16:37:56 +010086
87 if (status != B_OK) {
88 // look in common-etc folder (/boot/home/config/etc):
89 BPath commonEtcPath;
90 find_directory(B_USER_ETC_DIRECTORY, &commonEtcPath);
91 if (commonEtcPath.InitCheck() == B_OK) {
92 catalogName = BString(commonEtcPath.Path())
Adrien Destuguesc102dfd2015-01-18 11:50:51 +010093 << "/" << kCatFolder << fLanguageName
Adrien Destugues374c6dc2015-01-11 16:37:56 +010094 << "/" << fSignature
95 << kCatExtension;
96 status = ReadFromFile(catalogName.String());
97 }
98 }
99
100 if (status != B_OK) {
101 // look in system-etc folder (/boot/beos/etc):
102 BPath systemEtcPath;
103 find_directory(B_SYSTEM_ETC_DIRECTORY, &systemEtcPath);
104 if (systemEtcPath.InitCheck() == B_OK) {
105 catalogName = BString(systemEtcPath.Path())
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100106 << "/" << kCatFolder << fLanguageName
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100107 << "/" << fSignature
108 << kCatExtension;
109 status = ReadFromFile(catalogName.String());
110 }
111 }
112
113 fInitCheck = status;
114}
115
116
117/*
118 * constructs an empty AmigaCatalog with given sig and language.
119 * This is used for editing/testing purposes.
120 * InitCheck() will always be B_OK.
121 */
122AmigaCatalog::AmigaCatalog(const char *path, const char *signature,
123 const char *language)
124 :
125 HashMapCatalog(signature, language, 0),
126 fPath(path)
127{
128 fInitCheck = B_OK;
129}
130
131
132AmigaCatalog::~AmigaCatalog()
133{
134}
135
136
137status_t
138AmigaCatalog::ReadFromFile(const char *path)
139{
140 if (!path)
141 path = fPath.String();
142
143 BFile source(path, B_READ_ONLY);
144 if (source.InitCheck() != B_OK)
145 return source.InitCheck();
146
147 // Now read all the data from the file
148
149 int32 tmp;
150 source.Read(&tmp, sizeof(tmp)); // IFF header
151 if (ntohl(tmp) != 'FORM')
152 return B_BAD_DATA;
153
154 int32 dataSize;
155 source.Read(&dataSize, sizeof(dataSize)); // File size
156 dataSize = ntohl(dataSize);
157 source.Read(&tmp, sizeof(tmp)); // File type
158 if (ntohl(tmp) != 'CTLG')
159 return B_BAD_DATA;
160
161 dataSize -= 4; // Type is included in data size.
162
163 while(dataSize > 0) {
164 int32 chunkID, chunkSize;
165 source.Read(&chunkID, sizeof(chunkID));
166 source.Read(&chunkSize, sizeof(chunkSize));
167 chunkSize = ntohl(chunkSize);
168
169 // Round to word
170 if (chunkSize & 1) chunkSize++;
171
172 BStackOrHeapArray<char, 256> chunkData(chunkSize);
173 source.Read(chunkData, chunkSize);
174
175 chunkID = ntohl(chunkID);
176
177 switch(chunkID) {
178 case 'FVER': // Version
179 fSignature = chunkData;
180 break;
181 case 'LANG': // Language
182 fLanguageName = chunkData;
183 break;
184
185 case 'STRS': // Catalog strings
186 {
187 BMemoryIO strings(chunkData, chunkSize);
188 int32 strID, strLen;
189 puts("");
190
191 while (strings.Position() < chunkSize) {
192 strings.Read(&strID, sizeof(strID));
193 strings.Read(&strLen, sizeof(strLen));
194 strID = ntohl(strID);
195 strLen = ntohl(strLen);
196 if (strLen & 3) {
197 strLen &= ~3;
198 strLen += 4;
199 }
200 char strVal[strLen];
201 strings.Read(strVal, strLen);
202
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100203 char outVal[1024];
204 int32 outLen = 1024;
205 int32 cookie = 0;
206
207 convert_to_utf8(B_ISO1_CONVERSION, strVal, &strLen,
208 outVal, &outLen, &cookie);
209
210 SetString(strID, outVal);
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100211 }
212 break;
213 }
214
215 case 'CSET': // Unknown/unused
216 default:
217 break;
218 }
219
220 dataSize -= chunkSize + 8;
221 }
222
223 fPath = path;
224 fFingerprint = ComputeFingerprint();
225 return B_OK;
226}
227
228
229status_t
230AmigaCatalog::WriteToFile(const char *path)
231{
232 return B_NOT_SUPPORTED;
233}
234
235
236/*
237 * writes mimetype, language-name and signature of catalog into the
238 * catalog-file.
239 */
240void
241AmigaCatalog::UpdateAttributes(BFile& catalogFile)
242{
243 static const int bufSize = 256;
244 char buf[bufSize];
245 uint32 temp;
246 if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf, bufSize)
247 <= 0
248 || strcmp(kCatMimeType, buf) != 0) {
249 catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
250 kCatMimeType, strlen(kCatMimeType)+1);
251 }
252 if (catalogFile.ReadAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
253 &buf, bufSize) <= 0
254 || fLanguageName != buf) {
255 catalogFile.WriteAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
256 fLanguageName.String(), fLanguageName.Length()+1);
257 }
258 if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
259 &buf, bufSize) <= 0
260 || fSignature != buf) {
261 catalogFile.WriteAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
262 fSignature.String(), fSignature.Length()+1);
263 }
264 if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
265 0, &temp, sizeof(uint32)) <= 0) {
266 catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
267 0, &fFingerprint, sizeof(uint32));
268 }
269}
270
271
272void
273AmigaCatalog::UpdateAttributes(const char* path)
274{
275 BEntry entry(path);
276 BFile node(&entry, B_READ_WRITE);
277 UpdateAttributes(node);
278}
279
280
281BCatalogData *
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100282AmigaCatalog::Instantiate(const entry_ref &owner, const char *language,
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100283 uint32 fingerprint)
284{
285 AmigaCatalog *catalog
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100286 = new(std::nothrow) AmigaCatalog(owner, language, fingerprint);
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100287 if (catalog && catalog->InitCheck() != B_OK) {
288 delete catalog;
289 return NULL;
290 }
291 return catalog;
292}
293
294
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100295// #pragma mark -
296
297
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100298extern "C" BCatalogData *
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100299instantiate_catalog(const entry_ref &owner, const char *language,
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100300 uint32 fingerprint)
301{
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100302 return AmigaCatalog::Instantiate(owner, language, fingerprint);
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100303}
304
305
306extern "C" BCatalogData *
307create_catalog(const char *signature, const char *language)
308{
309 AmigaCatalog *catalog
310 = new(std::nothrow) AmigaCatalog("emptycat", signature, language);
311 return catalog;
312}
313
314
315extern "C" status_t
316get_available_languages(BMessage* availableLanguages,
317 const char* sigPattern = NULL, const char* langPattern = NULL,
318 int32 fingerprint = 0)
319{
320 // TODO
321 return B_ERROR;
322}
323
324
325uint8 gCatalogAddOnPriority = 80;