CP437 support

This commit is contained in:
yvt 2013-12-29 23:08:57 +09:00
parent 05e828b9d4
commit 9e638bb054
7 changed files with 221 additions and 8 deletions

View File

@ -93,6 +93,7 @@
E883191F1792A7CC002ABE6D /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = E88319161792A7CC002ABE6D /* unix.c */; };
E88319201792A7CC002ABE6D /* win32.c in Sources */ = {isa = PBXBuildFile; fileRef = E88319171792A7CC002ABE6D /* win32.c */; };
E88EB02F185D9DC500565D07 /* YsrDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E88EB02D185D9DC500565D07 /* YsrDevice.cpp */; };
E890F310187046990090AAB8 /* CP437.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E890F30E187046990090AAB8 /* CP437.cpp */; };
E89A648E17A11B4F00FDA893 /* GLModelRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E89A648C17A11B4E00FDA893 /* GLModelRenderer.cpp */; };
E89A649117A12FF900FDA893 /* GLDynamicLightShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E89A648F17A12FF800FDA893 /* GLDynamicLightShader.cpp */; };
E89A649417A1677F00FDA893 /* FallingBlock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E89A649217A1677F00FDA893 /* FallingBlock.cpp */; };
@ -465,6 +466,8 @@
E88319171792A7CC002ABE6D /* win32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = win32.c; sourceTree = "<group>"; };
E88EB02D185D9DC500565D07 /* YsrDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YsrDevice.cpp; sourceTree = "<group>"; };
E88EB02E185D9DC500565D07 /* YsrDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YsrDevice.h; sourceTree = "<group>"; };
E890F30E187046990090AAB8 /* CP437.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CP437.cpp; sourceTree = "<group>"; };
E890F30F187046990090AAB8 /* CP437.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CP437.h; sourceTree = "<group>"; };
E89A648C17A11B4E00FDA893 /* GLModelRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GLModelRenderer.cpp; sourceTree = "<group>"; };
E89A648D17A11B4E00FDA893 /* GLModelRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLModelRenderer.h; sourceTree = "<group>"; };
E89A648F17A12FF800FDA893 /* GLDynamicLightShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GLDynamicLightShader.cpp; sourceTree = "<group>"; };
@ -1377,6 +1380,8 @@
E8E0AFB4179BF25B00C6B5A9 /* Settings.h */,
E8B6B6EF17E40DA700E35523 /* RefCountedObject.cpp */,
E8B6B6F017E40DAA00E35523 /* RefCountedObject.h */,
E890F30E187046990090AAB8 /* CP437.cpp */,
E890F30F187046990090AAB8 /* CP437.h */,
);
path = Core;
sourceTree = "<group>";
@ -1741,6 +1746,7 @@
E8CF03FC178FACFF000683D4 /* GameMap.cpp in Sources */,
E8CF03FF178FB1E1000683D4 /* IGameMapListener.cpp in Sources */,
E8CF0402178FB52F000683D4 /* GLImage.cpp in Sources */,
E890F310187046990090AAB8 /* CP437.cpp in Sources */,
E8CF0405178FF776000683D4 /* Exception.cpp in Sources */,
E8CF04081790455B000683D4 /* GLProgram.cpp in Sources */,
E8CF040B1790471E000683D4 /* IFileSystem.cpp in Sources */,

View File

@ -626,7 +626,9 @@ namespace spades {
StandardPreferenceLayouter layouter(this);
layouter.AddHeading("Player Information");
layouter.AddInputField("Player Name", "cg_playerName", not options.GameActive).MaxLength = 15;
ConfigField@ nameField = layouter.AddInputField("Player Name", "cg_playerName", not options.GameActive);
nameField.MaxLength = 15;
nameField.DenyNonAscii = true;
layouter.AddHeading("Effects");
layouter.AddToggleField("Blood", "cg_blood");
@ -637,6 +639,9 @@ namespace spades {
layouter.AddToggleField("Chat Notify Sounds", "cg_chatBeep");
layouter.AddToggleField("Hit Indicator", "cg_hitIndicator");
//layouter.AddHeading("AoS 0.75/0.76 Compatibility");
//layouter.AddToggleField("Compatible Charset", "cg_legacyCharset");
layouter.AddHeading("Misc");
layouter.AddSliderField("Field of View", "cg_fov", 30, 90, 1,
ConfigNumberFormatter(0, " deg"));

View File

@ -258,6 +258,7 @@ namespace spades {
int MarkPosition = 0;
int CursorPosition = 0;
int MaxLength = 255;
bool DenyNonAscii = false;
private string text;
private FieldCommand@[] history;
@ -291,6 +292,18 @@ namespace spades {
historyPos = 0;
}
private bool CheckCharType(string s) {
if(DenyNonAscii) {
for(uint i = 0, len = s.length; i < len; i++) {
int c = s[i];
if((c & 0x80) != 0) {
return false;
}
}
}
return true;
}
void OnChanged() {
if(Changed !is null) {
Changed(this);
@ -327,6 +340,9 @@ namespace spades {
return Text.substr(SelectionStart, SelectionLength);
}
set {
if(!CheckCharType(value)) {
return;
}
FieldCommand cmd;
cmd.oldString = this.SelectedText;
if(cmd.oldString == value) return; // no change
@ -458,6 +474,9 @@ namespace spades {
}
void Insert(string text) {
if(!CheckCharType(text)) {
return;
}
string oldText = SelectedText;
SelectedText = text;
@ -476,7 +495,8 @@ namespace spades {
BackSpace();
}else if(key == "Left") {
if(Manager.IsShiftPressed) {
CursorPosition = ClampCursorPosition(CursorPosition - 1);
int cIdx = GetCharIndexForString(Text, CursorPosition);
CursorPosition = ClampCursorPosition(GetByteIndexForString(Text, cIdx - 1));
}else {
if(SelectionLength == 0) {
int cIdx = GetCharIndexForString(Text, CursorPosition);
@ -488,7 +508,8 @@ namespace spades {
return;
}else if(key == "Right") {
if(Manager.IsShiftPressed) {
CursorPosition = ClampCursorPosition(CursorPosition + 1);
int cIdx = GetCharIndexForString(Text, CursorPosition);
CursorPosition = ClampCursorPosition(GetByteIndexForString(Text, cIdx + 1));
}else {
if(SelectionLength == 0) {
int cIdx = GetCharIndexForString(Text, CursorPosition);

View File

@ -39,11 +39,16 @@
#include "TCGameMode.h"
#include <Core/Settings.h>
#include <enet/enet.h>
#include <Core/CP437.h>
SPADES_SETTING(cg_protocolVersion, "3");
SPADES_SETTING(cg_legacyCharset, "1");
namespace spades {
namespace client {
static const char UtfSign = -1;
enum{
BLUE_FLAG = 0,
GREEN_FLAG = 1,
@ -89,6 +94,30 @@ namespace spades {
PacketTypeVersionSend = 34, // C2S
};
static std::string EncodeString(std::string str) {
auto str2 = CP437::Encode(str, -1);
if(str2.find(-1) != std::string::npos) {
// some fallbacks; always use UTF8
str.insert(0, &UtfSign, 1);
}else{
if(cg_legacyCharset) {
str = str2;
}
}
return str;
}
static std::string DecodeString(std::string s) {
if(s.size() > 0 && s[0] == UtfSign){
return s.substr(1);
}
if(cg_legacyCharset) {
return CP437::Decode(s);
}
return s;
}
class NetPacketReader {
std::vector<char> data;
size_t pos;
@ -189,12 +218,16 @@ namespace spades {
std::string ReadString(size_t siz){
// convert to C string once so that
// null-chars are removed
return ReadData(siz).c_str(); // TODO: decode
std::string s = ReadData(siz).c_str();
s = DecodeString(s);
return s;
}
std::string ReadRemainingString() {
// convert to C string once so that
// null-chars are removed
return ReadRemainingData().c_str(); // TODO: decode
std::string s = ReadRemainingData().c_str();
s = DecodeString(s);
return s;
}
void DumpDebug() {
@ -256,16 +289,15 @@ namespace spades {
Write((uint8_t)v.x);
}
//lm: http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/IBMGRAPH.TXT
void Write(std::string str){
// TODO: encode from utf-8 to cp437
str = EncodeString(str);
data.insert(data.end(),
str.begin(),
str.end());
}
void Write(std::string str, size_t fillLen){
// TODO: encode from utf-8 to cp437
str = EncodeString(str);
Write(str.substr(0, fillLen));
size_t sz = str.size();
while(sz < fillLen){

93
Sources/Core/CP437.cpp Normal file
View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2013 yvt
* WTFPL
*/
#include "CP437.h"
#include <map>
#include <iterator>
#include <Core/Debug.h>
static const uint16_t cp437map[256] = {
/*
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,*/
// graphic symbols
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x25d8, 0x25cb, 0x000a, 0x2642, 0x2640, 0x000d, 0x266b, 0x263c,
0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0,
};
namespace spades {
class CP437::ReverseMap {
public:
std::map<uint32_t, char> mp;
ReverseMap() {
for(int i = 0; i < 256; i++) {
mp[cp437map[i]] = static_cast<char>(i);
}
}
};
CP437::ReverseMap CP437::reverse;
char CP437::EncodeChar(uint32_t unicode,
char fb) {
auto it = reverse.mp.find(unicode);
if(it == reverse.mp.end()){
return fb;
}else{
SPAssert(cp437map[static_cast<unsigned char>(it->second)] == unicode);
return it->second;
}
}
std::uint32_t CP437::DecodeChar(char c) {
auto r = cp437map[static_cast<unsigned char>(c)];
SPAssert(reverse.mp.find(r)->second == c);
return r;
}
std::string CP437::Encode(const std::string& str,
char fallback) {
size_t idx = 0;
std::string outp;
outp.reserve(str.size());
while(idx < str.size()){
size_t outNumBytes;
auto cp = GetCodePointFromUTF8String(str, idx, &outNumBytes);
idx += outNumBytes;
outp.push_back(EncodeChar(cp, fallback));
}
return outp;
}
std::string CP437::Decode(const std::string& str) {
std::string outp;
outp.reserve(str.size() * 3);
auto it = std::back_inserter(outp);
for(size_t i = 0; i < str.size(); i++) {
auto cp = DecodeChar(str[i]);
CodePointToUTF8(it, cp);
}
return outp;
}
}

24
Sources/Core/CP437.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2013 yvt
* WTFPL
*/
#include <cstdint>
#include <string>
#include <Core/Math.h>
namespace spades {
class CP437 {
class ReverseMap;
static ReverseMap reverse;
CP437() {}
~CP437() {}
public:
static char EncodeChar(std::uint32_t unicode,
char fallback = 0xff);
static std::uint32_t DecodeChar(char c);
static std::string Encode(const std::string&,
char fallback = 0xff);
static std::string Decode(const std::string&);
};
}

View File

@ -846,6 +846,38 @@ namespace spades {
std::string EscapeControlCharacters(const std::string& str);
uint32_t GetCodePointFromUTF8String(const std::string&, size_t start = 0, size_t *outNumBytes = nullptr);
template<typename Iterator>
static Iterator CodePointToUTF8(Iterator output, uint32_t cp) {
if(cp < 0x80) {
*(output++) = static_cast<char>(cp);
}else if(cp < 0x800) {
*(output++) = static_cast<char>((cp>>6) | 0xc0);
*(output++) = static_cast<char>((cp&0x3f) | 0x80);
}else if(cp < 0x10000) {
*(output++) = static_cast<char>((cp>>12) | 0xe0);
*(output++) = static_cast<char>(((cp>>6)&0x3f) | 0x80);
*(output++) = static_cast<char>((cp&0x3f) | 0x80);
}else if(cp < 0x200000) {
*(output++) = static_cast<char>((cp>>18) | 0xf0);
*(output++) = static_cast<char>(((cp>>12)&0x3f) | 0x80);
*(output++) = static_cast<char>(((cp>>6)&0x3f) | 0x80);
*(output++) = static_cast<char>((cp&0x3f) | 0x80);
}else if(cp < 0x4000000) {
*(output++) = static_cast<char>((cp>>24) | 0xf8);
*(output++) = static_cast<char>(((cp>>18)&0x3f) | 0x80);
*(output++) = static_cast<char>(((cp>>12)&0x3f) | 0x80);
*(output++) = static_cast<char>(((cp>>6)&0x3f) | 0x80);
*(output++) = static_cast<char>((cp&0x3f) | 0x80);
}else{
*(output++) = static_cast<char>((cp>>30) | 0xfc);
*(output++) = static_cast<char>(((cp>>24)&0x3f) | 0x80);
*(output++) = static_cast<char>(((cp>>18)&0x3f) | 0x80);
*(output++) = static_cast<char>(((cp>>12)&0x3f) | 0x80);
*(output++) = static_cast<char>(((cp>>6)&0x3f) | 0x80);
*(output++) = static_cast<char>((cp&0x3f) | 0x80);
}
return output;
}
std::string TrimSpaces(const std::string&);