blob: ce5ce77f07c03fd136b4635ec91e7e6a9af6a2b0 [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;
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100189
190 while (strings.Position() < chunkSize) {
191 strings.Read(&strID, sizeof(strID));
192 strings.Read(&strLen, sizeof(strLen));
193 strID = ntohl(strID);
194 strLen = ntohl(strLen);
195 if (strLen & 3) {
196 strLen &= ~3;
197 strLen += 4;
198 }
Adrien Destuguese3b38e72017-11-13 21:56:57 +0100199 char strBase[strLen];
200 char* strVal = strBase;
201 strings.Read(strBase, strLen);
202
203 if (strBase[1] == 0)
204 {
205 // Skip the \0 marker for menu entries…
206 strLen -= 2;
207 strVal += 2;
208 }
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100209
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100210 char outVal[1024];
211 int32 outLen = 1024;
212 int32 cookie = 0;
213
214 convert_to_utf8(B_ISO1_CONVERSION, strVal, &strLen,
215 outVal, &outLen, &cookie);
216
Adrien Destuguese3b38e72017-11-13 21:56:57 +0100217 // If the UTF-8 version is shorter, it's likely that
218 // something went wrong. Keep the original string.
219 if (outLen > strLen)
220 SetString(strID, outVal);
221 else
222 SetString(strID, strVal);
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100223 }
224 break;
225 }
226
227 case 'CSET': // Unknown/unused
228 default:
229 break;
230 }
231
232 dataSize -= chunkSize + 8;
233 }
234
235 fPath = path;
236 fFingerprint = ComputeFingerprint();
237 return B_OK;
238}
239
240
241status_t
242AmigaCatalog::WriteToFile(const char *path)
243{
244 return B_NOT_SUPPORTED;
245}
246
247
248/*
249 * writes mimetype, language-name and signature of catalog into the
250 * catalog-file.
251 */
252void
253AmigaCatalog::UpdateAttributes(BFile& catalogFile)
254{
255 static const int bufSize = 256;
256 char buf[bufSize];
257 uint32 temp;
258 if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf, bufSize)
259 <= 0
260 || strcmp(kCatMimeType, buf) != 0) {
261 catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
262 kCatMimeType, strlen(kCatMimeType)+1);
263 }
264 if (catalogFile.ReadAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
265 &buf, bufSize) <= 0
266 || fLanguageName != buf) {
267 catalogFile.WriteAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
268 fLanguageName.String(), fLanguageName.Length()+1);
269 }
270 if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
271 &buf, bufSize) <= 0
272 || fSignature != buf) {
273 catalogFile.WriteAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
274 fSignature.String(), fSignature.Length()+1);
275 }
276 if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
277 0, &temp, sizeof(uint32)) <= 0) {
278 catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
279 0, &fFingerprint, sizeof(uint32));
280 }
281}
282
283
284void
285AmigaCatalog::UpdateAttributes(const char* path)
286{
287 BEntry entry(path);
288 BFile node(&entry, B_READ_WRITE);
289 UpdateAttributes(node);
290}
291
292
293BCatalogData *
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100294AmigaCatalog::Instantiate(const entry_ref &owner, const char *language,
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100295 uint32 fingerprint)
296{
297 AmigaCatalog *catalog
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100298 = new(std::nothrow) AmigaCatalog(owner, language, fingerprint);
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100299 if (catalog && catalog->InitCheck() != B_OK) {
300 delete catalog;
301 return NULL;
302 }
303 return catalog;
304}
305
306
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100307// #pragma mark -
308
309
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100310extern "C" BCatalogData *
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100311instantiate_catalog(const entry_ref &owner, const char *language,
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100312 uint32 fingerprint)
313{
Adrien Destuguesc102dfd2015-01-18 11:50:51 +0100314 return AmigaCatalog::Instantiate(owner, language, fingerprint);
Adrien Destugues374c6dc2015-01-11 16:37:56 +0100315}
316
317
318extern "C" BCatalogData *
319create_catalog(const char *signature, const char *language)
320{
321 AmigaCatalog *catalog
322 = new(std::nothrow) AmigaCatalog("emptycat", signature, language);
323 return catalog;
324}
325
326
327extern "C" status_t
328get_available_languages(BMessage* availableLanguages,
329 const char* sigPattern = NULL, const char* langPattern = NULL,
330 int32 fingerprint = 0)
331{
332 // TODO
333 return B_ERROR;
334}
335
336
337uint8 gCatalogAddOnPriority = 80;