2018-07-25 19:06:21 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
|
|
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
#include <assert.h>
|
|
|
|
#include <boost/program_options.hpp>
|
|
|
|
#include <iostream>
|
2018-07-24 23:59:20 +08:00
|
|
|
#include <map>
|
2018-07-25 15:31:44 +08:00
|
|
|
#include <stdint.h>
|
2018-07-24 23:59:20 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2018-07-25 15:31:44 +08:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2018-07-24 23:59:20 +08:00
|
|
|
|
|
|
|
enum TokenType : int8_t
|
|
|
|
{
|
|
|
|
TOK_LABEL,
|
|
|
|
TOK_REF,
|
|
|
|
TOK_U8,
|
|
|
|
TOK_U16,
|
|
|
|
TOK_U32
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Stream
|
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
std::vector<TokenType> tokenTypes;
|
|
|
|
std::vector<uint32_t> tokenValues;
|
|
|
|
std::vector<std::string> tokenComments;
|
|
|
|
};
|
|
|
|
|
|
|
|
Stream stringStream;
|
|
|
|
std::vector<Stream> streams;
|
|
|
|
std::map<std::string, int> streamIndex;
|
|
|
|
std::vector<int> streamStack;
|
|
|
|
|
|
|
|
std::vector<int> labels;
|
2018-07-26 21:22:52 +08:00
|
|
|
std::vector<std::string> labelNames;
|
2018-07-24 23:59:20 +08:00
|
|
|
std::map<std::string, int> labelIndex;
|
|
|
|
|
|
|
|
std::vector<std::string> preText, postText;
|
|
|
|
|
|
|
|
const char *skipWhitespace(const char *p)
|
|
|
|
{
|
2018-07-25 03:10:42 +08:00
|
|
|
if (p == nullptr)
|
|
|
|
return "";
|
2018-07-24 23:59:20 +08:00
|
|
|
while (*p == ' ' || *p == '\t')
|
|
|
|
p++;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2018-07-25 03:10:42 +08:00
|
|
|
int main(int argc, char **argv)
|
2018-07-24 18:27:41 +08:00
|
|
|
{
|
2018-07-26 21:22:52 +08:00
|
|
|
bool debug = false;
|
2018-07-25 03:10:42 +08:00
|
|
|
bool verbose = false;
|
|
|
|
bool bigEndian = false;
|
|
|
|
bool writeC = false;
|
2018-07-24 18:27:41 +08:00
|
|
|
char buffer[512];
|
2018-07-24 23:59:20 +08:00
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
namespace po = boost::program_options;
|
|
|
|
po::positional_options_description pos;
|
|
|
|
po::options_description options("Allowed options");
|
2019-06-27 14:24:32 +08:00
|
|
|
options.add_options()("help,h", "verbose output");
|
|
|
|
options.add_options()("verbose,v", "verbose output");
|
|
|
|
options.add_options()("debug,d", "debug output");
|
|
|
|
options.add_options()("be,b", "big endian");
|
|
|
|
options.add_options()("c,c", "write c strings");
|
2018-07-25 15:31:44 +08:00
|
|
|
options.add_options()("files", po::value<std::vector<std::string>>(), "file parameters");
|
|
|
|
pos.add("files", -1);
|
|
|
|
|
|
|
|
po::variables_map vm;
|
|
|
|
try {
|
|
|
|
po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).positional(pos).run();
|
|
|
|
|
|
|
|
po::store(parsed, vm);
|
|
|
|
|
|
|
|
po::notify(vm);
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
std::cout << e.what() << "\n";
|
|
|
|
return 1;
|
|
|
|
}
|
2019-06-27 14:24:32 +08:00
|
|
|
if (vm.count("help")) {
|
|
|
|
std::cout << options;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (vm.count("verbose"))
|
2018-07-25 15:31:44 +08:00
|
|
|
verbose = true;
|
2019-06-27 14:24:32 +08:00
|
|
|
if (vm.count("debug"))
|
2018-07-26 21:22:52 +08:00
|
|
|
debug = true;
|
2019-06-27 14:24:32 +08:00
|
|
|
if (vm.count("be"))
|
2018-07-25 15:31:44 +08:00
|
|
|
bigEndian = true;
|
|
|
|
if (vm.count("c"))
|
|
|
|
writeC = true;
|
|
|
|
|
|
|
|
if (vm.count("files") == 0) {
|
|
|
|
printf("File parameters are mandatory\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
std::vector<std::string> files = vm["files"].as<std::vector<std::string>>();
|
|
|
|
if (files.size() != 2) {
|
|
|
|
printf("Input and output parameters must be set\n");
|
|
|
|
exit(-1);
|
2018-07-25 03:10:42 +08:00
|
|
|
}
|
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
FILE *fileIn = fopen(files.at(0).c_str(), "rt");
|
2018-07-25 03:10:42 +08:00
|
|
|
assert(fileIn != nullptr);
|
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
FILE *fileOut = fopen(files.at(1).c_str(), writeC ? "wt" : "wb");
|
2018-07-25 03:10:42 +08:00
|
|
|
assert(fileOut != nullptr);
|
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
while (fgets(buffer, 512, fileIn) != nullptr) {
|
2018-07-24 23:59:20 +08:00
|
|
|
std::string cmd = strtok(buffer, " \t\r\n");
|
|
|
|
|
|
|
|
if (cmd == "pre") {
|
|
|
|
const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
|
|
|
|
preText.push_back(p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == "post") {
|
|
|
|
const char *p = skipWhitespace(strtok(nullptr, "\r\n"));
|
|
|
|
postText.push_back(p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == "push") {
|
2018-07-25 03:10:42 +08:00
|
|
|
const char *p = strtok(nullptr, " \t\r\n");
|
2018-07-24 23:59:20 +08:00
|
|
|
if (streamIndex.count(p) == 0) {
|
|
|
|
streamIndex[p] = streams.size();
|
|
|
|
streams.resize(streams.size() + 1);
|
|
|
|
streams.back().name = p;
|
|
|
|
}
|
|
|
|
streamStack.push_back(streamIndex.at(p));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == "pop") {
|
|
|
|
streamStack.pop_back();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == "label" || cmd == "ref") {
|
2018-07-25 03:10:42 +08:00
|
|
|
const char *label = strtok(nullptr, " \t\r\n");
|
|
|
|
const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
|
2018-07-24 23:59:20 +08:00
|
|
|
Stream &s = streams.at(streamStack.back());
|
|
|
|
if (labelIndex.count(label) == 0) {
|
|
|
|
labelIndex[label] = labels.size();
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug)
|
|
|
|
labelNames.push_back(label);
|
2018-07-24 23:59:20 +08:00
|
|
|
labels.push_back(-1);
|
|
|
|
}
|
|
|
|
s.tokenTypes.push_back(cmd == "label" ? TOK_LABEL : TOK_REF);
|
|
|
|
s.tokenValues.push_back(labelIndex.at(label));
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug)
|
2018-07-24 23:59:20 +08:00
|
|
|
s.tokenComments.push_back(comment);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == "u8" || cmd == "u16" || cmd == "u32") {
|
2018-07-25 03:10:42 +08:00
|
|
|
const char *value = strtok(nullptr, " \t\r\n");
|
|
|
|
const char *comment = skipWhitespace(strtok(nullptr, "\r\n"));
|
2018-07-24 23:59:20 +08:00
|
|
|
Stream &s = streams.at(streamStack.back());
|
|
|
|
s.tokenTypes.push_back(cmd == "u8" ? TOK_U8 : cmd == "u16" ? TOK_U16 : TOK_U32);
|
|
|
|
s.tokenValues.push_back(atoll(value));
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug)
|
2018-07-24 23:59:20 +08:00
|
|
|
s.tokenComments.push_back(comment);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-25 03:10:42 +08:00
|
|
|
if (cmd == "str") {
|
|
|
|
const char *value = skipWhitespace(strtok(nullptr, "\r\n"));
|
2019-03-13 01:18:56 +08:00
|
|
|
assert(*value != 0);
|
|
|
|
char *end = strchr((char *)value + 1, *value);
|
2019-06-27 14:24:32 +08:00
|
|
|
assert(end != nullptr);
|
2019-03-13 01:18:56 +08:00
|
|
|
*end = 0;
|
|
|
|
value += 1;
|
|
|
|
const char *comment = skipWhitespace(strtok(end+1, "\r\n"));
|
2018-07-24 23:59:20 +08:00
|
|
|
std::string label = std::string("str:") + value;
|
|
|
|
Stream &s = streams.at(streamStack.back());
|
|
|
|
if (labelIndex.count(label) == 0) {
|
|
|
|
labelIndex[label] = labels.size();
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug)
|
|
|
|
labelNames.push_back(label);
|
2018-07-24 23:59:20 +08:00
|
|
|
labels.push_back(-1);
|
|
|
|
}
|
|
|
|
s.tokenTypes.push_back(TOK_REF);
|
|
|
|
s.tokenValues.push_back(labelIndex.at(label));
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug)
|
|
|
|
s.tokenComments.push_back(comment);
|
2018-07-24 23:59:20 +08:00
|
|
|
stringStream.tokenTypes.push_back(TOK_LABEL);
|
|
|
|
stringStream.tokenValues.push_back(labelIndex.at(label));
|
2018-07-26 21:22:52 +08:00
|
|
|
stringStream.tokenComments.push_back("");
|
2018-07-24 23:59:20 +08:00
|
|
|
while (1) {
|
|
|
|
stringStream.tokenTypes.push_back(TOK_U8);
|
|
|
|
stringStream.tokenValues.push_back(*value);
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug) {
|
|
|
|
char char_comment[4] = {'\'', *value, '\'', 0};
|
|
|
|
if (*value < 32 || *value >= 127)
|
|
|
|
char_comment[0] = 0;
|
|
|
|
stringStream.tokenComments.push_back(char_comment);
|
|
|
|
}
|
2018-07-24 23:59:20 +08:00
|
|
|
if (*value == 0)
|
|
|
|
break;
|
|
|
|
value++;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-25 03:10:42 +08:00
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
printf("Constructed %d streams:\n", int(streams.size()));
|
|
|
|
for (auto &s : streams)
|
|
|
|
printf(" stream '%s' with %d tokens\n", s.name.c_str(), int(s.tokenTypes.size()));
|
2018-07-24 18:27:41 +08:00
|
|
|
}
|
2018-07-24 23:59:20 +08:00
|
|
|
|
|
|
|
assert(!streams.empty());
|
|
|
|
assert(streamStack.empty());
|
|
|
|
streams.push_back(Stream());
|
2018-07-26 21:22:52 +08:00
|
|
|
streams.back().name = "strings";
|
2018-07-24 23:59:20 +08:00
|
|
|
streams.back().tokenTypes.swap(stringStream.tokenTypes);
|
|
|
|
streams.back().tokenValues.swap(stringStream.tokenValues);
|
2018-07-26 21:22:52 +08:00
|
|
|
streams.back().tokenComments.swap(stringStream.tokenComments);
|
2018-07-24 23:59:20 +08:00
|
|
|
|
2018-07-25 03:10:42 +08:00
|
|
|
int cursor = 0;
|
|
|
|
for (auto &s : streams) {
|
|
|
|
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
|
2018-07-25 15:31:44 +08:00
|
|
|
switch (s.tokenTypes[i]) {
|
2018-07-25 03:10:42 +08:00
|
|
|
case TOK_LABEL:
|
|
|
|
labels[s.tokenValues[i]] = cursor;
|
|
|
|
break;
|
|
|
|
case TOK_REF:
|
|
|
|
cursor += 4;
|
|
|
|
break;
|
|
|
|
case TOK_U8:
|
|
|
|
cursor += 1;
|
|
|
|
break;
|
|
|
|
case TOK_U16:
|
|
|
|
assert(cursor % 2 == 0);
|
|
|
|
cursor += 2;
|
|
|
|
break;
|
|
|
|
case TOK_U32:
|
|
|
|
assert(cursor % 4 == 0);
|
|
|
|
cursor += 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
printf("resolved positions for %d labels.\n", int(labels.size()));
|
2018-07-25 15:31:44 +08:00
|
|
|
printf("total data (including strings): %.2f MB\n", double(cursor) / (1024 * 1024));
|
2018-07-25 03:10:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<uint8_t> data(cursor);
|
|
|
|
|
|
|
|
cursor = 0;
|
|
|
|
for (auto &s : streams) {
|
2018-07-26 21:22:52 +08:00
|
|
|
if (debug)
|
|
|
|
printf("-- %s --\n", s.name.c_str());
|
|
|
|
|
2018-07-25 03:10:42 +08:00
|
|
|
for (int i = 0; i < int(s.tokenTypes.size()); i++) {
|
|
|
|
uint32_t value = s.tokenValues[i];
|
|
|
|
int numBytes = 0;
|
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
switch (s.tokenTypes[i]) {
|
2018-07-25 03:10:42 +08:00
|
|
|
case TOK_LABEL:
|
|
|
|
break;
|
|
|
|
case TOK_REF:
|
|
|
|
value = labels[value] - cursor;
|
|
|
|
numBytes = 4;
|
|
|
|
break;
|
|
|
|
case TOK_U8:
|
|
|
|
numBytes = 1;
|
|
|
|
break;
|
|
|
|
case TOK_U16:
|
|
|
|
numBytes = 2;
|
|
|
|
break;
|
|
|
|
case TOK_U32:
|
|
|
|
numBytes = 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bigEndian) {
|
2018-07-25 15:31:44 +08:00
|
|
|
switch (numBytes) {
|
2018-07-25 03:10:42 +08:00
|
|
|
case 4:
|
|
|
|
data[cursor++] = value >> 24;
|
|
|
|
data[cursor++] = value >> 16;
|
2018-07-26 08:53:01 +08:00
|
|
|
/* fall-through */
|
2018-07-25 03:10:42 +08:00
|
|
|
case 2:
|
|
|
|
data[cursor++] = value >> 8;
|
2018-07-26 08:53:01 +08:00
|
|
|
/* fall-through */
|
2018-07-25 03:10:42 +08:00
|
|
|
case 1:
|
|
|
|
data[cursor++] = value;
|
2018-07-26 08:53:01 +08:00
|
|
|
/* fall-through */
|
2018-07-25 03:10:42 +08:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
} else {
|
2018-07-25 15:31:44 +08:00
|
|
|
switch (numBytes) {
|
2018-07-25 03:10:42 +08:00
|
|
|
case 4:
|
2018-07-25 15:31:44 +08:00
|
|
|
data[cursor + 3] = value >> 24;
|
|
|
|
data[cursor + 2] = value >> 16;
|
2018-07-26 08:53:01 +08:00
|
|
|
/* fall-through */
|
2018-07-25 03:10:42 +08:00
|
|
|
case 2:
|
2018-07-25 15:31:44 +08:00
|
|
|
data[cursor + 1] = value >> 8;
|
2018-07-26 08:53:01 +08:00
|
|
|
/* fall-through */
|
2018-07-25 03:10:42 +08:00
|
|
|
case 1:
|
|
|
|
data[cursor] = value;
|
2018-07-26 08:53:01 +08:00
|
|
|
/* fall-through */
|
2018-07-25 03:10:42 +08:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
cursor += numBytes;
|
|
|
|
}
|
2018-07-26 21:22:52 +08:00
|
|
|
|
|
|
|
if (debug) {
|
|
|
|
printf("%08x ", cursor - numBytes);
|
|
|
|
for (int k = cursor - numBytes; k < cursor; k++)
|
|
|
|
printf("%02x ", data[k]);
|
|
|
|
for (int k = numBytes; k < 4; k++)
|
|
|
|
printf(" ");
|
|
|
|
|
|
|
|
unsigned long long v = s.tokenValues[i];
|
|
|
|
|
|
|
|
switch (s.tokenTypes[i]) {
|
|
|
|
case TOK_LABEL:
|
|
|
|
if (s.tokenComments[i].empty())
|
|
|
|
printf("label %s\n", labelNames[v].c_str());
|
|
|
|
else
|
|
|
|
printf("label %-24s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());
|
|
|
|
break;
|
|
|
|
case TOK_REF:
|
|
|
|
if (s.tokenComments[i].empty())
|
2018-07-27 00:02:50 +08:00
|
|
|
printf("ref %s\n", labelNames[v].c_str());
|
2018-07-26 21:22:52 +08:00
|
|
|
else
|
|
|
|
printf("ref %-26s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());
|
|
|
|
break;
|
|
|
|
case TOK_U8:
|
|
|
|
if (s.tokenComments[i].empty())
|
|
|
|
printf("u8 %llu\n", v);
|
|
|
|
else
|
|
|
|
printf("u8 %-27llu %s\n", v, s.tokenComments[i].c_str());
|
|
|
|
break;
|
|
|
|
case TOK_U16:
|
|
|
|
if (s.tokenComments[i].empty())
|
|
|
|
printf("u16 %-26llu\n", v);
|
|
|
|
else
|
|
|
|
printf("u16 %-26llu %s\n", v, s.tokenComments[i].c_str());
|
|
|
|
break;
|
|
|
|
case TOK_U32:
|
|
|
|
if (s.tokenComments[i].empty())
|
|
|
|
printf("u32 %-26llu\n", v);
|
|
|
|
else
|
|
|
|
printf("u32 %-26llu %s\n", v, s.tokenComments[i].c_str());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
2018-07-25 03:10:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(cursor == int(data.size()));
|
|
|
|
|
|
|
|
if (writeC) {
|
|
|
|
for (auto &s : preText)
|
|
|
|
fprintf(fileOut, "%s\n", s.c_str());
|
|
|
|
|
2018-07-25 15:31:44 +08:00
|
|
|
fprintf(fileOut, "const char %s[%d] =\n\"", streams[0].name.c_str(), int(data.size()) + 1);
|
2018-07-25 03:10:42 +08:00
|
|
|
|
|
|
|
cursor = 1;
|
2018-07-26 21:22:52 +08:00
|
|
|
for (int i = 0; i < int(data.size()); i++) {
|
|
|
|
auto d = data[i];
|
2018-07-25 03:10:42 +08:00
|
|
|
if (cursor > 70) {
|
|
|
|
fputc('\"', fileOut);
|
|
|
|
fputc('\n', fileOut);
|
|
|
|
cursor = 0;
|
|
|
|
}
|
|
|
|
if (cursor == 0) {
|
|
|
|
fputc('\"', fileOut);
|
|
|
|
cursor = 1;
|
|
|
|
}
|
2018-07-26 21:22:52 +08:00
|
|
|
if (d < 32 || d >= 127) {
|
2018-07-26 23:14:56 +08:00
|
|
|
if (i + 1 < int(data.size()) && (data[i + 1] < '0' || '9' < data[i + 1]))
|
2018-07-26 21:22:52 +08:00
|
|
|
cursor += fprintf(fileOut, "\\%o", int(d));
|
|
|
|
else
|
|
|
|
cursor += fprintf(fileOut, "\\%03o", int(d));
|
2018-07-25 15:31:44 +08:00
|
|
|
} else if (d == '\"' || d == '\'' || d == '\\') {
|
2018-07-25 03:10:42 +08:00
|
|
|
fputc('\\', fileOut);
|
|
|
|
fputc(d, fileOut);
|
|
|
|
cursor += 2;
|
|
|
|
} else {
|
|
|
|
fputc(d, fileOut);
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(fileOut, "\";\n");
|
|
|
|
|
|
|
|
for (auto &s : postText)
|
|
|
|
fprintf(fileOut, "%s\n", s.c_str());
|
|
|
|
} else {
|
|
|
|
fwrite(data.data(), int(data.size()), 1, fileOut);
|
|
|
|
}
|
|
|
|
|
2018-07-24 18:27:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|