hqlres.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "jliball.hpp"
  14. #include "hql.hpp"
  15. #include "jlib.hpp"
  16. #include "hqlres.hpp"
  17. #include "hqlcpp.ipp"
  18. #include "jmisc.hpp"
  19. #include "jexcept.hpp"
  20. #include "hqlcerrors.hpp"
  21. #include "thorplugin.hpp"
  22. #include "codesigner.hpp"
  23. #define BIGSTRING_BASE 101
  24. #define MANIFEST_BASE 1000
  25. class ResourceItem : public CInterface
  26. {
  27. public:
  28. ResourceItem(const char * _type, unsigned _id, size32_t _len, const void * _ptr)
  29. : data(_len, _ptr), type(_type), id(_id) {}
  30. public:
  31. MemoryAttr data;
  32. StringAttr type;
  33. unsigned id;
  34. };
  35. ResourceManager::ResourceManager(IHqlCppInstance &_cppInstance) : cppInstance(_cppInstance)
  36. {
  37. nextmfid = MANIFEST_BASE + 1;
  38. nextbsid = BIGSTRING_BASE;
  39. totalbytes = 0;
  40. finalized=false;
  41. }
  42. unsigned ResourceManager::count()
  43. {
  44. return resources.ordinality();
  45. }
  46. unsigned ResourceManager::addString(unsigned len, const char *data)
  47. {
  48. unsigned id = nextbsid++;
  49. resources.append(*new ResourceItem("BIGSTRING", id, len, data));
  50. return id;
  51. }
  52. void ResourceManager::addNamed(const char * type, unsigned len, const void * data, IPropertyTree *manifestEntry, unsigned id, bool addToManifest, bool compressed)
  53. {
  54. if (id==(unsigned)-1)
  55. id = nextmfid++;
  56. if (addToManifest)
  57. {
  58. if (finalized)
  59. throwError1(HQLERR_ResourceAddAfterFinalManifest, type);
  60. Owned<IPropertyTree> entry=createPTree("Resource");
  61. entry->setProp("@type", type);
  62. entry->setPropInt("@id", id);
  63. entry->setPropBool("@compressed", compressed);
  64. entry->setPropBool("@header", true);
  65. if (manifestEntry)
  66. mergePTree(entry, manifestEntry);
  67. ensureManifestInfo()->addPropTree("Resource", entry.getClear());
  68. }
  69. MemoryBuffer mb;
  70. appendResource(mb, len, data, compressed);
  71. resources.append(*new ResourceItem(type, id, mb.length(), mb.toByteArray()));
  72. }
  73. bool ResourceManager::addCompress(const char * type, unsigned len, const void * data, IPropertyTree *manifestEntry, unsigned id, bool addToManifest)
  74. {
  75. addNamed(type, len, data, manifestEntry, id, addToManifest, true);
  76. return true;
  77. }
  78. static void loadResource(const char *filepath, MemoryBuffer &content)
  79. {
  80. Owned <IFile> f = createIFile(filepath);
  81. Owned <IFileIO> fio = f->open(IFOread);
  82. if (!fio)
  83. throw makeStringExceptionV(0, "Failed to open resource file %s", filepath);
  84. read(fio, 0, (size32_t) f->size(), content);
  85. }
  86. bool ResourceManager::getDuplicateResourceId(const char *srctype, const char *respath, const char *filepath, int &id)
  87. {
  88. StringBuffer xpath;
  89. if (respath && *respath)
  90. xpath.appendf("Resource[@resourcePath='%s']", respath);
  91. else
  92. xpath.appendf("Resource[@originalFilename='%s']", filepath);
  93. Owned<IPropertyTreeIterator> iter = manifest->getElements(xpath.str());
  94. ForEach (*iter)
  95. {
  96. IPropertyTree &item = iter->query();
  97. if (item.hasProp("@id"))
  98. {
  99. const char *type = item.queryProp("@type");
  100. if (type && strieq(type, srctype))
  101. {
  102. id=item.getPropInt("@id");
  103. return true;
  104. }
  105. }
  106. }
  107. return false;
  108. }
  109. void updateManifestResourcePaths(IPropertyTree &resource, const char *dir)
  110. {
  111. StringBuffer filepath;
  112. makeAbsolutePath(resource.queryProp("@filename"), dir, filepath);
  113. resource.setProp("@originalFilename", filepath.str());
  114. StringBuffer respath;
  115. makePathUniversal(filepath.str(), respath);
  116. resource.setProp("@resourcePath", respath.str());
  117. }
  118. void expandManifestDirectory(IPropertyTree *manifestSrc, IPropertyTree &res, StringBuffer &dir, IDirectoryIterator *it, const char*mask, bool recursive)
  119. {
  120. if (!it)
  121. return;
  122. ForEach(*it)
  123. {
  124. if (it->isDir())
  125. {
  126. if (recursive)
  127. expandManifestDirectory(manifestSrc, res, dir, it->query().directoryFiles(mask, false, true), mask, recursive);
  128. continue;
  129. }
  130. StringBuffer reldir;
  131. Owned<IPropertyTree> newRes = createPTreeFromIPT(&res);
  132. reldir.append(splitRelativePath(it->query().queryFilename(), dir, reldir));
  133. VStringBuffer xpath("Resource[@filename='%s']", reldir.str());
  134. if (manifestSrc->hasProp(xpath))
  135. continue;
  136. newRes->setProp("@filename", reldir.str());
  137. updateManifestResourcePaths(*newRes, dir.str());
  138. if (manifestSrc->hasProp(xpath.setf("resource[@resourcePath='%s']", newRes->queryProp("@resourcePath"))))
  139. continue;
  140. manifestSrc->addPropTree("Resource", newRes.getClear());
  141. }
  142. }
  143. void expandManifestDirectory(IPropertyTree *manifestSrc, IPropertyTree &res, StringBuffer &dir, const char *path, const char*mask, bool recursive)
  144. {
  145. Owned<IDirectoryIterator> it = createDirectoryIterator(path, mask);
  146. expandManifestDirectory(manifestSrc, res, dir, it, mask, recursive);
  147. }
  148. void ResourceManager::addManifestFile(const char *filename, ICodegenContextCallback *ctxCallback)
  149. {
  150. StringBuffer fileContents;
  151. StringBuffer strippedFileContents;
  152. Owned<IFile> manifestFile = createIFile(filename);
  153. fileContents.loadFile(manifestFile);
  154. bool isSigned = false;
  155. const char *useContents = fileContents;
  156. // Check for signature
  157. if (queryCodeSigner().hasSignature(fileContents))
  158. {
  159. try
  160. {
  161. StringBuffer signer;
  162. isSigned = queryCodeSigner().verifySignature(fileContents, signer);
  163. if (!isSigned)
  164. throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_VERIFY, "Code sign verify: signature not verified");
  165. useContents = queryCodeSigner().stripSignature(fileContents, strippedFileContents).str();
  166. }
  167. catch (IException *E)
  168. {
  169. StringBuffer msg;
  170. E->errorMessage(msg);
  171. auto code = E->errorCode();
  172. auto aud = E->errorAudience();
  173. E->Release();
  174. throw makeStringExceptionV(aud, code, "While loading manifest file %s: %s", filename, msg.str());
  175. }
  176. }
  177. Owned<IPropertyTree> manifestSrc = createPTreeFromXMLString(useContents);
  178. StringBuffer dir;
  179. splitDirTail(filename, dir);
  180. ensureManifestInfo();
  181. Owned<IAttributeIterator> aiter = manifestSrc->getAttributes();
  182. ForEach (*aiter)
  183. manifest->setProp(aiter->queryName(), aiter->queryValue());
  184. Owned<IPropertyTreeIterator> iter = manifestSrc->getElements("*");
  185. ForEach(*iter)
  186. {
  187. IPropertyTree &item = iter->query();
  188. if (streq(item.queryName(), "Include") && item.hasProp("@filename"))
  189. addManifestInclude(item, dir.str(), ctxCallback);
  190. else if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
  191. {
  192. StringBuffer filepath;
  193. StringBuffer respath;
  194. makeAbsolutePath(item.queryProp("@filename"), dir.str(), filepath);
  195. makePathUniversal(filepath.str(), respath);
  196. item.setProp("@originalFilename", filepath.str());
  197. item.setProp("@resourcePath", respath.str());
  198. if (containsFileWildcard(filepath))
  199. {
  200. StringBuffer wildpath;
  201. const char *tail = splitDirTail(filepath, wildpath);
  202. expandManifestDirectory(manifestSrc, item, dir, wildpath, tail, item.getPropBool("@recursive"));
  203. manifestSrc->removeTree(&item);
  204. }
  205. }
  206. else
  207. manifest->addPropTree(item.queryName(), LINK(&item));
  208. }
  209. Owned<IPropertyTreeIterator> resources = manifestSrc->getElements("Resource[@filename]");
  210. ForEach(*resources)
  211. {
  212. IPropertyTree &item = resources->query();
  213. const char *resourceFilename = item.queryProp("@originalFilename");
  214. const char *md5 = item.queryProp("@md5");
  215. if (md5)
  216. {
  217. StringBuffer calculated;
  218. md5_filesum(resourceFilename, calculated);
  219. if (!strieq(calculated, md5))
  220. throw makeStringExceptionV(0, "MD5 mismatch on file %s in manifest %s", item.queryProp("@filename"), filename);
  221. }
  222. else if (isSigned)
  223. throw makeStringExceptionV(0, "MD5 must be supplied for file %s in signed manifest %s", item.queryProp("@filename"), filename);
  224. if (!item.hasProp("@type"))
  225. item.setProp("@type", "UNKNOWN");
  226. int id;
  227. if (getDuplicateResourceId(item.queryProp("@type"), item.queryProp("@resourcePath"), NULL, id))
  228. {
  229. item.setPropInt("@id", id);
  230. manifest->addPropTree("Resource", LINK(&item));
  231. }
  232. else
  233. {
  234. const char *type = item.queryProp("@type");
  235. if (strieq(type, "CPP") || strieq(type, "C"))
  236. {
  237. if (!ctxCallback->allowAccess("cpp", isSigned))
  238. throw makeStringExceptionV(0, "Embedded code via manifest file not allowed");
  239. cppInstance.useSourceFile(resourceFilename, item.queryProp("@compileFlags"), false);
  240. }
  241. else
  242. {
  243. if ((strieq(type, "jar") || strieq(type, "pyzip")) && !ctxCallback->allowAccess(type, isSigned))
  244. throw makeStringExceptionV(0, "Embedded %s files via manifest file not allowed", type);
  245. MemoryBuffer content;
  246. loadResource(resourceFilename, content);
  247. addCompress(type, content.length(), content.toByteArray(), &item); // MORE - probably should not recompress files known to be compressed, like jar
  248. }
  249. }
  250. }
  251. }
  252. void ResourceManager::addManifest(const char *filename, ICodegenContextCallback *ctxCallback)
  253. {
  254. StringBuffer path;
  255. Owned<IPropertyTree> t = createPTree();
  256. t->setProp("@originalFilename", makeAbsolutePath(filename, path).str());
  257. ensureManifestInfo()->addPropTree("Include", t.getClear());
  258. addManifestFile(filename, ctxCallback);
  259. }
  260. void ResourceManager::addManifestInclude(IPropertyTree &include, const char *dir, ICodegenContextCallback *ctxCallback)
  261. {
  262. StringBuffer includePath;
  263. makeAbsolutePath(include.queryProp("@filename"), dir, includePath);
  264. VStringBuffer xpath("Include[@originalFilename='%s']", includePath.str());
  265. if (manifest->hasProp(xpath.str()))
  266. return;
  267. include.setProp("@originalFilename", includePath.str());
  268. manifest->addPropTree("Include", LINK(&include));
  269. addManifestFile(includePath.str(), ctxCallback);
  270. }
  271. void ResourceManager::addManifestsFromArchive(IPropertyTree *archive, ICodegenContextCallback *ctxCallback)
  272. {
  273. if (!archive)
  274. return;
  275. if (finalized)
  276. throwError1(HQLERR_ResourceAddAfterFinalManifest, "MANIFEST");
  277. ensureManifestInfo();
  278. Owned<IPropertyTreeIterator> manifests = archive->getElements("AdditionalFiles/Manifest");
  279. ForEach(*manifests)
  280. {
  281. StringBuffer tempDir;
  282. StringBuffer manifestContents;
  283. StringBuffer strippedManifestContents;
  284. manifests->query().getProp(nullptr, manifestContents);
  285. const char *xml = manifestContents;
  286. bool isSigned = false;
  287. // Check for signature
  288. if (queryCodeSigner().hasSignature(xml))
  289. {
  290. try
  291. {
  292. StringBuffer signer;
  293. isSigned = queryCodeSigner().verifySignature(manifestContents, signer);
  294. if (!isSigned)
  295. throw makeStringExceptionV(0, "Code sign verify: signature not verified");
  296. xml = queryCodeSigner().stripSignature(manifestContents, strippedManifestContents).str();
  297. }
  298. catch (IException *E)
  299. {
  300. StringBuffer msg;
  301. E->errorMessage(msg);
  302. auto code = E->errorCode();
  303. auto aud = E->errorAudience();
  304. E->Release();
  305. throw makeStringExceptionV(aud, code, "While loading manifest %s: %s", manifests->query().queryProp("@originalFileName"), msg.str());
  306. }
  307. }
  308. Owned<IPropertyTree> manifestSrc = createPTreeFromXMLString(xml);
  309. Owned<IAttributeIterator> aiter = manifestSrc->getAttributes();
  310. ForEach (*aiter)
  311. manifest->setProp(aiter->queryName(), aiter->queryValue());
  312. StringBuffer manifestDir;
  313. if (manifestSrc->hasProp("@originalFilename"))
  314. splitDirTail(manifestSrc->queryProp("@originalFilename"), manifestDir);
  315. Owned<IPropertyTreeIterator> iter = manifestSrc->getElements("*");
  316. ForEach(*iter)
  317. {
  318. IPropertyTree &item = iter->query();
  319. if (streq(item.queryName(), "Resource") && item.hasProp("@filename"))
  320. {
  321. if (!item.hasProp("@type"))
  322. item.setProp("@type", "UNKNOWN");
  323. const char *filename;
  324. if (item.hasProp("@originalFilename"))
  325. filename = item.queryProp("@originalFilename");
  326. else
  327. filename = item.queryProp("@filename");
  328. int id;
  329. if (getDuplicateResourceId(item.queryProp("@type"), NULL, filename, id))
  330. {
  331. item.setPropInt("@id", (int)id);
  332. manifest->addPropTree("Resource", LINK(&item));
  333. }
  334. else
  335. {
  336. MemoryBuffer content;
  337. VStringBuffer xpath("AdditionalFiles/Resource[@originalFilename=\"%s\"]", filename);
  338. const char *md5=item.queryProp("@md5");
  339. if (!archive->hasProp(xpath.str()))
  340. {
  341. if (md5)
  342. xpath.clear().appendf("AdditionalFiles/Resource[@filename='%s'][@md5='%s']", filename, md5);
  343. else
  344. xpath.clear().appendf("AdditionalFiles/Resource[@originalFilename=\"%s\"]", filename);
  345. if (!archive->hasProp(xpath.str()))
  346. throw makeStringExceptionV(0, "Failed to locate resource for %s in archive", filename);
  347. }
  348. archive->getPropBin(xpath.str(), content);
  349. if (md5)
  350. {
  351. StringBuffer calculated;
  352. md5_data(content, calculated);
  353. if (!strieq(calculated, md5))
  354. throw makeStringExceptionV(0, "MD5 mismatch %s in archive", filename);
  355. }
  356. const char *type = item.queryProp("@type");
  357. if (strieq(type, "CPP") || strieq(type, "C"))
  358. {
  359. if (!ctxCallback->allowAccess("cpp", isSigned))
  360. throw makeStringExceptionV(0, "Embedded code via manifest file not allowed");
  361. if (!tempDir.length())
  362. {
  363. getTempFilePath(tempDir, "eclcc", nullptr);
  364. tempDir.append(PATHSEPCHAR).append("tmp.XXXXXX"); // Note - we share same temp dir for all from this manifest
  365. if (!mkdtemp((char *) tempDir.str()))
  366. throw makeStringExceptionV(0, "Failed to create temporary directory %s (error %d)", tempDir.str(), errno);
  367. cppInstance.addTemporaryDir(tempDir.str());
  368. }
  369. StringBuffer tempFileName;
  370. tempFileName.append(tempDir).append(PATHSEPCHAR).append(item.queryProp("@filename"));
  371. if (!recursiveCreateDirectoryForFile(tempFileName))
  372. throw makeStringExceptionV(0, "Failed to create temporary file %s (error %d)", tempFileName.str(), errno);
  373. FILE *source = fopen(tempFileName.str(), "wt");
  374. fwrite(content.toByteArray(), content.length(), 1, source);
  375. fclose(source);
  376. cppInstance.useSourceFile(tempFileName, item.queryProp("@compileFlags"), true);
  377. }
  378. else
  379. {
  380. if ((strieq(type, "jar") || strieq(type, "pyzip")) && !ctxCallback->allowAccess(type, isSigned))
  381. throw makeStringExceptionV(0, "Embedded %s files via manifest file not allowed", type);
  382. addCompress(type, content.length(), content.toByteArray(), &item); // MORE - probably should not recompress files known to be compressed, like jar
  383. }
  384. }
  385. }
  386. else
  387. manifest->addPropTree(item.queryName(), LINK(&item));
  388. }
  389. }
  390. }
  391. void ResourceManager::addWebServiceInfo(IPropertyTree *wsinfo)
  392. {
  393. //convert legacy web service info to the new resource format
  394. if (wsinfo)
  395. {
  396. if (wsinfo->hasProp("SOAP"))
  397. ensureManifestInfo()->addProp("WS-PARAMS", wsinfo->queryProp("SOAP"));
  398. if (wsinfo->hasProp("HELP"))
  399. {
  400. const char *content = wsinfo->queryProp("HELP");
  401. addCompress("HELP", strlen(content)+1, content);
  402. }
  403. if (wsinfo->hasProp("INFO"))
  404. {
  405. const char *content = wsinfo->queryProp("INFO");
  406. addCompress("INFO", strlen(content)+1, content);
  407. }
  408. if (wsinfo->hasProp("OTX"))
  409. {
  410. const char *content = wsinfo->queryProp("OTX");
  411. addCompress("HYPER-LINK", strlen(content)+1, content);
  412. }
  413. if (wsinfo->hasProp("HTML"))
  414. {
  415. const char *content = wsinfo->queryProp("HTML");
  416. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  417. manifestEntry->setProp("@name", "Custom Form");
  418. addCompress("XSLT", strlen(content)+1, content, manifestEntry);
  419. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/XSLT/FORM");
  420. view->setProp("@resource", "Custom Form");
  421. }
  422. if (wsinfo->hasProp("HTMLD"))
  423. {
  424. const char *content = wsinfo->queryProp("HTMLD");
  425. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  426. manifestEntry->setProp("@name", "Custom HTML");
  427. addCompress("HTML", strlen(content)+1, content, manifestEntry);
  428. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/HTML/FORM");
  429. view->setProp("@resource", "Custom HTML");
  430. }
  431. if (wsinfo->hasProp("RESULT"))
  432. {
  433. const char *content = wsinfo->queryProp("RESULT");
  434. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  435. manifestEntry->setProp("@name", "Results");
  436. addCompress("XSLT", strlen(content)+1, content, manifestEntry);
  437. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/XSLT/RESULTS");
  438. view->setProp("@resource", "Results");
  439. }
  440. if (wsinfo->hasProp("ERROR"))
  441. {
  442. const char *content = wsinfo->queryProp("ERROR");
  443. Owned<IPropertyTree> manifestEntry = createPTree("Resource");
  444. manifestEntry->setProp("@name", "Error");
  445. addCompress("XSLT", strlen(content)+1, content, manifestEntry);
  446. IPropertyTree *view = ensurePTree(ensureManifestInfo(), "Views/XSLT/ERROR");
  447. view->setProp("@resource", "Error");
  448. }
  449. }
  450. }
  451. void ResourceManager::finalize()
  452. {
  453. if (!finalized)
  454. {
  455. if (manifest)
  456. {
  457. StringBuffer content;
  458. toXML(manifest, content);
  459. addCompress("MANIFEST", content.length()+1, content.str(), NULL, MANIFEST_BASE, false);
  460. }
  461. finalized=true;
  462. }
  463. }
  464. void ResourceManager::putbytes(int h, const void *b, unsigned len)
  465. {
  466. unsigned written = _write(h, b, len);
  467. assertex(written == len);
  468. totalbytes += len;
  469. }
  470. void ResourceManager::flushAsText(const char *filename)
  471. {
  472. finalize();
  473. StringBuffer name;
  474. int len = strlen(filename);
  475. name.append(filename,0,len-4).append(".txt");
  476. FILE* f = fopen(name.str(), "wb");
  477. if (f==NULL)
  478. {
  479. IERRLOG("Create resource text file %s failed", name.str());
  480. return; // error is ignorable.
  481. }
  482. ForEachItemIn(idx, resources)
  483. {
  484. ResourceItem&s = (ResourceItem&)resources.item(idx);
  485. fwrite(s.data.get(),1,s.data.length(),f);
  486. }
  487. fclose(f);
  488. }
  489. bool ResourceManager::flush(StringBuffer &filename, const char *basename, bool flushText, bool target64bit)
  490. {
  491. finalize();
  492. // Use "resources" for strings that are a bit large to generate in the c++ (some compilers had limits at 64k)
  493. // or that we want to access without having to run the dll/so
  494. // In linux there is no .res concept but we can achieve the same effect by generating an object file with a specially-named section
  495. // bintils tools can be used to extract the data externally (internally we just have a named symbol for it)
  496. // Alternatively we can generate an assembler file to create the equivalent object file, if binutils is not available
  497. bool isObjectFile = true;
  498. #ifdef _WIN32
  499. filename.append(basename).append(".res");
  500. int h = _open(filename, _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY|_O_SEQUENTIAL, _S_IREAD | _S_IWRITE | _S_IEXEC);
  501. //assertex(h != HFILE_ERROR);
  502. if (h == HFILE_ERROR) // error can not be ignored!
  503. throwError1(HQLERR_ResourceCreateFailed, filename.str());
  504. totalbytes = 0;
  505. putbytes(h, "\x00\x00\x00\x00\x20\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00"
  506. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x20);
  507. MemoryBuffer temp;
  508. ForEachItemIn(idx, resources)
  509. {
  510. ResourceItem&s = static_cast<ResourceItem&>(resources.item(idx));
  511. __int32 len = s.data.length();
  512. unsigned lenType = strlen(s.type);
  513. unsigned sizeType = (lenType+1)*2;
  514. unsigned sizeTypeName = (sizeType + 4);
  515. unsigned packedSizeTypeName = ((sizeTypeName + 2) & ~3);
  516. __int32 lenHeader = 4 + 4 + packedSizeTypeName + 4 + 2 + 2 + 4 + 4;
  517. unsigned short id = s.id;
  518. temp.clear();
  519. temp.append(sizeof(len), &len);
  520. temp.append(sizeof(lenHeader), &lenHeader);
  521. for (unsigned i=0; i < lenType; i++)
  522. temp.append((byte)s.type[i]).append((byte)0);
  523. temp.append((byte)0).append((byte)0);
  524. temp.append((byte)0xff).append((byte)0xff);
  525. temp.append(sizeof(id), &id);
  526. if (temp.length() & 2)
  527. temp.append((byte)0).append((byte)0);
  528. temp.append(4, "\x00\x00\x00\x00"); // version
  529. temp.append(12, "\x30\x10\x09\x04\x00\x00\x00\x00\x00\x00\x00\x00"); // 0x1030 memory 0x0409 language
  530. assertex(lenHeader == temp.length());
  531. putbytes(h, temp.bufferBase(), lenHeader);
  532. putbytes(h, s.data.get(), len);
  533. if (totalbytes & 3)
  534. putbytes(h, "\x00\x00\x00",4-(totalbytes & 3));
  535. }
  536. _close(h);
  537. #else
  538. isObjectFile = false;
  539. filename.append(basename).append(".res.s");
  540. FILE *f = fopen(filename, "wt");
  541. if (!f)
  542. throwError1(HQLERR_ResourceCreateFailed, filename.str());
  543. //MORE: This should really use targetCompiler instead
  544. #if defined(__APPLE__)
  545. const bool generateClang = true;
  546. #else
  547. const bool generateClang = false;
  548. #endif
  549. ForEachItemIn(idx, resources)
  550. {
  551. ResourceItem &s = (ResourceItem &) resources.item(idx);
  552. const char *type = s.type.str();
  553. unsigned id = s.id;
  554. VStringBuffer binfile("%s_%s_%u.bin", filename.str(), type, id);
  555. VStringBuffer label("%s_%u_txt_start", type, id);
  556. if (generateClang)
  557. {
  558. #ifdef __APPLE__
  559. if (id <= 1200) // There is a limit of 255 sections before linker complains - and some are used elsewhere
  560. #endif
  561. fprintf(f, " .section __TEXT,%s_%u\n", type, id);
  562. fprintf(f, " .global _%s\n", label.str()); // For some reason apple needs a leading underbar and linux does not
  563. fprintf(f, "_%s:\n", label.str());
  564. }
  565. else
  566. {
  567. #if defined(__linux__) && defined(__GNUC__) && defined(__arm__)
  568. fprintf(f, " .section .note.GNU-stack,\"\",%%progbits\n"); // Prevent the stack from being marked as executable
  569. #else
  570. fprintf(f, " .section .note.GNU-stack,\"\",@progbits\n"); // Prevent the stack from being marked as executable
  571. #endif
  572. fprintf(f, " .section %s_%u,\"a\"\n", type, id);
  573. fprintf(f, " .global %s\n", label.str());
  574. fprintf(f, " .type %s,STT_OBJECT\n", label.str());
  575. fprintf(f, "%s:\n", label.str());
  576. }
  577. fprintf(f, " .incbin \"%s\"\n", binfile.str());
  578. FILE *bin = fopen(binfile, "wb");
  579. if (!bin)
  580. {
  581. fclose(f);
  582. throwError1(HQLERR_ResourceCreateFailed, binfile.str());
  583. }
  584. fwrite(s.data.get(), 1, s.data.length(), bin);
  585. fclose(bin);
  586. }
  587. fclose(f);
  588. #endif
  589. if (flushText)
  590. flushAsText(filename);
  591. return isObjectFile;
  592. }
  593. bool ResourceManager::queryWriteText(StringBuffer & resTextName, const char * filename)
  594. {
  595. int len = strlen(filename);
  596. resTextName.append(filename,0,len-4).append(".txt");
  597. return true;
  598. }
  599. #if 0
  600. int test()
  601. {
  602. ResourceManager r;
  603. r.add("Hello there!2");
  604. r.add("Hello again");
  605. r.flush("c:\\t2.res");
  606. return 6;
  607. }
  608. static int dummy = test();
  609. #endif