skins.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. var logging = require("./logging");
  2. var lwip = require("lwip");
  3. var fs = require("fs");
  4. var exp = {};
  5. // extracts the face from an image +buffer+
  6. // result is saved to a file called +outname+
  7. // callback: error
  8. exp.extract_face = function(buffer, outname, callback) {
  9. lwip.open(buffer, "png", function(err, image) {
  10. if (err) {
  11. callback(err);
  12. } else {
  13. image.batch()
  14. .crop(8, 8, 15, 15) // face
  15. .writeFile(outname, function(write_err) {
  16. if (write_err) {
  17. callback(write_err);
  18. } else {
  19. callback(null);
  20. }
  21. });
  22. }
  23. });
  24. };
  25. // extracts the helm from an image +buffer+ and lays it over a +facefile+
  26. // +facefile+ is the filename of an image produced by extract_face
  27. // result is saved to a file called +outname+
  28. // callback: error
  29. exp.extract_helm = function(rid, facefile, buffer, outname, callback) {
  30. lwip.open(buffer, "png", function(err, skin_img) {
  31. if (err) {
  32. callback(err);
  33. } else {
  34. lwip.open(facefile, function(open_err, face_img) {
  35. if (open_err) {
  36. callback(open_err);
  37. } else {
  38. face_img.toBuffer("png", { compression: "none" }, function(buf_err, face_buffer) {
  39. // FIXME: buf_err is not handled
  40. skin_img.crop(40, 8, 47, 15, function(crop_err, helm_img) {
  41. if (crop_err) {
  42. callback(crop_err);
  43. } else {
  44. face_img.paste(0, 0, helm_img, function(img_err, face_helm_img) {
  45. if (img_err) {
  46. callback(img_err);
  47. } else {
  48. if (!skin_img.__trans) {
  49. logging.log(rid, "Skin is not transparent, skipping helm!");
  50. callback(null);
  51. } else {
  52. face_helm_img.toBuffer("png", {compression: "none"}, function(buf_err2, face_helm_buffer) {
  53. // FIXME: buf_err2 is not handled
  54. if (face_helm_buffer.toString() !== face_buffer.toString()) {
  55. face_helm_img.writeFile(outname, function(write_err) {
  56. callback(write_err);
  57. });
  58. } else {
  59. logging.log(rid, "helm img == face img, not storing!");
  60. callback(null);
  61. }
  62. });
  63. }
  64. }
  65. });
  66. }
  67. });
  68. });
  69. }
  70. });
  71. }
  72. });
  73. };
  74. // resizes the image file +inname+ to +size+ by +size+ pixels
  75. // callback: error, image buffer
  76. exp.resize_img = function(inname, size, callback) {
  77. lwip.open(inname, function(err, image) {
  78. if (err) {
  79. callback(err, null);
  80. } else {
  81. image.batch()
  82. .resize(size, size, "nearest-neighbor") // nearest-neighbor doesn't blur
  83. .toBuffer("png", function(buf_err, buffer) {
  84. // FIXME: buf_err is not handled
  85. callback(null, buffer);
  86. });
  87. }
  88. });
  89. };
  90. // returns "alex" or "steve" calculated by the +uuid+
  91. exp.default_skin = function(uuid) {
  92. if (uuid.length <= 16) {
  93. // we can't get the skin type by username
  94. return "steve";
  95. } else {
  96. // great thanks to Minecrell for research into Minecraft and Java's UUID hashing!
  97. // https://git.io/xJpV
  98. // MC uses `uuid.hashCode() & 1` for alex
  99. // that can be compacted to counting the LSBs of every 4th byte in the UUID
  100. // an odd sum means alex, an even sum means steve
  101. // XOR-ing all the LSBs gives us 1 for alex and 0 for steve
  102. var lsbs_even = parseInt(uuid[ 7], 16) ^
  103. parseInt(uuid[15], 16) ^
  104. parseInt(uuid[23], 16) ^
  105. parseInt(uuid[31], 16);
  106. return lsbs_even ? "alex" : "steve";
  107. }
  108. };
  109. // helper method for opening a skin file from +skinpath+
  110. // callback: error, image buffer
  111. exp.open_skin = function(rid, skinpath, callback) {
  112. fs.readFile(skinpath, function(err, buf) {
  113. if (err) {
  114. logging.error(rid, "error while opening skin file:", err);
  115. callback(err, null);
  116. } else {
  117. callback(null, buf);
  118. }
  119. });
  120. };
  121. // write the image +buffer+ to the +outpath+ file
  122. // callback: error
  123. exp.save_image = function(buffer, outpath, callback) {
  124. lwip.open(buffer, "png", function(err, image) {
  125. if (err) {
  126. callback(err);
  127. } else {
  128. image.batch()
  129. .writeFile(outpath, function(write_err) {
  130. if (write_err) {
  131. callback(write_err);
  132. } else {
  133. callback(null);
  134. }
  135. });
  136. }
  137. });
  138. };
  139. module.exports = exp;