Message.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Message.cpp
  3. * -----------
  4. * Purpose: Various functions for processing song messages (allocating, reading from file...)
  5. * Notes : Those functions should offer a rather high level of abstraction compared to
  6. * previous ways of reading the song messages. There are still many things to do,
  7. * though. Future versions of ReadMessage() could e.g. offer charset conversion
  8. * and the code is not yet ready for unicode.
  9. * Some functions for preparing the message text to be written to a file would
  10. * also be handy.
  11. * Authors: OpenMPT Devs
  12. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  13. */
  14. #include "stdafx.h"
  15. #include "Message.h"
  16. #include "../common/FileReader.h"
  17. OPENMPT_NAMESPACE_BEGIN
  18. // Read song message from a mapped file.
  19. // [in] data: pointer to the data in memory that is going to be read
  20. // [in] length: number of characters that should be read. Trailing null characters are automatically removed.
  21. // [in] lineEnding: line ending formatting of the text in memory.
  22. // [out] returns true on success.
  23. bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEnding)
  24. {
  25. const char *str = mpt::byte_cast<const char *>(data);
  26. while(length != 0 && str[length - 1] == '\0')
  27. {
  28. // Ignore trailing null character.
  29. length--;
  30. }
  31. // Simple line-ending detection algorithm. VERY simple.
  32. if(lineEnding == leAutodetect)
  33. {
  34. size_t nCR = 0, nLF = 0, nCRLF = 0;
  35. // find CRs, LFs and CRLFs
  36. for(size_t i = 0; i < length; i++)
  37. {
  38. char c = str[i];
  39. if(c == '\r')
  40. nCR++;
  41. else if(c == '\n')
  42. nLF++;
  43. if(i && str[i - 1] == '\r' && c == '\n')
  44. nCRLF++;
  45. }
  46. // evaluate findings
  47. if(nCR == nLF && nCR == nCRLF)
  48. lineEnding = leCRLF;
  49. else if(nCR && !nLF)
  50. lineEnding = leCR;
  51. else if(!nCR && nLF)
  52. lineEnding = leLF;
  53. else
  54. lineEnding = leMixed;
  55. }
  56. size_t finalLength = 0;
  57. // calculate the final amount of characters to be allocated.
  58. for(size_t i = 0; i < length; i++)
  59. {
  60. finalLength++;
  61. if(str[i] == '\r' && lineEnding == leCRLF)
  62. i++; // skip the LF
  63. }
  64. clear();
  65. reserve(finalLength);
  66. for(size_t i = 0; i < length; i++)
  67. {
  68. char c = str[i];
  69. switch(c)
  70. {
  71. case '\r':
  72. if(lineEnding != leLF)
  73. c = InternalLineEnding;
  74. else
  75. c = ' ';
  76. if(lineEnding == leCRLF)
  77. i++; // skip the LF
  78. break;
  79. case '\n':
  80. if(lineEnding != leCR && lineEnding != leCRLF)
  81. c = InternalLineEnding;
  82. else
  83. c = ' ';
  84. break;
  85. case '\0':
  86. c = ' ';
  87. break;
  88. default:
  89. break;
  90. }
  91. push_back(c);
  92. }
  93. return true;
  94. }
  95. bool SongMessage::Read(FileReader &file, const size_t length, LineEnding lineEnding)
  96. {
  97. FileReader::off_t readLength = std::min(static_cast<FileReader::off_t>(length), file.BytesLeft());
  98. FileReader::PinnedView fileView = file.ReadPinnedView(readLength);
  99. bool success = Read(fileView.data(), fileView.size(), lineEnding);
  100. return success;
  101. }
  102. // Read comments with fixed line length from a mapped file.
  103. // [in] data: pointer to the data in memory that is going to be read
  104. // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended).
  105. // [in] lineLength: The fixed length of a line.
  106. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line)
  107. // [out] returns true on success.
  108. bool SongMessage::ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength)
  109. {
  110. if(lineLength == 0)
  111. return false;
  112. clear();
  113. reserve(length);
  114. size_t readPos = 0, writePos = 0;
  115. while(readPos < length)
  116. {
  117. size_t thisLineLength = std::min(lineLength, length - readPos);
  118. append(mpt::byte_cast<const char *>(data) + readPos, thisLineLength);
  119. append(1, InternalLineEnding);
  120. // Fix weird chars
  121. for(size_t pos = writePos; pos < writePos + thisLineLength; pos++)
  122. {
  123. switch(operator[](pos))
  124. {
  125. case '\0':
  126. case '\n':
  127. case '\r':
  128. operator[](pos) = ' ';
  129. break;
  130. }
  131. }
  132. readPos += thisLineLength + std::min(lineEndingLength, length - readPos - thisLineLength);
  133. writePos += thisLineLength + 1;
  134. }
  135. return true;
  136. }
  137. bool SongMessage::ReadFixedLineLength(FileReader &file, const size_t length, const size_t lineLength, const size_t lineEndingLength)
  138. {
  139. FileReader::off_t readLength = std::min(static_cast<FileReader::off_t>(length), file.BytesLeft());
  140. FileReader::PinnedView fileView = file.ReadPinnedView(readLength);
  141. bool success = ReadFixedLineLength(fileView.data(), fileView.size(), lineLength, lineEndingLength);
  142. return success;
  143. }
  144. // Retrieve song message.
  145. // [in] lineEnding: line ending formatting of the text in memory.
  146. // [out] returns formatted song message.
  147. std::string SongMessage::GetFormatted(const LineEnding lineEnding) const
  148. {
  149. std::string comments;
  150. comments.reserve(length());
  151. for(auto c : *this)
  152. {
  153. if(c == InternalLineEnding)
  154. {
  155. switch(lineEnding)
  156. {
  157. case leCR:
  158. comments.push_back('\r');
  159. break;
  160. case leCRLF:
  161. comments.push_back('\r');
  162. comments.push_back('\n');
  163. break;
  164. case leLF:
  165. comments.push_back('\n');
  166. break;
  167. default:
  168. comments.push_back('\r');
  169. break;
  170. }
  171. } else
  172. {
  173. comments.push_back(c);
  174. }
  175. }
  176. return comments;
  177. }
  178. bool SongMessage::SetFormatted(std::string message, LineEnding lineEnding)
  179. {
  180. MPT_ASSERT(lineEnding == leLF || lineEnding == leCR || lineEnding == leCRLF);
  181. switch(lineEnding)
  182. {
  183. case leLF:
  184. message = mpt::replace(message, std::string("\n"), std::string(1, InternalLineEnding));
  185. break;
  186. case leCR:
  187. message = mpt::replace(message, std::string("\r"), std::string(1, InternalLineEnding));
  188. break;
  189. case leCRLF:
  190. message = mpt::replace(message, std::string("\r\n"), std::string(1, InternalLineEnding));
  191. break;
  192. default:
  193. MPT_ASSERT_NOTREACHED();
  194. break;
  195. }
  196. if(message == *this)
  197. {
  198. return false;
  199. }
  200. assign(std::move(message));
  201. return true;
  202. }
  203. OPENMPT_NAMESPACE_END