Initial version of the Amiga IFF catalog parser.
diff --git a/AmigaCatalog.cpp b/AmigaCatalog.cpp
new file mode 100644
index 0000000..618dbb5
--- /dev/null
+++ b/AmigaCatalog.cpp
@@ -0,0 +1,314 @@
+/*
+** Copyright 2009-2015 Adrien Destugues, pulkomandy@pulkomandy.tk.
+** Distributed under the terms of the MIT License.
+*/
+
+#include "AmigaCatalog.h"
+
+#include <iostream>
+#include <memory>
+#include <new>
+
+#include <Application.h>
+#include <Directory.h>
+#include <File.h>
+#include <FindDirectory.h>
+#include <fs_attr.h>
+#include <Mime.h>
+#include <Path.h>
+#include <Resources.h>
+#include <Roster.h>
+#include <StackOrHeapArray.h>
+#include <String.h>
+
+#include <LocaleRoster.h>
+#include <Catalog.h>
+
+
+class BMessage;
+
+
+using BPrivate::HashMapCatalog;
+using BPrivate::AmigaCatalog;
+
+
+/*	This add-on implements reading of Amiga catalog files. These are IFF files
+ *	of type CTLG and used to localize Amiga applications. In most cases you
+ *	should use the Haiku standard catalogs instead, unless:
+ *	- You are porting an application from Amiga and want to use the same locale
+ *		files
+ *	- You want to use ID-based string lookup instead of string-to-string, for
+ *		performance reasons.
+ */
+
+
+static const char *kCatFolder = "Catalogs/";
+static const char *kCatExtension = ".catalog";
+
+const char *AmigaCatalog::kCatMimeType
+	= "locale/x-vnd.Be.locale-catalog.amiga";
+
+static int16 kCatArchiveVersion = 1;
+	// version of the catalog archive structure, bump this if you change it!
+
+
+/*
+ * constructs a AmigaCatalog with given signature and language and reads
+ * the catalog from disk.
+ * InitCheck() will be B_OK if catalog could be loaded successfully, it will
+ * give an appropriate error-code otherwise.
+ */
+AmigaCatalog::AmigaCatalog(const char *signature, const char *language,
+	uint32 fingerprint)
+	:
+	HashMapCatalog(signature, language, fingerprint)
+{
+	// give highest priority to catalog living in sub-folder of app's folder:
+	app_info appInfo;
+	be_app->GetAppInfo(&appInfo);
+	node_ref nref;
+	nref.device = appInfo.ref.device;
+	nref.node = appInfo.ref.directory;
+	BDirectory appDir(&nref);
+	BString catalogName(kCatFolder);
+	catalogName << fLanguageName
+		<< "/" << fSignature
+		<< kCatExtension;
+	BPath catalogPath(&appDir, catalogName.String());
+	status_t status = ReadFromFile(catalogPath.Path());
+
+	if (status != B_OK) {
+		// look in common-etc folder (/boot/home/config/etc):
+		BPath commonEtcPath;
+		find_directory(B_USER_ETC_DIRECTORY, &commonEtcPath);
+		if (commonEtcPath.InitCheck() == B_OK) {
+			catalogName = BString(commonEtcPath.Path())
+							<< kCatFolder << fLanguageName
+							<< "/" << fSignature
+							<< kCatExtension;
+			status = ReadFromFile(catalogName.String());
+		}
+	}
+
+	if (status != B_OK) {
+		// look in system-etc folder (/boot/beos/etc):
+		BPath systemEtcPath;
+		find_directory(B_SYSTEM_ETC_DIRECTORY, &systemEtcPath);
+		if (systemEtcPath.InitCheck() == B_OK) {
+			catalogName = BString(systemEtcPath.Path())
+							<< kCatFolder << fLanguageName
+							<< "/" << fSignature
+							<< kCatExtension;
+			status = ReadFromFile(catalogName.String());
+		}
+	}
+
+	fInitCheck = status;
+}
+
+
+/*
+ * constructs an empty AmigaCatalog with given sig and language.
+ * This is used for editing/testing purposes.
+ * InitCheck() will always be B_OK.
+ */
+AmigaCatalog::AmigaCatalog(const char *path, const char *signature,
+	const char *language)
+	:
+	HashMapCatalog(signature, language, 0),
+	fPath(path)
+{
+	fInitCheck = B_OK;
+}
+
+
+AmigaCatalog::~AmigaCatalog()
+{
+}
+
+
+status_t
+AmigaCatalog::ReadFromFile(const char *path)
+{
+	if (!path)
+		path = fPath.String();
+
+	BFile source(path, B_READ_ONLY);
+	if (source.InitCheck() != B_OK)
+		return source.InitCheck();
+
+	// Now read all the data from the file
+
+	int32 tmp;
+	source.Read(&tmp, sizeof(tmp)); // IFF header
+	if (ntohl(tmp) != 'FORM')
+		return B_BAD_DATA;
+
+	int32 dataSize;
+	source.Read(&dataSize, sizeof(dataSize)); // File size
+	dataSize = ntohl(dataSize);
+	source.Read(&tmp, sizeof(tmp)); // File type
+	if (ntohl(tmp) != 'CTLG')
+		return B_BAD_DATA;
+
+	dataSize -= 4; // Type is included in data size.
+
+	while(dataSize > 0) {
+		int32 chunkID, chunkSize;
+		source.Read(&chunkID, sizeof(chunkID));
+		source.Read(&chunkSize, sizeof(chunkSize));
+		chunkSize = ntohl(chunkSize);
+
+		// Round to word
+		if (chunkSize & 1) chunkSize++;
+
+		BStackOrHeapArray<char, 256> chunkData(chunkSize);
+		source.Read(chunkData, chunkSize);
+
+		chunkID = ntohl(chunkID);
+
+		switch(chunkID) {
+			case 'FVER': // Version
+				fSignature = chunkData;
+				break;
+			case 'LANG': // Language
+				fLanguageName = chunkData;
+				break;
+
+			case 'STRS': // Catalog strings
+			{
+				BMemoryIO strings(chunkData, chunkSize);
+				int32 strID, strLen;
+				puts("");
+
+				while (strings.Position() < chunkSize) {
+					strings.Read(&strID, sizeof(strID));
+					strings.Read(&strLen, sizeof(strLen));
+					strID = ntohl(strID);
+					strLen = ntohl(strLen);
+					if (strLen & 3) {
+						strLen &= ~3;
+						strLen += 4;
+					}
+					char strVal[strLen];
+					strings.Read(strVal, strLen);
+
+					SetString(strID, strVal);
+				}
+				break;
+			}
+
+			case 'CSET': // Unknown/unused
+			default:
+				break;
+		}
+		
+		dataSize -= chunkSize + 8;
+	}
+
+	fPath = path;
+	fFingerprint = ComputeFingerprint();
+	return B_OK;
+}
+
+
+status_t
+AmigaCatalog::WriteToFile(const char *path)
+{
+	return B_NOT_SUPPORTED;
+}
+
+
+/*
+ * writes mimetype, language-name and signature of catalog into the
+ * catalog-file.
+ */
+void
+AmigaCatalog::UpdateAttributes(BFile& catalogFile)
+{
+	static const int bufSize = 256;
+	char buf[bufSize];
+	uint32 temp;
+	if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf, bufSize)
+			<= 0
+		|| strcmp(kCatMimeType, buf) != 0) {
+		catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
+			kCatMimeType, strlen(kCatMimeType)+1);
+	}
+	if (catalogFile.ReadAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
+		&buf, bufSize) <= 0
+		|| fLanguageName != buf) {
+		catalogFile.WriteAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
+			fLanguageName.String(), fLanguageName.Length()+1);
+	}
+	if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
+		&buf, bufSize) <= 0
+		|| fSignature != buf) {
+		catalogFile.WriteAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
+			fSignature.String(), fSignature.Length()+1);
+	}
+	if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
+		0, &temp, sizeof(uint32)) <= 0) {
+		catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
+			0, &fFingerprint, sizeof(uint32));
+	}
+}
+
+
+void
+AmigaCatalog::UpdateAttributes(const char* path)
+{
+	BEntry entry(path);
+	BFile node(&entry, B_READ_WRITE);
+	UpdateAttributes(node);
+}
+
+
+BCatalogData *
+AmigaCatalog::Instantiate(const char *signature, const char *language,
+	uint32 fingerprint)
+{
+	AmigaCatalog *catalog
+		= new(std::nothrow) AmigaCatalog(signature, language, fingerprint);
+	if (catalog && catalog->InitCheck() != B_OK) {
+		delete catalog;
+		return NULL;
+	}
+	return catalog;
+}
+
+
+extern "C" BCatalogData *
+instantiate_catalog(const char *signature, const char *language,
+	uint32 fingerprint)
+{
+	AmigaCatalog *catalog
+		= new(std::nothrow) AmigaCatalog(signature, language, fingerprint);
+	if (catalog && catalog->InitCheck() != B_OK) {
+		delete catalog;
+		return NULL;
+	}
+	return catalog;
+}
+
+
+extern "C" BCatalogData *
+create_catalog(const char *signature, const char *language)
+{
+	AmigaCatalog *catalog
+		= new(std::nothrow) AmigaCatalog("emptycat", signature, language);
+	return catalog;
+}
+
+
+extern "C" status_t
+get_available_languages(BMessage* availableLanguages,
+	const char* sigPattern = NULL, const char* langPattern = NULL,
+	int32 fingerprint = 0)
+{
+	// TODO
+	return B_ERROR;
+}
+
+
+uint8 gCatalogAddOnPriority = 80;
diff --git a/AmigaCatalog.h b/AmigaCatalog.h
new file mode 100644
index 0000000..f2f5974
--- /dev/null
+++ b/AmigaCatalog.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2009-2015, Adrien Destugues, pulkomandy@pulkomandy.tk.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef _AMIGA_CATALOG_H_
+#define _AMIGA_CATALOG_H_
+
+
+#include <HashMapCatalog.h>
+#include <DataIO.h>
+#include <String.h>
+
+
+class BFile;
+
+namespace BPrivate {
+
+
+class AmigaCatalog : public HashMapCatalog {
+	public:
+		AmigaCatalog(const char *signature, const char *language,
+			uint32 fingerprint);
+				// constructor for normal use
+		AmigaCatalog(entry_ref *appOrAddOnRef);
+				// constructor for embedded catalog
+		AmigaCatalog(const char *path, const char *signature,
+			const char *language);
+				// constructor for editor-app
+
+		~AmigaCatalog();
+
+		// implementation for editor-interface:
+		status_t ReadFromFile(const char *path = NULL);
+		status_t WriteToFile(const char *path = NULL);
+
+		static BCatalogData *Instantiate(const char *signature,
+			const char *language, uint32 fingerprint);
+
+		static const char *kCatMimeType;
+
+	private:
+		void UpdateAttributes(BFile& catalogFile);
+		void UpdateAttributes(const char* path);
+
+		mutable BString		fPath;
+};
+
+
+} // namespace BPrivate
+
+
+#endif /* _AMIGA_CATALOG_H_ */