summaryrefslogtreecommitdiff
blob: 89e24335bbc90c06fb55ccdeedb53bec6546998b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
Description: Be friendly for other open source projects: work with WINE style namemangling.
 Patch 3382938 from Andre_H (modified).
Origin: upstream, http://sourceforge.net/p/dosbox/code-0/3743/
Author: Peter Veenstra <qbix79@users.sourceforge.net>
Last-Update: 2011-08-30
--- a/src/dos/drive_cache.cpp
+++ b/src/dos/drive_cache.cpp
@@ -370,6 +370,60 @@
 	return false;
 }
 
+#define WINE_DRIVE_SUPPORT 1
+#if WINE_DRIVE_SUPPORT
+//Changes to interact with WINE by supporting their namemangling.
+//The code is rather slow, because orglist is unordered, so it needs to be avoided if possible.
+//Hence the tests in GetLongFileName
+
+
+// From the Wine project
+static Bits wine_hash_short_file_name( char* name, char* buffer )
+{
+	static const char hash_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
+	static const char invalid_chars[] = { '*','?','<','>','|','"','+','=',',',';','[',']',' ','\345','~','.',0 };
+	char* p;
+	char* ext;
+	char* end = name + strlen(name);
+	char* dst;
+	unsigned short hash;
+	int i;
+
+	// Compute the hash code of the file name
+	for (p = name, hash = 0xbeef; p < end - 1; p++)
+		hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
+	hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); // Last character
+
+
+	// Find last dot for start of the extension
+	for (p = name + 1, ext = NULL; p < end - 1; p++) if (*p == '.') ext = p;
+
+	// Copy first 4 chars, replacing invalid chars with '_'
+	for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
+	{
+		if (p == end || p == ext) break;
+		*dst++ = (*p < 0 || strchr( invalid_chars, *p ) != NULL) ? '_' : toupper(*p);
+	}
+	// Pad to 5 chars with '~'
+	while (i-- >= 0) *dst++ = '~';
+
+	// Insert hash code converted to 3 ASCII chars
+	*dst++ = hash_chars[(hash >> 10) & 0x1f];
+	*dst++ = hash_chars[(hash >> 5) & 0x1f];
+	*dst++ = hash_chars[hash & 0x1f];
+
+	// Copy the first 3 chars of the extension (if any)
+	if (ext)
+	{
+		*dst++ = '.';
+		for (i = 3, ext++; (i > 0) && ext < end; i--, ext++)
+			*dst++ = (*ext < 0 || strchr( invalid_chars, *ext ) != NULL) ? '_' : toupper(*ext);
+	}
+
+	return dst - buffer;
+}
+#endif
+
 Bits DOS_Drive_Cache::GetLongName(CFileInfo* curDir, char* shortName) {
 	std::vector<CFileInfo*>::size_type filelist_size = curDir->fileList.size();
 	if (GCC_UNLIKELY(filelist_size<=0)) return -1;
@@ -390,6 +444,20 @@
 			return mid;
 		};
 	}
+#ifdef WINE_DRIVE_SUPPORT
+	if (strlen(shortName) < 8 || shortName[4] != '~' || shortName[5] == '.' || shortName[6] == '.' || shortName[7] == '.') return -1; // not available
+	// else it's most likely a Wine style short name ABCD~###, # = not dot  (length at least 8)
+	// The above test is rather strict as the following loop can be really slow if filelist_size is large.
+	char buff[CROSS_LEN];
+	for (Bits i = 0; i < filelist_size; i++) {
+		res = wine_hash_short_file_name(curDir->fileList[i]->orgname,buff);
+		if (!strncmp(shortName,buff,res))
+		{	// Found
+			strcpy(shortName,curDir->fileList[i]->orgname);
+			return i;
+		};
+	}
+#endif
 	// not available
 	return -1;
 }